La combinaison de prédicats
-
23-08-2019 - |
Question
Est-il possible que vous pouvez combiner 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 qui commence par « -b » ou la première chaîne qui ne commence pas par « - ». Cependant, cela me donne une erreur (binaire « || » non définie).
Est-il possible de le faire?
La solution
Je peux recommander boost.lambda pour combiner des objets de fonction pour ces tâches. Bien qu'il soit un poids lourd de bits pour un problème simple. ( modifier ) Voir la réponse de la communauté wiki a commencé 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);
}
et ensuite l'utiliser: ... lazy_or(bind1st(...), bind1st(...)) ...
Autres conseils
Eh bien vous avez std :: logical_or et std :: compose2 qui peut 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 devraient aller à la documentation SGI.
Si vous voulez composer prédicats, la plus belle façon de l'é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 le chemin de Phoenix l'une des possibilités est d'utiliser les fonctions paresseux de Phoenix, et la solution pourrait ressembler à 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 voulez gérer les options de ligne de commande puis utilisez les options du programme Boost.