2009-02-13 14 views
7

¿Hay alguna forma de combinar predicados?Combinación de predicados

Digamos que tengo algo como esto:

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; 
} 

Esto funciona, pero ahora me gustaría hacer algo como:

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

Así que me gustaría encontrar la primera cuerda que comienza con "-b" o la primera cadena que no comienza con "-". Sin embargo, esto me da un error (binario '||' undefined).

¿Hay alguna manera de hacer esto?

Respuesta

5

Puedo recomendar boost.lambda para combinar funciones-objetos para tales tareas. Aunque es un poco pesado para un problema tan simple. (edit) Consulte la respuesta wiki de la comunidad iniciada por xhantt para ver un buen ejemplo utilizando STL.

(vieja, obsoleta, respuesta) Usted puede escribir su propia utilidad de esta, de forma similar:

// 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); 
} 

y luego usarlo: ... lazy_or(bind1st(...), bind1st(...)) ...

+1

Probablemente necesites una función shim para que compile también: no hay inferencia de argumento de plantilla para las clases. – xtofl

+0

Thx para señalar esto, edité la respuesta, y arreglé alguna otra parte también ... – gimpf

4

Si se quiere componer predicados, la mejor manera de escribir probablemente está utilizando el refuerzo 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 
           ) 
        ); 

Por la forma en Phoenix una de las posibilidades es usar las funciones perezosas Phoenix, y la solución c Ould aspecto similar a la de abajo:

// 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, "-") 
       ); 

Sin embargo, para realizar la tarea que probablemente tiene más sentido emplear diferentes herramientas - por ejemplo: las expresiones regulares (Regex Boost o Boost Xpressive). Si desea manejar las opciones de la línea de comando, entonces use las Opciones del Programa Boost.

+0

Ojalá pudiera aceptar dos respuestas para esta pregunta. Al final, encontré el modo no bibliotecario el más interesante. Aún así, gracias por dedicar el tiempo para escribir estos fragmentos. – drby

5

bien que han std::logical_or y std::compose2 que pueden hacer el trabajo

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

pero creo que boost :: lambda y/o Fénix son más legibles en el final, y son mi solución recomendada.

Los créditos deben ir a la documentación de SGI.

+0

Sabía que había olvidado la redacción obvia. Acabo de encontrar logical_or, y simplemente no lo recuerdo :-( – gimpf

+0

El problema con compose2 es que no forma parte del estándar actual de C++. – Anonymous

+0

Bueno, ni boost :: lambda ni phoenix son parte del estándar, sin embargo. – Ismael

Cuestiones relacionadas