STL Algorithmus statt Schleife: Argumente binden, wie?
-
Servus,
ich stehe am Anfang des Durcharbeitens von "Effective STL". Bisher habe ich von der STL wenig mehr als die üblichen Container genutzt.
Nun würde ich gerne eine Schleife, die über einen Vector von Werten iteriert, durch einen STL-Operation ersetzen.
Dabei scheitere ich aber am binden der freien Argumente.
Hier der vorher-Zustand:
// macht was mit den einzelnen Werten double transmogrify(double, double, double); // schreibe verarbeitete Werte in neuen Vektor: std::vector<float> modified_values; modified_values.resize(old_values.size()); for (std::size_t i = 0 ; i < old_values.size() ; i++) modified_values[i] = transmogrify(old_values[i], member, anderer_member);Der gewünschte nachher-Zustand wäre in etwa folgender:
std::vector<float> modified_values; modified_values.resize(old_values.size()); std::transform(old_values.begin(), old_values.end(), modified_values.begin(), transmogrify(_1, member, anderer_member) );Folgendes habe ich probiert, geht nicht:
-Zweimal std::bind2nd auf transmogrify anwenden, um zuerst aus der triadischen eine dyadische Funktion*) zu machen (den zweiten Parameter von dreien binden), und danach aus der dyadischen eine monadische Funktion (durch binden des noch verbliebenen zweiten (vormals dritten) Parameters).
Fehlermeldung (gcc 4.4):/usr/include/c++/4.4/backward/binders.h:135: error: ‘double(double, double, double)’ is not a class, struct, or union type-Oben gezeigte _1 als Platzhalter von boost::lambda benutzen
Fehlermeldung:cannot convert ‘const boost::lambda::lambda_functor<boost::lambda::placeholder<1> >’ to ‘double’ for argument ‘1’ to ‘double transmogrify(double, double, double)’Ich vermute, ein Problem ist hier die implizite Umwandlung von double in float, die ich in diesem Kontext leider vornehmen muss.
Habe std::for_each bisher immer nur mit monadischen Funktionen genutzt (etwa um Werte auszugeben), vielleicht kann mir armen Unwissenden mal ein Guru der Funktoren erklären, wie man das mit mehr Parametern macht.
Danke!
wie nennt man das im C++-Sprachgebrauch? Trinäre Funktion??? Triadisch/Dyadisch/Monadisch sind die Bezeichnungen, die ich aus "Clean Code" kenne.
-
Wenn du sowieso boost benutzt kannst du ja gleich boost::bind benutzen. Das geht dann ziemlich genau so, wie du dir das vorstellst.
Ansonsten kannst du die beiden Parameter einfach in ein struct packen und dann das übergeben, anstatt das mit 3 Parametern machen. Dann kannst du auch ganz einfach bind2nd benutzen.
-
Hmm, das boost::lambda war eher eine Verlegenheitslösung, schöner wäre es natürlich, wenn es ohne boost ginge.
-
drakon schrieb:
Wenn du sowieso boost benutzt kannst du ja gleich boost::bind benutzen. Das geht dann ziemlich genau so, wie du dir das vorstellst.
Oder tr1::bind welcher beim gcc4.4 dabei ist. <tr1/functional>
-
drakon schrieb:
Ansonsten kannst du die beiden Parameter einfach in ein struct packen und dann das übergeben, anstatt das mit 3 Parametern machen. Dann kannst du auch ganz einfach bind2nd benutzen.
Hmm, also mit einem pair als 2ter Paramater hab ich das gleiche Problem:
double transmogrify(double, std::pair<double, double>); std::transform(old_values.begin(), old_values.end(), modified_values.begin(), std::bind2nd(transmogrify, std::make_pair(member, anderer_member)));Fehler:
In file included from /usr/include/c++/4.4/bits/stl_function.h:712, from /usr/include/c++/4.4/functional:50, from test.cpp:9: /usr/include/c++/4.4/backward/binders.h: In instantiation of ‘std::binder2nd<double(double, std::pair<double, double>)>’: test.cpp:34: instantiated from here /usr/include/c++/4.4/backward/binders.h:135: error: ‘double(double, std::pair<double, double>)’ is not a class, struct, or union type /usr/include/c++/4.4/backward/binders.h:138: error: ‘double(double, std::pair<double, double>)’ is not a class, struct, or union type /usr/include/c++/4.4/backward/binders.h:141: error: ‘double(double, std::pair<double, double>)’ is not a class, struct, or union type /usr/include/c++/4.4/backward/binders.h:146: error: ‘double(double, std::pair<double, double>)’ is not a class, struct, or union type /usr/include/c++/4.4/backward/binders.h:152: error: ‘double(double, std::pair<double, double>)’ is not a class, struct, or union type /usr/include/c++/4.4/backward/binders.h:137: error: field ‘std::binder2nd<double(double, std::pair<double, double>)>::op’ invalidly declared function type /usr/include/c++/4.4/backward/binders.h: In function ‘std::binder2nd<_Operation> std::bind2nd(const _Operation&, const _Tp&) [with _Operation = double(double, std::pair<double, double>), _Tp = std::pair<double, double>]’: test.cpp:34: instantiated from here /usr/include/c++/4.4/backward/binders.h:161: error: ‘double(double, std::pair<double, double>)’ is not a class, struct, or union type In file included from /usr/include/c++/4.4/algorithm:62, from test.cpp:8: /usr/include/c++/4.4/bits/stl_algo.h: In function ‘_OIter std::transform(_IIter, _IIter, _OIter, _UnaryOperation) [with _IIter = __gnu_cxx::__normal_iterator<const float*, std::vector<float, std::allocator<float> > >, _OIter = __gnu_cxx::__normal_iterator<float*, std::vector<float, std::allocator<float> > >, _UnaryOperation = std::binder2nd<double(double, std::pair<double, double>)>]’: test.cpp:34: instantiated from here /usr/include/c++/4.4/bits/stl_algo.h:4703: error: no match for call to ‘(std::binder2nd<double(double, std::pair<double, double>)>) (const float&)’
-
Alternativ kannst du dir einen Funktor basteln, der Parameter als Konstruktor uebernimmt und den Klammeroperator implementiert. Sowas aehnliches habe ich mal bezueglich Sortierung gemacht. Guckst du hier. Die letzte im Artikel angesprochene Moeglichkeit laesst sich auch auf std::transform uebertragen.
-
#include <algorithm> #include <vector> #include <tr1/functional> // macht was mit den einzelnen Werten double transmogrify(double, double, double) { return 1.0; } int main() { using namespace std; using namespace tr1::placeholders; double member = 4., anderer_member = 5.6; std::vector<double> old_values(10,0); std::vector<double> modified_values; modified_values.resize(old_values.size()); std::transform(old_values.begin(), old_values.end(), modified_values.begin(), tr1::bind(transmogrify,_1, member, anderer_member )); return 0; }
-
Wobei ich immer eher gegen die Verwendung dieser Funktionen bin, da sie erstens schwerer zu lesen und noch viel viel schlechter zu erweitern sind. Mal eben noch ein paar Debugausgaben da reinschreiben (und nur an der Stelle, also nicht in der aufgerufenen Funktion) geht nicht, ohne das ganze wieder aufzudröseln.
-
knivil schrieb:
Alternativ kannst du dir einen Funktor basteln, der Parameter als Konstruktor uebernimmt und den Klammeroperator implementiert.
Super Idee, Danke! Funktioniert. Warum hab ich da nicht gleich dran gedacht?