9 Stimmen

Funktionszusammensetzung in C++

Es gibt viele beeindruckende Boost-Bibliotheken wie Boost.Lambda oder Boost.Phoenix, die dazu beitragen, C++ zu einer wirklich funktionalen Sprache zu machen. Aber gibt es einen einfachen Weg, eine zusammengesetzte Funktion aus beliebigen 2 oder mehr beliebigen Funktionen oder Funktoren zu erstellen?

Wenn ich habe: int f(int x) und int g(int x), möchte ich etwas wie f . g machen, was statisch ein neues Funktionsobjekt generieren würde, das äquivalent zu f(g(x)) ist.

Dies scheint durch verschiedene Techniken möglich zu sein, wie sie hier diskutiert werden. Sicherlich können Sie Aufrufe an boost::lambda::bind verketten, um einen zusammengesetzten Funktor zu erstellen. Aber gibt es in Boost etwas, das es Ihnen leicht ermöglicht, beliebige 2 oder mehr Funktionen oder Funktionen als kombinieren, um einen einzelnen zusammengesetzten Funktor zu erstellen, ähnlich wie Sie es in einer Sprache wie Haskell tun würden?

1 Stimmen

Sie können boost::bind anstelle von boost::lambda::bind für diese Aufgabe verwenden. Es sieht so aus: bind(g, bind(f, _1)). Kennen Sie das?

0 Stimmen

Ja, Sie können verschachtelte Aufrufe von boost::bind verwenden, um zusammengesetzte Funktoren zu erstellen. Ich habe mich jedoch gefragt, ob es einen besseren Weg gibt, um dies zu tun.

11voto

SpikedPunchVictim Punkte 111

Für alle, die auf diese Seite gestoßen sind, gibt es einen großartigen Blog-Beitrag zu diesem Thema von bureau14:

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

Dies macht sich die neuen Funktionen von C++ 11 zunutze und verwendet auch boost.

1 Stimmen

Fast vier Jahre nach qqlbrows Anfrage, aber hoffentlich für jemanden nützlich - hier ist der aktualisierte Link: https://blog.quasardb.net/2012/11/19/function-composition-in‌​-c11

5voto

Robert Mason Punkte 3829

Bei der Begegnung dieser Frage möchte ich darauf hinweisen, dass dies heute mit einer relativ eleganten Syntax unter Verwendung nur der Standardbibliothek und einiger Hilfsklassen dank decltype, auto und perfekter Weiterleitung möglich ist.

Definieren dieser beiden Klassen:

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

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

Ein einfaches Programm:

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::create(f, g);
        //oder:
        auto bar = pipe_source()[g][h];
        std::cout << foo(10) << std::endl;
        std::cout << bar(10) << std::endl;
        return 0;
}

Das hat den zusätzlichen Vorteil, dass, sobald es in einer Pipe ist, solange der Rückgabetyp korrekt ist, eine weitere Funktion f zu der Kette mit pipe[f] hinzugefügt werden kann.

Dann:

$ g++ test.cpp -o test -std=c++11
$ ./test
98
4
$

3voto

Edward Strange Punkte 39597

Ich kenne derzeit nichts, das die von Ihnen gewünschte Syntax unterstützt. Es wäre jedoch einfach, eine zu erstellen. Überschreiben Sie einfach * für Funktoren (zum Beispiel boost::function<>), damit es einen zusammengesetzten Funktor zurückgibt.

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

Nicht getestet, aber ich vermute, es ist nah dran, wenn es nicht von alleine funktioniert.

1 Stimmen

Da Rohfunktionen weder Klassen noch Aufzählungen sind, wird ein Aufruf wie f1 * f2 nicht nach überladenen Operatoren suchen, obwohl :( Der integrierte Operator wird verwendet und schlägt fehl, da er zwei Funktionen nicht multiplizieren kann.

0 Stimmen

Ja, funktioniert nur mit Funktoren. Du könntest eine Hilfsfunktion erstellen, die zu einer boost::function castet, um reguläre Funktionen anzupassen.

1voto

Puppy Punkte 141483

Template sie.

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

Ausgezeichneter Einsatz von typedefs wird empfohlen.
Bearbeiten: Hoppla. Es stellt sich heraus, dass die Typinferenz in Konstruktoren schlecht ist. Ich komme in einer Minute mit etwas zurück, das tatsächlich funktioniert :P
Noch mehr Bearbeiten:
Wenn Sie nur Functors anstelle von Funktionsoiden wollten, könnten Sie einfach eine neue Instanz erstellen oder sogar nur statische Funktionen verwenden.

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

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

Dies setzt voraus, dass Sie in einer gegebenen Funktion jede unterschiedliche Signatur irgendwie manuell behandeln können. Die Verwendung von decltype könnte in dieser Hinsicht mit einem C++0x-Compiler helfen.

1voto

Victor Laskin Punkte 2469

C++11. Kein Boost. Keine Hilfsklassen. Beliebige Anzahl von Argumenten. Nur std::function und Variadic Templates.

template 
struct function_composition_traits : public function_composition_traits
{};

template 
struct function_composition_traits
{
        typedef std::function composition;

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

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

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

Verwendung:

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 << "Ergebnis: " << test(2,3);

Ausgabe:

Ergebnis: 3.33333

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X