以功能方式组合谓词并允许短路工作
Posted
技术标签:
【中文标题】以功能方式组合谓词并允许短路工作【英文标题】:Combining predicates in a functional way and allowing short-circuiting to work 【发布时间】:2021-05-13 12:14:39 【问题描述】:前言
我问a similar 问题:假设我有一个谓词auto p1 = [](int x) return x > 2;
和一个谓词auto p2 = [](int x) return x < 6;
,我如何结合p1
和p2
得到p1and2
这样p1and2(x) == p1(x) && p2(x)
?答案是使用boost::hana::demux
(有关详细信息,请参阅链接的问题)。
新问题和问题
然而,有时,只有当另一个谓词评估为给定的真实性值时,才应该对一个谓词进行评估,例如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 && ...); );
并像这样使用它
std::optional<int> empty;
contexpr auto has_value_which_is_positive = all(has_value, has_positive);
bool result = has_value_which_is_positive(empty);
会导致运行时失败,因为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 && ...)
。
所以我的问题是,我如何结合has_value
和has_positive
得到has_value_which_is_positive
?更一般地说,我如何将几个谓词“和”在一起,以便仅在短路机制所需的范围内对它们进行评估?
我的尝试
我认为,为了防止谓词被评估,我可以将它们包装在一些函数对象中,当应用于参数(std::optional
)时,返回另一个包装谓词和std::optional
在一起,并且有一个 operator bool
转换函数,只有在计算折叠表达式时才会触发。
这是我的尝试,这是可悲的未定义行为,因为main
中的断言有时会失败,有时不会:
#include <optional>
#include <boost/hana/functional/demux.hpp>
#include <boost/hana/functional/curry.hpp>
template<typename P, typename T>
struct LazilyAppliedPred
LazilyAppliedPred(P const& p, T const& t)
: p(p)
, t(t)
P const& p;
T const& t;
operator bool() const
return p(t);
;
constexpr auto lazily_applied_pred = [](auto const& p, auto const& t)
return LazilyAppliedPred(p,t);
;
auto constexpr lazily_applied_pred_curried = boost::hana::curry<2>(lazily_applied_pred);
constexpr auto all_true = [](auto const&... x) return (x && ...); ;
constexpr auto all = boost::hana::demux(all_true);
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(lazily_applied_pred_curried(has_value),
lazily_applied_pred_curried(has_positive))(std::optional<int>2));
(Follow up question.)
【问题讨论】:
【参考方案1】:我刚刚意识到执行我所描述的操作的临时 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...
【讨论】:
【参考方案2】:也许你的问题比我能理解的更微妙,但这个非常简单的解决方案似乎可以解决问题(据我所知)。
/**
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",
has_value, positive);
test_predicate("is_positive",
is_positive, positive);
test_predicate("has_value_and_is_positive",
has_value_and_is_positive, positive);
test_predicate("is_positive_and_has_value",
is_positive_and_has_value, positive);
std::cout << "~~~~ negative ~~~~\n";
const auto negative=std::optional<int>-3;
test_predicate("has_value",
has_value, negative);
test_predicate("is_positive",
is_positive, negative);
test_predicate("has_value_and_is_positive",
has_value_and_is_positive, negative);
test_predicate("is_positive_and_has_value",
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
**/
【讨论】:
以上是关于以功能方式组合谓词并允许短路工作的主要内容,如果未能解决你的问题,请参考以下文章
如何让 extjs 组合框像普通的 html 选择框一样工作?
对于 CONTAINS 全文谓词,SQL Server 2008 中的逻辑短路似乎失败