11

C++を使用して再現したい次のPythonスニペットがあります。

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

編集:私の質問を明確にするために、アイデアは、を使用して関数を順番に適用するだけでなく、無限ジェネレーターforのようにアルゴリズムを使用できるようにすることです。std::transform次のステップは関数の合成であるため、関数の合成方法(方言などのより機能的な言語)も重要です。

更新:素晴らしい答えをくれたbradgonesurfing、David Brown、Xeoに感謝します!Xeoを選んだのは、それが最も簡潔で、私が望んでいた場所に正しく到達できるからですが、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;
}

私は知っています、それはあなたが期待していたものではありませんが、イテレータの反復ではありませんが、あなたが望むときに確かに評価します。非常に関連する記事はこれです:

Dでのコンポーネントプログラミング

2012年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 に答える