Композиция функций в C ++ - PullRequest
9 голосов
/ 16 мая 2010

Существует множество впечатляющих библиотек Boost, таких как Boost.Lambda или Boost.Phoenix, которые в значительной степени превращают C ++ в действительно функциональный язык. Но есть ли простой способ создать составную функцию из любых 2 или более произвольных функций или функторов?

Если у меня есть: int f(int x) и int g(int x), я хочу сделать что-то вроде f . g, которое статически сгенерирует новый функциональный объект, эквивалентный f(g(x)).

Это представляется возможным с помощью различных методов, таких как обсуждаемые здесь . Конечно, вы можете связывать вызовы boost::lambda::bind для создания составного функтора. Но есть ли в Boost что-нибудь, что позволяет вам легко брать любые 2 или более функций или функциональных объектов и комбинировать их для создания единого составного функтора, подобно тому, как вы делали бы это на языке наподобие Haskell?

Ответы [ 6 ]

10 голосов
/ 21 ноября 2012

Для любого, кто наткнулся на эту страницу, есть отличное сообщение в блоге на эту тему от бюро 14:

http://blog.quasardb.net/function-composition-in-c11/

Это использует преимущества новых функций в C ++ 11, а также использует boost.

4 голосов
/ 01 ноября 2012

Спотыкаясь на этот вопрос, я хотел бы указать всем, кто сталкивался с этим сегодня, что это возможно при относительно элегантном синтаксисе с использованием только стандартной библиотеки и нескольких вспомогательных классов благодаря decltype, auto и совершенной пересылке .

Определение этих двух классов:

template <class Arg, class ArgCall, class OuterCall>
class pipe {
private:
    ArgCall argcall;
    OuterCall outercall;
public:
    typedef pipe<Arg, ArgCall, OuterCall>  this_type;
    pipe(ArgCall ac, OuterCall oc) : argcall(ac), outercall(oc) {}
    auto operator()(Arg arg) -> decltype(outercall(argcall(arg))) {
        return outercall(argcall(arg));
    }
    template <class NewCall>
    pipe<Arg, this_type, NewCall> operator[](NewCall&& nc) {
        return {*this, std::forward<NewCall>(nc)};
    }
};

template <class Arg>
class pipe_source {
public:
    typedef pipe_source<Arg> this_type;
    Arg operator()(Arg arg) {
        return arg;
    }
    template <class ArgCall, class OuterCall>
    static pipe<Arg, ArgCall, OuterCall> create(ArgCall&& ac, OuterCall&& oc) {
        return {std::forward<ArgCall>(ac), std::forward<OuterCall>(oc)};
    }
    template <class OuterCall>
    pipe<Arg, this_type, OuterCall> operator[](OuterCall&& oc) {
        return {*this, std::forward<OuterCall>(oc)};
    }
};

Простая программа:

int f(int x) {
        return x*x;
}

int g(int x) {
        return x-2;
}

int h(int x) {
        return x/2;
}

int main() {
        auto foo = pipe_source<int>::create(f, g);
        //or:
        auto bar = pipe_source<int>()[g][h];
        std::cout << foo(10) << std::endl;
        std::cout << bar(10) << std::endl;
        return 0;
}

Это дает дополнительное преимущество: если он находится в конвейере, если тип возвращаемого значения правильный, вы можете добавить еще одну функцию f в цепочку с конвейером [f].

Тогда:

$ g++ test.cpp -o test -std=c++11
$ ./test
98
4
$
3 голосов
/ 16 мая 2010

Я не знаю ничего, что поддерживает синтаксис, который вы хотите в настоящее время. Тем не менее, было бы просто создать его. Просто переопределите * для функторов (например, boost :: function <>), чтобы он возвращал составной функтор.


template < typename R1, typename R2, typename T1, typename T2 >
boost::function<R1(T2)> operator * (boost::function<R1(T2)> const& f, boost::function<R2(T2)> const& g)
{
  return boost::bind(f, boost::bind(g, _1));
}

Не проверено, но я подозреваю, что оно близко, если не работает "из коробки".

1 голос
/ 16 мая 2010

Шаблон их.

template<typename T1> class FunctorOne {
    FunctorOne(T1 newt)
        : t(newt) {}
    void operator()() {
        t();
    }
    T1 t;
};
template<> class FunctorOne<void> {
    void operator()() {
    }
};
template<typename T1> class FunctorTwo {
    FunctorOne(T1 newt)
        : t(newt) {}
    void operator()() {
        t();
    }
    T1 t;
};
template<> class FunctorTwo<void> {
    void operator()() {
    }
};
FunctorOne<FunctorTwo<FunctorOne<FunctorTwo<void>>>>> strangefunctionobject(FunctorTwo(FunctorOne(FunctorTwo()));

Рекомендуется отличное использование typedefs.
Редактировать: Упс. Оказывается, что вывод типа в конструкторах отстой. Я вернусь через минуту с чем-то, что действительно работает: P
Еще больше править:
Если вам нужны только функторы, а не функционоиды, вы можете просто создать новый экземпляр или даже просто использовать статические функции.

template<typename T1, typename T2> class FunctorOne {
public:
    static bool Call() {
        T1::Call(T2::Call());
        return true;
    }
};
template<> class FunctorOne<void, void> {
public:
    static bool Call() {
    }
};
template<typename T1> class FunctorTwo {
public:
    static bool Call() {
        T1::Call();
    }
};
template<> class FunctorTwo<void> {
public:
    static bool Call() {
    }
};

bool haicakes = FunctorOne<FunctorTwo<void>, FunctorTwo<void>>::Call();

Это предполагает, что в любой заданной функции вы можете обрабатывать каждую отдельную подпись несколько вручную. Использование decltype может помочь в этом отношении с компилятором C ++ 0x.

0 голосов
/ 12 февраля 2015

C ++ 11. Нет повышения. Нет вспомогательных классов. Любое количество аргументов. Просто шаблоны std :: function и variadic.

template <typename F1, typename F2>
struct function_composition_traits : public function_composition_traits<decltype(&F1::operator()), decltype(&F2::operator())>
{};

template <typename ClassType1, typename ReturnType1, typename... Args1, typename ClassType2, typename ReturnType2, typename... Args2>
struct function_composition_traits<ReturnType1(ClassType1::*)(Args1...) const, ReturnType2(ClassType2::*)(Args2...) const>
{
        typedef std::function<ReturnType2(Args1...)> composition;

        template <typename Func1, typename Func2>
        inline static composition compose(const Func1& f1, const Func2& f2) {
            return [f1,f2](Args1... args) -> ReturnType2 { return f2(f1(std::forward<Args1>(args)...)); };
        }
};

template <typename F1, typename F2>
typename function_composition_traits<F1,F2>::composition compose(const F1& lambda1,const F2& lambda2)
{
        return function_composition_traits<F1,F2>::template compose<F1,F2>(lambda1, lambda2);
}

template <typename F, typename... Fs>
auto compose(F f, Fs... fs) -> decltype(compose(f, compose(fs...)))
{
        return compose(f, compose(std::forward<Fs>(fs)...));
}

Использование:

auto add = [](int x, int y){ return x+y; };
auto mul = [](int x){ return x*2; };
auto divide = [](int x) { return (double)x / 3.0; };
auto test = compose(add, mul, divide);

cout << "Result: " << test(2,3);

Выход:

Результат: 3.33333

0 голосов
/ 31 декабря 2014

См. Этот ответ https://stackoverflow.com/a/27727236/286335. Действительно короткий, легкий и общий.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...