2010-07-06 11 views
10

Come si può chiamare uno su parte di un container, utilizzando for_each()?Come combinare una funzione e un predicato in for_each?

Ho creato un for_each_if() per fare un

for(i in shapes) 
    if(i.color == 1) 
     displayShape(i); 

e la chiamata si presenta come

for_each_if(shapes.begin(), shapes.end(), 
         bind2nd(ptr_fun(colorEquals), 0), 
         ptr_fun(displayShape)); 

bool colorEquals(Shape& s, int color) { 
    return s.color == color; 
} 

Tuttavia, mi sento imitando gli algoritmi STL-simili non è una cosa che dovrei fare.

  1. C'è un modo per utilizzare le parole chiave STL solo esistenti per produrre questo?

    ho fatto non vogliono fare un

    for_each(shapes.begin(), shapes.end(), 
            bind2nd(ptr_fun(display_shape_if_color_equals), 0)); 
    

    perché, in un caso più complicato, il nome funtore sarebbe fuorviante rispetto a ciò che il funtore

  2. * C'è un modo per accedere a un membro di una struct (come colorEquals) per funzioni come for_each senza dover creare una funzione? *

risposta

1

Per utilizzare un regolare for_each con un se è necessario un Functor che emuli una condizione if.

#include <algorithm> 
#include <vector> 
#include <functional> 
#include <iostream> 
#include <boost/bind.hpp> 

using namespace std; 

struct incr { 
    typedef void result_type; 
    void operator()(int& i) { ++i; } 
}; 

struct is_odd { 
    typedef bool return_type; 
    bool operator() (const int& value) {return (value%2)==1; } 
}; 


template<class Fun, class Cond> 
struct if_fun { 
    typedef void result_type; 
    void operator()(Fun fun, Cond cond, int& i) { 
    if(cond(i)) fun(i); 
    } 
}; 


int main() { 
    vector<int> vec; 
    for(int i = 0; i < 10; ++i) vec.push_back(i); 

    for_each(vec.begin(), vec.end(), boost::bind(if_fun<incr, is_odd>(), incr(), is_odd(), _1)); 
    for(vector<int>::const_iterator it = vec.begin(); it != vec.end(); ++it) 
    cout << *it << " "; 
} 

Purtroppo il mio modello di aggiustamenti non è abbastanza buono per gestire questo con bind1st e bind2nd come si arriva in qualche modo confuso con il legante viene restituito essere un unary_function ma sembra piuttosto buono con boost::bind comunque. Il mio esempio non è perfetto in quanto non consente al Func passato in if_fun di tornare e immagino che qualcuno possa segnalare altri difetti. I suggerimenti sono ben accetti

+0

Freddo. Suppongo di poter aggiungere una funzione di supporto per istanziare un oggetto 'if_fun' in modo da poter ridurre i parametri del template quando si chiama' for_each() '... –

+0

boost :: bind funziona ugualmente bene con le normali funzioni quindi il rumore extra di I Functional basati sulla classe non sono realmente necessari – bradgonesurfing

+0

Questa soluzione è un casino un po 'pericoloso. Amplifica gli adattatori e la gamma è ciò che desideri utilizzare qui. – newhouse

8

L'imitazione di algoritmi simili a STL è esattamente ciò che si dovrebbe fare. Ecco perché sono nella STL.

In particolare, è possibile utilizzare un functor anziché creare una funzione effettiva e vincolarla. Questo è molto più ordinato, davvero.

template<typename Iterator, typename Pred, typename Operation> void 
for_each_if(Iterator begin, Iterator end, Pred p, Operation op) { 
    for(; begin != end; begin++) { 
     if (p(*begin)) { 
      op(*begin); 
     } 
    } 
} 
struct colorequals { 
    colorequals(int newcol) : color(newcol) {} 
    int color; 
    bool operator()(Shape& s) { return s.color == color; } 
}; 
struct displayshape { 
    void operator()(Shape& s) { // display the shape } 
}; 
for_each_if(shapes.begin(), shapes.end(), colorequals(0), displayshape()); 

Questo di solito è considerato il modo idiomatico di andare.

+0

Devo lavorare con i compilatori di ~ 10 C++, alcuni risalenti alla fine degli anni '90. L'implementazione STL in molti di essi è così diversificata, il che implica che S in STL non corrisponde a "Standard". Ecco perché tendo ad evitare di imitare le funzioni STL, e piuttosto di chiamarle. Ho pensato che se for_each_if() è il modo di risolvere il problema, allora dovrebbe essere già stato in STL, non dovrebbe? (Voglio dire remove_if, find_if, count_if c'è già). –

+1

@Grim: Normalmente le persone si limitano a inserire if nel proprio functor per for_each. Non c'è bisogno di un tale costrutto for_each_if. – Puppy

4

L'utilizzo degli adattatori del campo di incremento è molto più ordinato.

using boost::adaptor::filtered; 
using boost::bind; 

class Shape { 
    int color() const; 
}; 

void displayShape(const Shape & c); 

bool test_color(const Shape & s, int color){ 
    return s.color() == color; 
} 

boost::for_each 
    (vec | filtered(bind(&test_color, _1, 1) 
    , bind(&displayShape, _1) 
    ) 

Si noti l'uso della nuova biblioteca gamma di astrarre iteratori a favore delle gamme e gli adattatori gamma biblioteca per comporre una pipeline di operazioni.

Tutti gli algoritmi basati su iteratore standard hanno portati su algoritmi basati su intervallo.

immaginare questo

typedef boost::unordered_map<int, std::string> Map; 
Map map; 
... 
using boost::adaptor::map_keys; 
using boost::bind 
using boost::ref 
using boost::adaptor::filtered; 

bool gt(int a, int b) 
{ return a > b }; 

std::string const & get(const Map & map, int const & a) 
{ return map[a] } 

// print all items from map whose key > 5 
BOOST_FOREACH 
    (std::string const & s 
    , map 
     | map_keys 
     | filtered(bind(&gt, _1, 5)) 
     | transformed(bind(&get, ref(map), _1)) 
    ) 
    { 
     cout << s; 
    } 

Leggi Range Adaptors e Range Algorithm.

+0

l'url automatico-> una conversione href ha saltato gli algoritmi, presumibilmente a causa della lunghezza. –

Problemi correlati