Объединение предикатов - PullRequest
       20

Объединение предикатов

7 голосов
/ 13 февраля 2009

Есть ли способ объединить предикаты?

Допустим, у меня есть что-то вроде этого:

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

Это работает, но теперь я хотел бы сделать что-то вроде:

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

Так что я хотел бы найти первую строку, которая начинается с "-b" или первую строку, которая не начинается с "-". Тем не менее, это дает мне ошибку (двоичный «||» неопределен).

Есть ли способ сделать это?

Ответы [ 3 ]

5 голосов
/ 13 февраля 2009

Ну, у вас есть std :: logic_or и std :: compose2 , которые могут выполнять работу

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

но я думаю, что boost :: lambda и / или phoenix в конечном итоге более читабельны и являются моим рекомендуемым решением.

Кредиты должны идти в документацию SGI.

5 голосов
/ 13 февраля 2009

Я могу рекомендовать boost.lambda для объединения функциональных объектов для таких задач. Хотя это немного тяжеловесно для такой простой проблемы. ( edit ). Хороший пример использования STL см. В вики-ответе сообщества, начатом xhantt.

(старый, устарел, ответ) Вы можете написать свою собственную утилиту для этого, аналогично:

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

и затем используйте его: ... lazy_or(bind1st(...), bind1st(...)) ...

4 голосов
/ 13 февраля 2009

Если вы хотите составить предикаты, лучше всего написать их, вероятно, с помощью Boost Lambda или 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
                               )
                    );

Для способа Phoenix одной из возможностей является использование ленивых функций Phoenix, и решение может выглядеть аналогично приведенному ниже:

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

Однако для выполнения вашей задачи, вероятно, имеет больше смысла использовать различные инструменты - например, регулярные выражения (Boost Regex или Boost Xpressive). Если вы хотите обработать параметры командной строки, используйте параметры программы Boost.

...