【问题标题】:Combining Predicates组合谓词
【发布时间】:2010-10-07 10:05:17
【问题描述】:

有什么方法可以组合谓词吗?

假设我有这样的事情:

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”开头的字符串或第一个不以“-”开头的字符串。但是,这给了我一个错误(二进制“||”未定义)。

有什么办法吗?

【问题讨论】:

    标签: c++ stl functional-programming predicate


    【解决方案1】:

    我可以推荐 boost.lambda 为此类任务组合函数对象。虽然对于这样一个简单的问题来说它有点重量级。 (edit)查看由 xhantt 发起的社区 wiki 回答,了解使用 STL 的一个很好的示例。

    (old, deprecated, answer) 你可以为此编写自己的实用程序,类似:

    // 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(...)) ...

    【讨论】:

    • 您可能还需要一个 shim 函数来编译它:类没有模板参数推断。
    【解决方案2】:

    如果您想编写谓词,最好的编写方法可能是使用 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 Program Options。

    【讨论】:

    • 我希望我能接受这个问题的两个答案。最后我发现非图书馆方式最有趣。不过,感谢您花时间写出这些 sn-ps。
    【解决方案3】:

    你有 std::logical_orstd::compose2 可以完成这项工作

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

    但我认为 boost::lambda 和/或 phoenix 最终更具可读性,是我推荐的解决方案。

    学分应该转到 SGI 文档。

    【讨论】:

    • 我知道我已经忘记了明显的作曲。我刚刚找到了logical_or,只是不记得了:-(
    • compose2 的问题在于它不是当前 C++ 标准的一部分。
    • 好吧,boost::lambda 和 phoenix 都不是标准的一部分。
    • 我知道,我的意思是很清楚从哪里获取 lambda 和 phoenix,而对于 compose2 则不是很清楚(有些编译器有,有些没有,等等)。
    • 但仍然认为 boost::lambda 最终更具可读性和紧凑性,如果我可以使用 boost 我肯定会使用它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-16
    • 1970-01-01
    • 2012-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多