【问题标题】:Acessing Values only at certain indexes using iterators使用迭代器仅在某些索引处访问值
【发布时间】:2019-10-04 16:52:27
【问题描述】:

我无法理解std::accumulate。它可以用于仅将向量中偶数索引处的值相加吗?

int rob(vector<int>& nums) {

    int a = std::accumulate(nums.begin(), nums.end(), 0);

   std::cout <<" a: " <<a; 
   return a;

}

如果我有vector y = {1, 2, 3, 4}

如何更改上述代码,使std::accumulate 仅迭代索引 [0] 和 [2]。甚至可以使用std::accumulate 吗?

【问题讨论】:

标签: c++ algorithm lambda stl iterator


【解决方案1】:

你有几个选择。快速且(真正)肮脏的方法是遍历整个集合,并调用一个跟踪当前索引的函数,并忽略奇数索引处的值。它有效,但充其量是丑陋的,更重要的是,它在相当基本的层面上是错误的,迫使本应是累积功能的东西负责进行迭代。简而言之,这与其说是解决方案,不如说是一个问题。

干净的方法是意识到访问集合中的所有其他项目实际上是关于迭代,而不是关于特定算法(std::accumulate 或任何其他)。所以我们在这里应该使用的是一个迭代器,它访问我们想要访问的项目。这是一个最小的实现:

#include <vector>
#include <iterator>
#include <iostream>
#include <numeric>

template <class Iterator>
class n_iterator { 
     Iterator i;
     size_t n;
public:
    // We construct this iterator from some other iterator, plus a "step" value
    // to tell us how many items to skip forward when `++` is applied.
    n_iterator(Iterator i, size_t n) : i(i), n(n) {}

    // When you dereference this iterator, it's equivalent to dereferencing
    // the underlying iterator.
    typename std::iterator_traits<Iterator>::value_type operator *() { return *i; }

    // ...but when you increment it, you move ahead N places instead of 1.
    n_iterator &operator++() { std::advance(i, n); return *this; }

    // iterator comparisons just compare the underlying iterators.
    bool operator==(n_iterator const &other) const { return i == other.i; }
    bool operator!=(n_iterator const &other) const { return i != other.i; }
};

int main() { 
    std::vector<int> y { 1, 2, 3, 4};
    auto total = std::accumulate(y.begin(), y.end(), 0);

    std::cout << "total: " << total << "\n";

    auto skip_total = std::accumulate(n_iterator(y.begin(), 2), n_iterator(y.end(), 2), 0);

    std::cout << "Skipped total: " << skip_total << "\n";
}

这个实现似乎足以让 g++ 7.1 编译代码,但对于实际使用,您可能应该实现迭代器的整个接口(例如,至少,它应该真正具有 value_type、@987654325 的定义@等)

目前,这也只提供一个前向迭代器,而不管底层迭代器。根据情况(和底层迭代器的类别),您还可以支持双向和/或随机迭代。

【讨论】:

  • 这和boost::strided_iterator非常相似
  • @MarshallClow:啊,原来如此——我想我应该猜到 Boost 已经有了这个。
【解决方案2】:

你来了

int rob( const vector<int>& nums) {

    int i = 0;
    int a = std::accumulate(nums.begin(), nums.end(), 0,
                            [&i]( const auto &acc, const auto &value )
                            {
                                return ( i ^= 1 ) ? acc + value : acc;
                            } );

   std::cout <<" a: " <<a; 
   return a;

}

这是一个演示程序

#include <iostream>
#include <vector>
#include <iterator>
#include <numeric>

int rob( const std::vector<int> &nums )
{
    int i = 0;

    int a = std::accumulate( std::begin( nums ), std::end( nums ), 0,
                             [&i]( const auto &acc, const auto &value )
                             {
                                return ( i ^= 1 ) ? acc + value : acc;
                             } );

   return a;
}

int main() 
{
    std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    std::cout << rob( v ) << '\n';

    return 0;
}

它的输出是

20

您可以在函数中再添加一个参数,您可以选择是对偶数求和还是对奇数求和。例如

#include <iostream>
#include <vector>
#include <iterator>
#include <numeric>

int rob( const std::vector<int> &nums, bool odds = false )
{
    int i = odds;

    int a = std::accumulate( std::begin( nums ), std::end( nums ), 0,
                             [&i]( const auto &acc, const auto &value )
                             {
                                return ( i ^= 1 ) ? acc + value : acc;
                             } );

   return a;
}

int main() 
{
    std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    std::cout << rob( v ) << '\n';

    std::cout << rob( v, true ) << '\n';
    return 0;
}

程序输出是

20
25

在这种情况下,您可以删除变量 i 的声明。例如

#include <iostream>
#include <vector>
#include <iterator>
#include <numeric>

int rob( const std::vector<int> &nums, bool odds = false )
{
    int a = std::accumulate( std::begin( nums ), std::end( nums ), 0,
                             [&odds]( const auto &acc, const auto &value )
                             {
                                return ( odds = !odds ) ? acc + value : acc;
                             } );

   return a;
}

int main() 
{
    std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    std::cout << rob( v ) << '\n';

    std::cout << rob( v, true ) << '\n';
    return 0;
}

【讨论】:

  • 您能解释一下Lamda 的工作原理吗?我不明白它如何跳过奇数索引处的值。
  • @learningtocodecplusplus 表达式 i ^= 1 控制索引。如果 i 等于 0,则表达式设置为 1,并返回这部分条件语句 acc + value。如果 i 等于 1 则表达式 i ^= 1 等于 0 并返回 acc 的原始值。
  • 这太聪明了。它在01 之间更改I 的值,因此它将所有其他元素相加。如果i 是一个布尔值,在truefalse 之间交替,它会更清晰。
  • 是否保证 BinaryOperation 每个元素只会按顺序执行一次?我认为不是,您的解决方案依赖于此。
  • @LightnessRacesinOrbit 我刚刚浏览了标准。:)
猜你喜欢
  • 2021-07-03
  • 2016-08-25
  • 2012-04-28
  • 1970-01-01
  • 1970-01-01
  • 2012-03-19
  • 1970-01-01
  • 2011-07-18
  • 1970-01-01
相关资源
最近更新 更多