Skip to content Skip to sidebar Skip to footer

Lazy Transform In C++

I have the following Python snippet that I would like to reproduce using C++: from itertools import count, imap source = count(1) pipe1 = imap(lambda x: 2 * x, source) pipe2 = ima

Solution 1:

Employing Boost.Range:

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

Live example including the generate function.

Solution 2:

I think the most idiomatic way to do this in C++ is with iterators. Here is a basic iterator class that takes an iterator and applies a function to its result:

template<classIterator, classFunction>
classLazyIterMap
{
private:
    Iterator i;
    Function f;
public:
    LazyIterMap(Iterator i, Function f) : i(i), f(f) {}
    decltype(f(*i)) operator* () { returnf(*i); }
    void operator++ () { ++i; }
};

template<classIterator, classFunction>
LazyIterMap<Iterator, Function> makeLazyIterMap(Iterator i, Function f)
{
    returnLazyIterMap<Iterator, Function>(i, f);
}

This is just a basic example and is still incomplete as it has no way to check if you've reached the end of the iterable sequence.

Here's a recreation of your example python code (also defining a simple infinite counter class).

#include<iostream>classCounter
{
public:
    Counter (int start) : value(start) {}
    intoperator* () { return value; }
    voidoperator++ () { ++value; }
private:
    int value;
};

intmain(int argc, charconst *argv[]){
    Counter source(0);
    auto pipe1 = makeLazyIterMap(source, [](int n) { return2 * n; });
    auto pipe2 = makeLazyIterMap(pipe1, [](int n) { return n + 1; });
    auto sink = makeLazyIterMap(pipe2, [](int n) { return3 * n; });
    for (int i = 0; i < 10; ++i, ++sink)
    {
        std::cout << *sink << std::endl;
    }
}

Apart from the class definitions (which are just reproducing what the python library functions do), the code is about as long as the python version.

Solution 3:

I think the boost::rangex library is what you are looking for. It should work nicely with the new c++lambda syntax.

Solution 4:

intpipe1(int val){
    return2*val;
}

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

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

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

I know, it's not quite what you were expecting, but it certainly evaluates at the time you want it to, although not with an iterator iterface. A very related article is this:

Component programming in D

Edit 6/Nov/12:

An alternative, still sticking to bare C++, is to use function pointers and construct your own piping for the above functions (vector of function pointers from SO q: How can I store function pointer in vector?):

typedef std::vector<int (*)(int)> funcVec;
intrunPipe(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;
}

This is intended to run through all the functions in a vector of such and return the resulting value. Then you can:

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

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

Of course you could also construct a wrapper for that via a struct (I would use a closure if C++ did them...):

structpipeWork {
     funcVec funcs;
     intrun(int i);
};

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

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

cout << foo(5) << endl;

Or something like that. Caveat: No idea what this will do if the pointers are passed between threads.

Extra caveat: If you want to do this with varying function interfaces, you will end up having to have a load of void *(void *)(void *) functions so that they can take whatever and emit whatever, or lots of templating to fix the kind of pipe you have. I suppose ideally you'd construct different kinds of pipe for different interfaces between functions, so that a | b | c works even when they are passing different types between them. But I'm going to guess that that's largely what the Boost stuff is doing.

Solution 5:

Depending on the simplicity of the functions :

#define pipe1(x) 2*x#define pipe2(x) pipe1(x)+1#define sink(x) pipe2(x)*3int j = 1while( ++j > 0 )
{
    std::cout << sink(j) << std::endl;
}

Post a Comment for "Lazy Transform In C++"