合并谓词

7

有没有办法将谓词组合起来?

假设我有这样的代码:

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
我可以推荐使用boost.lambda来组合函数对象完成此类任务。虽然对于这样一个简单的问题,它可能有点沉重。(编辑)请查看由xhantt开始的社区wiki答案,其中提供了一个使用STL的很好的示例。
(旧的、不再使用的答案)您可以编写自己的实用程序来完成类似的操作:
// 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(...)) ...

(注:本句为原文,无需翻译)

1
您可能需要一个shim函数来编译它:类没有模板参数推断。 - xtofl

5

你可以使用std::logical_orstd::compose2来完成这项任务。

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

但我认为在最终使用时,boost::lambda和/或phoenix更易读,并且是我推荐的解决方案。

功劳归于SGI文档。


compose2的问题在于它不是当前C++标准的一部分。 - Anonymous
嗯,boost::lambda和phoenix都还不是标准的一部分。 - Ismael
我知道,我的意思是获取lambda和phoenix相当清晰,而对于compose2来说就不那么清晰了(有些编译器有它,有些没有等等)。 - Anonymous
但我仍然认为boost::lambda最终更易读和紧凑,如果我能使用boost,我肯定会使用它。 - Ismael
但是,在资源有限的编译器中,有时候让compose2工作起来比让完整的boost::lambda工作起来更容易。 - Ismael
显示剩余3条评论

4

如果你想编写谓词,最好的方法可能是使用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程序选项。


我希望我能接受这个问题的两个答案。最终,我发现非库方式最有趣。还是感谢你们花时间编写这些片段。 - drby

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接