使用迭代器行为生成仿函数

时间:2015-02-01 13:20:20

标签: c++ iterator generator functor

我有一个问题,很可能之前就是这样问过,因为我认为我想要的东西是很多人都想要的东西。但是,我无法想出任何方式表达它将返回我想要的搜索(不是谷歌,不是在这里)。所以这里的答案可能只是用来描述我的意思的单个术语。

我想要实现的内容大致如下:

  1. 它可以采用仿函数struct / class并生成一系列值 对于基于函子函数的所述仿函数。应该可以使用有状态仿函数,即应该可以在状态a中实例化仿函数并使其运行直到它处于状态b,生成值范围{f(a),f(a + 1) ,...,f(b)},其中f(a + 1)表示由f表示的系列中的下一个项目。

  2. 它的行为类似于迭代器,即它可以传递而不是迭代器,例如用值填充向量。

  3. 我认为这个名称应该是生成器或生成迭代器,因为它的功能是什么,但是我找不到这个术语的东西是非常不成功的。我已经编写了自己的实现,但它有它的问题,我想问一下,如果有更多的努力之前有这样的事情。

    为了节省您粘贴所有后续代码的工作,以防您想要尝试此操作,我将其放在ideone上。我认为在运行代码后很清楚它的作用。

    我当前的实现看起来像这样(这是一个缩短的版本,其中有些东西像 - 和 - 缺失,所以我确实实现了它们,因此它至少可以作为双向迭代器工作。我也有一个[]函数,所以我想把它变成random_access。):

    template <class F>
    class generator{
    public:
    //typedefs required for iterator-use
    typedef typename F::value_type value_type;
    typedef typename F::step_type step_type;
    typedef value_type * pointer;
    typedef value_type & reference;
    typedef typename F::size_type size_type;
    typedef typename F::difference_type difference_type;
    typedef std::bidirectional_iterator_tag iterator_category;
    
    generator(value_type init, step_type step) : t(init), step(step){}
    
    generator<F> &operator++() {
        t += step; return *this;
    }
    
    generator<F> &
    operator+=(size_type n)
    {
        t += n * step;
        return *this;
    }
    
    generator<F>
    operator+(size_type n)
    {
        return generator(*this) += n;
    }
    
    value_type operator*() const {
        return f(t);
    }
    
    value_type operator*() const {
        return f(t);
    }
    
    friend bool operator==(const generator<F> &lhs, const generator<F> &rhs){
        return lhs.t == rhs.t;
    }
    friend bool operator!=(const generator<F> &lhs, const generator<F> &rhs){
        return !(lhs == rhs);
    }
    private:
        value_type t;
        value_type step;
        F f;
    };
    

    我使用不同的模板尽可能轻松地提供typedef:

    template <typename T>
    struct simple_generator_function
    {
        typedef T value_type;
        typedef T step_type;
        typedef T difference_type;
        typedef size_t size_type;
    };
    

    现在这两个人与一个混凝土&#34;发电机一起工作&#34;像这样:

    template <typename T>
    struct square_generator : public simple_generator_function<T> {
    
        T operator()(T t) const{
            return t * t;
        }
    };
    
    int main(void) {
        using int_sqg = generator<square_generator<int>>;
        //starting at initial value 1 take steps of size 4
        int_sqg gen(1, 1);
        //fill with all before inital + n * step
        vector<int> v(gen, gen+10);
        copy(begin(v), end(v), ostream_iterator<int>(cout, ","));
        cout << '\n';
    }
    

    长话短说:是否有一个提升或其他库,它以一种可靠的方式提供这种方式,这种迭代器/仿函数混合的名称一般是什么?

    编辑1:

    我认为任何解决方案最多都可以是InputIterator,因为据我研究过,所有其他迭代器都必须从 operator *(),在这种情况下是不可能的。很可能,它归结为编写一个模板,将常规仿函数转换为InputIterator。

    状态:到目前为止答案都很好,但是我在考虑之前已经考虑了很长一段时间,并且我考虑过类似的解决方案,所以我的问题没有得到真正的回答。我已经更新了要求1.) - 希望 - 更清楚地反映我想要的东西。如果没有任何结果,我可能会尝试将我当前的代码改进为更稳定的版本并将其放在github上。

    编辑2(赏金结束):

    尽管我对这个解决方案并不完全满意,但是将boost :: irange与boost :: transform结合起来作为ectamur建议最接近做我想做的事情,所以我会给予他赏金。

4 个答案:

答案 0 :(得分:5)

解决此问题的Boost.Range方法是使用transform迭代器适配器:

auto rng = boost::irange(1, 10)
    | boost::adaptors::transformed([](int i) { return i * i; });
std::vector<int> v{rng.begin(), rng.end()};

请注意这是如何将转换的关注点与输入范围的开始/停止/步骤(可选)参数分开。

答案 1 :(得分:4)

当然,我们可以编写自己的迭代器:

template <typename F, typename Value>
class func_iterator
: std::iterator<
    std::random_access_iterator_tag,
    typename std::result_of<F(Value)>::type,
    Value,
    typename std::result_of<F(Value)>::type,
    typename std::result_of<F(Value)>::type>
{ .. };

这个迭代器需要三件事:一个函数(F f),当前值和一步(Value value, step)。取消引用每次都会计算函数的值:

using T = typename std::result_of<F(Value)>::type;
T operator*() { return f(value); }

选择的迭代函数(省略后缀,因为它们看起来一样):

func_iterator& operator++() {
    value += step;
    return *this;
}

func_iterator& operator--() {
    value -= step;
    return *this;
}

func_iterator operator+(Value amt) {
    return func_iterator{f, value + amt * step, step};
}

迭代器(用于std::distance)和相等之间的区别:

Value operator-(const func_iterator& rhs) {
    assert(step == rhs.step);
    return (value - rhs.value) / step;
}

bool operator==(const func_iterator& rhs) {
    return value == rhs.value && step == rhs.step;
}

最后是一个为我们创建迭代器的函数:

template <typename F, typename Value>
func_iterator<F, Value> make_func_iterator(F f, Value v, Value s = 1) {
    return func_iterator<F, Value>{f, v, s};
}

把它放在一起,我可以做类似的事情:

auto sq_it = make_func_iterator([](int x){return x*x;}, 1);
std::vector<int> squares{sq_it, sq_it + 10}; // v now holds {1, 4, 9, 16, ..., 100}

或者只是:

// get a bunch of even numbers, complicatedly:
auto ident = make_func_iterator([](int x){return x;}, 2, 2);
std::vector<int> evens{ident, ident+200}; // holds {2, 4, ..., 400}

答案 2 :(得分:1)

我想知道这是否可以称为制表。如果是这样,你对以下界面有什么看法?

#include <iostream>
#include <vector>
#include <list>

#include "tabulate.hpp"

template<typename T>
void show(const T& data) {
  for(const auto & x: data) std::cout << x << " ";
  std::cout << std::endl;
}

int main() {
  auto fun = [](double x) { return 2.0 * x; };

  std::vector<double> x  {1, 2, 3, 4, 5};

  std::cout << "using range-for" << std::endl;
  for(const auto & fx : tabulate(fun, x.begin(), x.end())) {
    std::cout << fx << std::endl;
  }

  std::cout << "initializing a vector" << std::endl;
  auto init = tabulate(fun, x.begin(), x.end());
  std::vector<double> values(init.begin(), init.end());
  show(values);

  std::cout << "automatic construction of vector" << std::endl;
  auto in_vector = make_tabulation<std::vector<double>>(fun, x);
  show(in_vector);

  std::cout << "automatic construction of list" << std::endl;
  auto in_list   = make_tabulation<std::list<double>>(fun, x);
  show(in_list);
}

由以下标题实现:

#pragma once
#include <iterator>

template<typename Fun,
         typename InputIt,
         typename T = typename std::iterator_traits<InputIt>::value_type
         >
class tabulate_iterator

    : public std::iterator<std::input_iterator_tag, T> {

 public:

  tabulate_iterator()
      : m_is_valid(false) { }

  tabulate_iterator(Fun& fun, InputIt beg, InputIt end)
      : m_fun(&fun),
        m_beg(beg),
        m_end(end),
        m_is_valid(beg != end) {
    this->read();
  }

  const T& operator*() const {
    return m_current;
  }

  const T* operator->() const {
    return &(operator*());
  }

  tabulate_iterator& operator++() {
    this->read();
    return *this;
  }

  tabulate_iterator operator++(int) {
    auto tmp = *this;
    this->read();
    return tmp;
  }

  bool equals(const tabulate_iterator& other) const {
    return ((m_is_valid == other.m_is_valid) and
            (not m_is_valid));
  }

  bool operator==(const tabulate_iterator& other) const {
    return this->equals(other);
  }

  bool operator!=(const tabulate_iterator& other) const {
    return not this->equals(other);
  }

 private:

  void read() {
    if(m_is_valid and m_beg != m_end) {
      m_current = (*m_fun)(*m_beg++);
    } else {
      m_is_valid = false;
    }
  }

  T       m_current;
  Fun*    m_fun;
  InputIt m_beg;
  InputIt m_end;
  bool    m_is_valid;

};

template<typename Fun,
         typename InputIt,
         typename T = typename std::iterator_traits<InputIt>::value_type
         >
class tabulate_range {

 public:

  tabulate_iterator<Fun, InputIt, T> begin() const {
    return m_it;
  }

  tabulate_iterator<Fun, InputIt, T> end() const {
    return m_it_end;
  }

 private:

  template<typename Fun_, typename InputIt_, typename T_>
  friend tabulate_range<Fun_, InputIt_, T_> tabulate(Fun_, InputIt_, InputIt_);

  tabulate_range(Fun fun, InputIt beg, InputIt end)
      : m_it(fun, beg, end),
        m_it_end() { }

  tabulate_iterator<Fun, InputIt, T> m_it;
  tabulate_iterator<Fun, InputIt, T> m_it_end;
};

template<typename Fun,
         typename InputIt,
         typename T = typename std::iterator_traits<InputIt>::value_type
         >
tabulate_range<Fun, InputIt, T> tabulate(Fun fun, InputIt beg, InputIt end) {
  return tabulate_range<Fun, InputIt, T>(fun, beg, end);
}

template<typename OutContainer, typename Fun, typename InContainer>
OutContainer make_tabulation(Fun fun, const InContainer& x) {
  auto init = tabulate(fun, x.begin(), x.end());
  return OutContainer(init.begin(), init.end());
}

一些警告:我刚刚开始破解这段代码,因此可能存在错误;把它作为概念的证明。

编译(GCC 4.8.2 / Linux; ICC 14.0.2 20140120 / Linux):

{CXX} tabulate.cpp -std=c++11 -Wall -Wextra -Werror

输出:

$ ./a.out
using range-for
2
4
6
8
10
initializing a vector
2 4 6 8 10
automatic construction of vector
2 4 6 8 10
automatic construction of list
2 4 6 8 10

答案 3 :(得分:0)

C ++库已经提供了一些算法,可以实现您自己尝试执行的大部分功能。我认为你最好调整你的模板,以便它可以与C ++库无缝地工作。

我正在考虑std::generate()

因此,您可以在步骤#1中执行您计划执行的操作,但将第2步替换为实现operator()并返回序列中的下一个值。

然后,您可以让std::generate()负责使用您的值填充实际序列。