以功能方式组合谓词并允许短路工作

问题描述

前言

我问了 a similar 个问题:假设我有一个谓词 auto p1 = [](int x){ return x > 2; }一个谓词 auto p2 = [](int x){ return x < 6; },我如何结合 p1p2 来获得 {{ 1}} 这样 p1and2?答案是使用 boost::hana::demux(有关详细信息,请参阅链接的问题)。

新问题和疑问

然而,有时,只有当另一个谓词评估为给定的真实性值时,才应该对一个谓词进行评估,例如p1and2(x) == p1(x) && p2(x)

例如一个谓词可能是

true

和另一个谓词

constexpr auto has_value = [](std::optional<int> opt){ return opt.has_value(); };

以下内容很容易识别

constexpr auto has_positive = [](std::optional<int> opt){ return opt.value() == 3; };

定义

bool b1 = has_value(some_opt_int) && has_positive(some_opt_int); // true or false,but just fine either way
bool b2 = has_positive(some_opt_int) && has_value(some_opt_int); // runtime error if !some_opt_int.has_value()

并像这样使用它

constexpr auto all = boost::hana::demux([](auto const&... x) { return (x && ...); });

会导致运行时失败,因为the it's the function call to the variadic generic lambda wrapped in all which forces the evaluation of its arguments,not the fold expression (x && ...)

所以我的问题是,我如何结合 std::optional<int> empty{}; contexpr auto has_value_which_is_positive = all(has_value,has_positive); bool result = has_value_which_is_positive(empty); has_value 来获得 has_positive?更一般地说,我如何将几个谓词“和”在一起,以便仅根据短路机制要求对它们进行评估?

我的尝试

我认为,为了防止谓词被评估,我可以将它们包装在一些函数对象中,当应用于参数(has_value_which_is_positive)时,返回另一个包装谓词的对象和 std::optional 在一起,并有一个 std::optional 转换函数,只有在计算折叠表达式时才会触发。

这是我的尝试,这是一个可悲的未定义行为,因为 operator bool 中的断言有时会失败,有时不会:

main

(Follow up question.)

解决方法

我刚刚意识到执行我所描述的操作的临时 lambda 实际上非常简洁:

#include <assert.h>
#include <optional>

constexpr auto all = [](auto const& ... predicates){
    return [&predicates...](auto const& x){
        return (predicates(x) && ...);
    };
};

constexpr auto has_value = [](std::optional<int> o){
    return o.has_value();
};
constexpr auto has_positive = [](std::optional<int> o){
    assert(o.has_value());
    return o.value() > 0;
};

int main() {
    assert(all(has_value,has_positive)(std::optional<int>{2}));
    assert(!all(has_value,has_positive)(std::optional<int>{}));
}

然而,there's still something about short-circuiting in fold expressions that doesn't convince me...

,

也许你的问题比我能理解的更微妙,但这个非常简单的解决方案似乎可以解决问题(据我所知)。

/**
  g++ -std=c++17 -o prog_cpp prog_cpp.cpp \
      -pedantic -Wall -Wextra -Wconversion -Wno-sign-conversion \
      -g -O0 -UNDEBUG -fsanitize=address,undefined
**/

#include <iostream>
#include <optional>

template<typename Predicate1,typename Predicate2>
auto
lazy_and(Predicate1 p1,Predicate2 p2)
{
  return [&](const auto &arg)
  {
    return p1(arg)&&p2(arg);
  };
}

int
main()
{
  // basic predicates
  const auto has_value=[&](const auto &opt){ return opt.has_value(); };
  const auto is_positive=[&](const auto &opt){ return opt.value()>0; };
  // combination of predicates
  const auto has_value_and_is_positive=lazy_and(has_value,is_positive);
  const auto is_positive_and_has_value=lazy_and(is_positive,has_value);
  // test
  const auto test_predicate=
    [&](const auto &title,const auto &predicate,const auto &arg)
    {
      try
      {
        const auto result=predicate(arg);
        std::cout << title << " --> " << result << '\n';
      }
      catch(const std::exception &e)
      {
        std::cerr << title << " !!! " << e.what() << '\n';
      }
    };
  std::cout << "~~~~ empty ~~~~\n";
  const auto empty=std::optional<int>{};
  test_predicate("has_value",has_value,empty);
  test_predicate("is_positive",is_positive,empty);
  test_predicate("has_value_and_is_positive",has_value_and_is_positive,empty);
  test_predicate("is_positive_and_has_value",is_positive_and_has_value,empty);
  std::cout << "~~~~ positive ~~~~\n";
  const auto positive=std::optional<int>{5};
  test_predicate("has_value",positive);
  test_predicate("is_positive",positive);
  test_predicate("has_value_and_is_positive",positive);
  test_predicate("is_positive_and_has_value",positive);
  std::cout << "~~~~ negative ~~~~\n";
  const auto negative=std::optional<int>{-3};
  test_predicate("has_value",negative);
  test_predicate("is_positive",negative);
  test_predicate("has_value_and_is_positive",negative);
  test_predicate("is_positive_and_has_value",negative);
}

/**
~~~~ empty ~~~~
has_value --> 0
is_positive !!! bad optional access
has_value_and_is_positive --> 0
is_positive_and_has_value !!! bad optional access
~~~~ positive ~~~~
has_value --> 1
is_positive --> 1
has_value_and_is_positive --> 1
is_positive_and_has_value --> 1
~~~~ negative ~~~~
has_value --> 1
is_positive --> 0
has_value_and_is_positive --> 0
is_positive_and_has_value --> 0
**/

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...