2009-02-13 8 views
7

Existe-t-il un moyen de combiner des prédicats?Combinaison de prédicats

Disons que j'ai quelque chose comme ceci:

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

Cela fonctionne, mais maintenant je voudrais faire quelque chose comme:

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

Je voudrais trouver la première chaîne cela commence par "-b" ou la première chaîne qui ne commence pas par "-". Cependant, cela me donne une erreur (binaire '||' indéfini).

Y at-il un moyen de le faire?

Répondre

5

Je peux recommander boost.lambda pour combiner des objets-fonctions pour de telles tâches. Bien qu'il soit un peu lourd pour un problème aussi simple. (edit) Voir la réponse wiki de la communauté commencée par xhantt pour un bon exemple en utilisant STL.

(vieux, dépréciée, réponse) Vous pouvez écrire votre propre utilitaire pour cela, similaire:

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

puis l'utiliser: ... lazy_or(bind1st(...), bind1st(...)) ...

+1

Vous auriez probablement besoin d'une fonction shim pour compiler aussi: il n'y a pas d'inférence d'argument de template pour les classes. – xtofl

+0

Thx pour le signaler, j'ai édité la réponse, et ai corrigé une autre partie aussi ... – gimpf

4

Si vous voulez composer prédicats, la plus belle façon de écrire utilise probablement le Boost Lambda ou 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 
           ) 
        ); 

pour la façon dont Phoenix l'une des possibilités est d'utiliser les fonctions paresseux de Phoenix, et la solution c ould regard semblable à celui ci-dessous:

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

Cependant, pour accomplir votre tâche, il est probablement plus judicieux d'employer différents outils - par exemple: les expressions régulières (Boost Regex ou Boost Xpressive). Si vous souhaitez gérer les options de ligne de commande, utilisez les options du programme Boost.

+0

Je voudrais pouvoir accepter deux réponses à cette question. En fin de compte, j'ai trouvé le moyen non-bibliothèque le plus intéressant. Encore, merci d'avoir pris le temps d'écrire ces extraits. – drby

5

Eh bien, vous avez std::logical_or et std::compose2 qui peuvent faire le travail

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

mais je pense que boost :: lambda et/ou Phoenix sont plus lisibles à la fin, et sont ma solution recommandée. Les crédits doivent aller à la documentation SGI.

+0

Je savais que j'avais oublié la composition évidente. Je viens de trouver logical_or, et je ne m'en souviens pas :-( – gimpf

+0

Le problème avec composer2 est qu'il ne fait pas partie de la norme C++ actuelle – Anonymous

+0

Eh bien, ni boost :: lambda ni phoenix ne font partie de la norme, pour le moment. – Ismael