首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >与Python生成器模式等效的C++

与Python生成器模式等效的C++
EN

Stack Overflow用户
提问于 2012-01-30 03:58:56
回答 13查看 70.5K关注 0票数 143

我有一些在C++中需要模仿的Python代码示例。我不需要任何特定的解决方案(例如基于联合例程的产率解决方案,尽管它们也是可接受的答案),我只需要以某种方式再现语义。

Python

这是一个基本的序列生成器,显然太大,无法存储物化版本。

代码语言:javascript
运行
复制
def pair_sequence():
    for i in range(2**32):
        for j in range(2**32):
            yield (i, j)

其目标是维护上面序列的两个实例,并以半锁定步长(但以块的形式)迭代它们。在下面的示例中,first_pass使用对序列初始化缓冲区,second_pass重新生成相同的序列并再次处理缓冲区。

代码语言:javascript
运行
复制
def run():
    seq1 = pair_sequence()
    seq2 = pair_sequence()

    buffer = [0] * 1000
    first_pass(seq1, buffer)
    second_pass(seq2, buffer)
    ... repeat ...

C++

在C++中,我唯一能找到的解决方案就是用C++协同器来模拟yield,但是我没有找到任何关于如何做到这一点的很好的参考。我还对这个问题的替代(非一般)解决方案感兴趣。我没有足够的内存预算来保存一份两次通过之间的顺序。

EN

回答 13

Stack Overflow用户

回答已采纳

发布于 2012-01-30 07:34:31

生成器存在于C++中,只是以另一个名称存在:输入迭代器。例如,从std::cin读取类似于有一个char生成器。

您只需了解生成器的功能:

  • 有大量的数据:局部变量定义了一个状态
  • 有一个init方法
  • 有一个“下一步”方法
  • 有一种方法可以让信号终止

在你的小例子中,这是很容易的。概念上:

代码语言:javascript
运行
复制
struct State { unsigned i, j; };

State make();

void next(State&);

bool isDone(State const&);

当然,我们将其包装为一个适当的类:

代码语言:javascript
运行
复制
class PairSequence:
    // (implicit aliases)
    public std::iterator<
        std::input_iterator_tag,
        std::pair<unsigned, unsigned>
    >
{
  // C++03
  typedef void (PairSequence::*BoolLike)();
  void non_comparable();
public:
  // C++11 (explicit aliases)
  using iterator_category = std::input_iterator_tag;
  using value_type = std::pair<unsigned, unsigned>;
  using reference = value_type const&;
  using pointer = value_type const*;
  using difference_type = ptrdiff_t;

  // C++03 (explicit aliases)
  typedef std::input_iterator_tag iterator_category;
  typedef std::pair<unsigned, unsigned> value_type;
  typedef value_type const& reference;
  typedef value_type const* pointer;
  typedef ptrdiff_t difference_type;

  PairSequence(): done(false) {}

  // C++11
  explicit operator bool() const { return !done; }

  // C++03
  // Safe Bool idiom
  operator BoolLike() const {
    return done ? 0 : &PairSequence::non_comparable;
  }

  reference operator*() const { return ij; }
  pointer operator->() const { return &ij; }

  PairSequence& operator++() {
    static unsigned const Max = std::numeric_limts<unsigned>::max();

    assert(!done);

    if (ij.second != Max) { ++ij.second; return *this; }
    if (ij.first != Max) { ij.second = 0; ++ij.first; return *this; }

    done = true;
    return *this;
  }

  PairSequence operator++(int) {
    PairSequence const tmp(*this);
    ++*this;
    return tmp;
  }

private:
  bool done;
  value_type ij;
};

所以哼是的..。可能是C++有点冗长:)

票数 103
EN

Stack Overflow用户

发布于 2012-10-04 21:08:43

在C++中有迭代器,但是实现迭代器并不简单:我们必须参考迭代器概念并仔细设计新的迭代器类来实现它们。值得庆幸的是,Boost有一个立面模板,可以帮助实现迭代器和迭代器兼容的生成器。

有时是可以使用无堆栈的协同线来实现迭代器。

另见这篇文章,其中提到克里斯托弗·M·科尔霍夫( Christopher M. Kohlhoff )的一次switch黑客攻击和奥利弗·科瓦尔克( Oliver )的Boost.Coroutine。书名/作者责任者:J.

我想你也可以写一种生成器用羔羊

代码语言:javascript
运行
复制
std::function<int()> generator = []{
  int i = 0;
  return [=]() mutable {
    return i < 10 ? i++ : -1;
  };
}();
int ret = 0; while ((ret = generator()) != -1) std::cout << "generator: " << ret << std::endl;

或与函子:

代码语言:javascript
运行
复制
struct generator_t {
  int i = 0;
  int operator() () {
    return i < 10 ? i++ : -1;
  }
} generator;
int ret = 0; while ((ret = generator()) != -1) std::cout << "generator: " << ret << std::endl;

下面是一个用魔多协同实现的生成器:

代码语言:javascript
运行
复制
#include <iostream>
using std::cout; using std::endl;
#include <mordor/coroutine.h>
using Mordor::Coroutine; using Mordor::Fiber;

void testMordor() {
  Coroutine<int> coro ([](Coroutine<int>& self) {
    int i = 0; while (i < 9) self.yield (i++);
  });
  for (int i = coro.call(); coro.state() != Fiber::TERM; i = coro.call()) cout << i << endl;
}
票数 61
EN

Stack Overflow用户

发布于 2016-08-06 08:51:16

由于Boost.Coroutine2现在很好地支持它(我发现它是因为我想解决完全相同的yield问题),所以我发布了符合您最初意图的C++代码:

代码语言:javascript
运行
复制
#include <stdint.h>
#include <iostream>
#include <memory>
#include <boost/coroutine2/all.hpp>

typedef boost::coroutines2::coroutine<std::pair<uint16_t, uint16_t>> coro_t;

void pair_sequence(coro_t::push_type& yield)
{
    uint16_t i = 0;
    uint16_t j = 0;
    for (;;) {
        for (;;) {
            yield(std::make_pair(i, j));
            if (++j == 0)
                break;
        }
        if (++i == 0)
            break;
    }
}

int main()
{
    coro_t::pull_type seq(boost::coroutines2::fixedsize_stack(),
                          pair_sequence);
    for (auto pair : seq) {
        print_pair(pair);
    }
    //while (seq) {
    //    print_pair(seq.get());
    //    seq();
    //}
}

在本例中,pair_sequence不使用附加参数。如果需要的话,当std::bind或lambda传递给coro_t::pull_type构造函数时,应该使用它来生成一个函数对象,该对象只接受(push_type的)一个参数。

票数 25
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9059187

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档