2009-02-13 11 views
7

Esiste un modo per combinare predicati?Combinazione di predicati

Diciamo che ho qualcosa di simile:

class MatchBeginning : public binary_function<CStdString, CStdString, bool> 
{ public: 
      bool operator()(const CStdString &inputOne, const CStdString &inputTwo) const 
    { return inputOne.substr(0, inputTwo.length()).compare(inputTwo) == 0; } 
}; 

int main(int argc, char* argv[]) 
{ 
    CStdString myString("foo -b ar -t az"); 

    vector<CStdString> tokens; 

    // splits the string every time it encounters a "-" 
    split(myString, tokens, "-", true, true); 

    vector<CStdString>::iterator searchResult = find_if(tokens.begin(), tokens.end(), not1(bind2nd(MatchBeginning(), "-")));   

    return 0; 
} 

questo funziona, ma ora mi piacerebbe fare qualcosa di simile:

searchResult = find_if(tokens.begin(), tokens.end(), bind2nd(MatchBeginning(), "-b") || not1(bind2nd(MatchBeginning(), "-"))); 

Così mi piacerebbe trovare la prima stringa che inizia con "-b" o la prima stringa che non inizia con "-". Tuttavia, questo mi dà un errore (binario '||' indefinito).

C'è un modo per farlo?

risposta

5

Posso consigliare boost.lambda per la combinazione di oggetti funzione per tali attività. Anche se è un po 'pesante per un problema così semplice. (modifica) Vedere la risposta wiki della comunità iniziata da xhantt per un buon esempio utilizzando STL.

(vecchio, obsoleto, risposta) È possibile scrivere la propria utilità per questo, simile:

// here we define the combiner... 
template<class Left, class Right> 
class lazy_or_impl { 
    Left m_left; 
    Right m_right; 
public: 
    lazy_or_impl(Left const& left, Right const& right) : m_left(left), m_right(right) {} 
    typename Left::result_type operator()(typename Left::argument_type const& a) const { 
    return m_left(a) || m_right(a); 
    } 
}; 

// and a helper function which deduces the template arguments 
// (thx to xtofl to point this out) 
template<class Left, class Right> 
lazy_or_impl<Left, Right> lazy_or(Left const& left, Right const& right) { 
    return lazy_or_impl<Left, Right>(left, right); 
} 

e poi usarlo: ... lazy_or(bind1st(...), bind1st(...)) ...

+1

Probabilmente avresti bisogno anche di una funzione di shim da compilare: non esiste alcuna inferenza argomento template per le classi. – xtofl

+0

Thx per averlo indicato, ho modificato la risposta e corretto anche un'altra parte ... – gimpf

4

Se si desidera comporre predicati, il modo più bello per scrivere è probabilmente utilizzando la spinta Lambda o Boost Phoenix:

// Lambda way: 
// Needs: 
// #include <boost/lambda/lambda.hpp> 
// #include <boost/lambda/bind.hpp> 
{ 
    using namespace boost::lambda; 
    foo_vec::const_iterator it 
     = std::find_if(
        tokens.begin(), 
        tokens.end(), 
        bind(MatchBeginning(), _1, "-b") || !bind(MatchBeginning(), _1, "-") 
        ); 
} 
// Boost bind way: 
// Needs: 
// #include <boost/bind.hpp> 
{ 
    foo_vec::const_iterator it 
     = std::find_if(
        tokens.begin(), 
        tokens.end(), 
        boost::bind(
           std::logical_or<bool>(), 
           boost::bind(MatchBeginning(), _1, "-b"), 
           !boost::bind(MatchBeginning(), _1, "-") // ! overloaded in bind 
           ) 
        ); 

per il modo in cui Phoenix una delle possibilità è quella di utilizzare le funzioni di Phoenix pigri, e la soluzione c ould aspetto simile a quello qui sotto:

// Requires: 
// #include <boost/spirit/include/phoenix_core.hpp> 
// #include <boost/spirit/include/phoenix_function.hpp> 
// #include <boost/spirit/include/phoenix_operator.hpp> 
namespace phx = boost::phoenix; 

struct match_beginning_impl 
{ 
    template <typename Arg1, typename Arg2> 
    struct result 
    { 
     typedef bool type; 
    }; 

    template <typename Arg1, typename Arg2> 
    bool operator()(Arg1 arg1, Arg2 arg2) const 
    { 
     // Do stuff 
    } 
}; 
phx::function<match_beginning_impl> match_beginning; 

using phx::arg_names::arg1; 

foo_vec::const_iterator it 
    = std::find_if(
       tokens.begin(), 
       tokens.end(), 
       match_beginning(arg1, "-b") || !match_beginning(arg1, "-") 
       ); 

Tuttavia per completare l'attività probabilmente ha più senso di impiegare diversi strumenti - ad esempio: le espressioni regolari (Boost Regex o Boost Xpressive). Se si desidera gestire le opzioni della riga di comando, utilizzare le opzioni del programma di potenziamento.

+0

Vorrei poter accettare due risposte per questa domanda. Alla fine ho trovato il modo non-libreria il più interessante. Comunque, grazie per aver dedicato del tempo a scrivere questi frammenti. – drby

5

bene bisogna std::logical_or e std::compose2 che può fare il lavoro

find_if(tokens.begin(), tokens.end(), 
    compose2(logical_or<bool>(), 
    bind2nd(MatchBeginning(), "-b"), 
    bind2nd(MatchBeginning(), "-") 
) 
); 

ma penso che boost :: lambda e/o Phoenix sono più leggibili, alla fine, e sono la mia soluzione consigliata.

I crediti devono essere inviati alla documentazione SGI.

+0

Sapevo di aver dimenticato l'ovvia composizione. Ho appena trovato logico_or, e semplicemente non lo ricordo :-( – gimpf

+0

Il problema con compose2 è che non fa parte dello standard C++ corrente – Anonymous

+0

Bene, né boost :: lambda né phoenix fanno parte dello standard, ancora. – Ismael