11

我有以下 Python 片段,我想使用 C++ 重现:

from itertools import count, imap

source = count(1)
pipe1 = imap(lambda x: 2 * x, source)
pipe2 = imap(lambda x: x + 1, pipe1)
sink = imap(lambda x: 3 * x, pipe2)
for i in sink:
    print i

我听说过Boost Phoenix,但我找不到transform与 Python 的imap.

编辑:为了澄清我的问题,这个想法不仅是使用 a 按顺序应用函数for,而是能够使用std::transform无限生成器之类的算法。组合函数的方式(使用更实用的语言,如方言)也很重要,因为下一步是函数组合。

更新:感谢 bradgonesurfing、David Brown 和 Xeo 提供的惊人答案!我选择 Xeo's 是因为它最简洁,而且它可以让我到达我想去的地方,但 David's 对于理解这些概念非常重要。此外,bradgonesurfing 的提示 Boost::Range :)。

4

5 回答 5

14

使用Boost.Range :

int main(){
  auto map = boost::adaptors::transformed; // shorten the name
  auto sink = generate(1) | map([](int x){ return 2*x; })
                          | map([](int x){ return x+1; })
                          | map([](int x){ return 3*x; });
  for(auto i : sink)
    std::cout << i << "\n";
}

generate包括该功能的实时示例。

于 2012-10-30T18:00:47.303 回答
5

我认为在 C++ 中最惯用的方法是使用迭代器。这是一个基本的迭代器类,它接受一个迭代器并将一个函数应用于其结果:

template<class Iterator, class Function>
class LazyIterMap
{
private:
    Iterator i;
    Function f;
public:
    LazyIterMap(Iterator i, Function f) : i(i), f(f) {}
    decltype(f(*i)) operator* () { return f(*i); }
    void operator++ () { ++i; }
};

template<class Iterator, class Function>
LazyIterMap<Iterator, Function> makeLazyIterMap(Iterator i, Function f)
{
    return LazyIterMap<Iterator, Function>(i, f);
}

这只是一个基本示例,仍然不完整,因为它无法检查您是否已到达可迭代序列的末尾。

这是您的示例 python 代码的重新创建(也定义了一个简单的无限计数器类)。

#include <iostream>

class Counter
{
public:
    Counter (int start) : value(start) {}
    int operator* () { return value; }
    void operator++ () { ++value; }
private:
    int value;
};

int main(int argc, char const *argv[])
{
    Counter source(0);
    auto pipe1 = makeLazyIterMap(source, [](int n) { return 2 * n; });
    auto pipe2 = makeLazyIterMap(pipe1, [](int n) { return n + 1; });
    auto sink = makeLazyIterMap(pipe2, [](int n) { return 3 * n; });
    for (int i = 0; i < 10; ++i, ++sink)
    {
        std::cout << *sink << std::endl;
    }
}

除了类定义(只是复制 python 库函数所做的)之外,代码大约与 python 版本一样长。

于 2012-10-30T17:54:44.430 回答
2

我认为 boost::rangex 库是您正在寻找的。它应该与新的 c++lambda 语法很好地配合使用。

于 2012-10-30T17:28:58.083 回答
1
int pipe1(int val) {
    return 2*val;
}

int pipe2(int val) {
    return val+1;
}

int sink(int val) {
    return val*3;
}

for(int i=0; i < SOME_MAX; ++i)
{
    cout << sink(pipe2(pipe1(i))) << endl;
}

我知道,这不是您所期望的,但它肯定会在您想要的时候进行评估,尽管不是使用迭代器 iterface。一篇非常相关的文章是这样的:

D 中的组件编程

12 年 11 月 6 日编辑:

仍然坚持裸 C++ 的另一种方法是使用函数指针并为上述函数构造自己的管道(来自 SO q 的函数指针向量:如何将函数指针存储在向量中?):

typedef std::vector<int (*)(int)> funcVec;
int runPipe(funcVec funcs, int sinkVal) {
    int running = sinkVal;
    for(funcVec::iterator it = funcs.begin(); it != funcs.end(); ++it) {
        running = (*(*it))(running); // not sure of the braces and asterisks here
    }
    return running;
}

这旨在遍历此类向量中的所有函数并返回结果值。那么你也能:

funcVec funcs;
funcs.pushback(&pipe1);
funcs.pushback(&pipe2);
funcs.pushback(&sink);

for(int i=0; i < SOME_MAX; ++i)
{
    cout << runPipe(funcs, i) << endl;
}

当然,您也可以通过结构为此构造一个包装器(如果 C++ 这样做,我会使用闭包......):

struct pipeWork {
     funcVec funcs;
     int run(int i);
};

int pipeWork::run(int i) {
    //... guts as runPipe, or keep it separate and call:
    return runPipe(funcs, i);
}

// later...
pipeWork kitchen;
kitchen.funcs = someFuncs;
int (*foo) = &kitchen.run();

cout << foo(5) << endl;

或类似的东西。警告:不知道如果指针在线程之间传递会发生什么。

额外警告:如果你想用不同的函数接口来做到这一点,你最终将不得不加载大量void *(void *)(void *)函数,以便它们可以接受任何内容并发出任何内容,或者大量模板来修复你拥有的管道类型。我想理想情况下,你会为​​函数之间的不同接口构造不同类型的管道,这样a | b | c即使它们在它们之间传递不同类型也可以工作。但我猜这很大程度上就是 Boost 正在做的事情。

于 2012-10-30T17:19:07.467 回答
-4

根据功能的简单性:

#define pipe1(x) 2*x
#define pipe2(x) pipe1(x)+1

#define sink(x) pipe2(x)*3

int j = 1
while( ++j > 0 )
{
    std::cout << sink(j) << std::endl;
}
于 2012-10-30T17:29:36.257 回答