【问题标题】:How to reduce boilerplate for iterators?如何减少迭代器的样板文件?
【发布时间】:2016-12-27 12:21:22
【问题描述】:

主要作为一个练习,我正在实现从基础B 到基础10 的转换:

unsigned fromBaseB(std::vector<unsigned> x,unsigned b){
    unsigned out = 0;
    unsigned pow = 1;
    for (size_t i=0;i<x.size();i++){
        out += pow * x[i];
        pow *= b;
    }
    return out;
}

int main() {
    auto z = std::vector<unsigned>(9,0);
    z[3] = 1;
    std::cout << fromBaseB(z,3) << std::endl;
}

现在我想用算法来写这个。例如。使用accumulate我可以写

unsigned fromBaseB2(std::vector<unsigned> x,unsigned b){
    unsigned pow = 1;
    return std::accumulate(x.begin(),
                           x.end(),0u,
                           [pow,b](unsigned sum,unsigned v) mutable {
                                unsigned out = pow*v;
                                pow *= b;
                                return out+sum;
                           });
}

但是,恕我直言,这根本不是更好的代码。实际上,将其写成内积会更自然,因为这正是我们进行基变换所必须计算的。但是要使用inner_product,我需要一个迭代器:

template <typename T> struct pow_iterator{
    typedef T value_type;
    pow_iterator(T base) : base(base),value(1) {}
    T base,value;
    pow_iterator& operator++(){ value *= base;return *this; }
    T operator*() {return value; }
    bool operator==(const pow_iterator& other) const { return value == other.value;}
};

unsigned fromBaseB3(std::vector<unsigned> x,unsigned b){
    return std::inner_product(x.begin(),x.end(),pow_iterator<unsigned>(b),0u);
}

使用那个迭代器,现在调用算法很干净,但是我必须为迭代器编写很多样板代码。也许这只是我对应该如何使用算法和迭代器的误解......实际上这只是我有时面临的一般问题的一个例子:我有一个基于简单模式计算的数字序列,我希望有一个迭代器,当取消引用时从该序列返回相应的数字。当序列存储在容器中时,我只需使用容器提供的迭代器,但我也想做同样的事情,即使没有容器存储值。我当然可以尝试编写自己的通用迭代器来完成这项工作,但是标准库中是否存在可以帮助的东西?

对我来说感觉有点奇怪,我可以使用 lambda 欺骗 accumulate 来计算内积,但要直接使用 inner_product 我必须做一些额外的事情(或者预先计算幂并将它们存储在一个容器,或者编写一个迭代器,即一个单独的class)。

tl;dr:有没有一种简单的方法可以减少上述 pow_iterator 的样板文件?

更一般(但可能过于宽泛)的问题:对于未存储在容器中但仅在迭代器被取消引用时才计算的值序列使用迭代器是否“可以”?有没有实现它的“C++方式”?

【问题讨论】:

  • 查看 boost.iterator 库。特别是 iterator_adapter 和 iterator_facade。

标签: c++ algorithm iterator


【解决方案1】:

正如 Richard Hodges 在 cmets 中所写,您可以查看 boost::iterator。或者,有range-v3。如果您使用 boost,则有几种可能的方法。下面展示了如何使用 boost::iterator::counting_iteratorboost::iterator::transform_iterator (C++ 11) 来做到这一点:

#include <iostream>
#include <cmath>

#include <boost/iterator/counting_iterator.hpp>
#include <boost/iterator/transform_iterator.hpp>                                                                                           

int main() {
    const std::size_t base = 2;
    auto make_it = [](std::size_t i) {
        return boost::make_transform_iterator(
            boost::make_counting_iterator(i),
            [](std::size_t j){return std::pow(base, j);});};
    for(auto b = make_it(0); b != make_it(10); ++b)
        std::cout << *b << std::endl;
}

这是输出:

$ ./a.out 
1
2
4
8
16
32
64
128
256
512

【讨论】:

  • 您确实意识到您刚刚从 Boost 中添加了大量样板文件,对吧? :-)
猜你喜欢
  • 2013-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-26
相关资源
最近更新 更多