考虑以下玩具代码以确定范围是否包含元素:
template<typename Iter, typename T>
bool contains1(Iter begin, Iter end, const T& x)
{
for (; begin != end; ++begin)
{
if (*begin == x) return true;
}
return false;
}
(是的,我知道,标准库中已有完美的算法,但这不是重点。)
我如何用for_each
和lambda写同样的东西?以下不起作用......
template<typename Iter, typename T>
bool contains2(Iter begin, Iter end, const T& x)
{
std::for_each(begin, end, [&x](const T& y) {
if (x == y) return true;
});
return false;
}
...因为那只会从lambda返回,而不是从函数返回。
我是否必须抛出异常以摆脱lambda?同样,对于这个特定问题可能有十几个更好的解决方案,根本不涉及lambdas,但这不是我要求的。
答案 0 :(得分:7)
std::for_each
不是您应该使用的算法。您似乎想要std::find_if
或类似的东西。您应该使用最适合您任务的算法,而不仅仅是您熟悉的算法。
如果你真的,真的,真的必须尽早从算法“返回”,你可以 -
警告:以下是真正的真的坏主意,你几乎不应该这样做。实际上,查看代码可能会让你的脸变得脆弱。 你已被警告过了!
抛出异常:
bool contains2(Iter begin, Iter end, const T& x)
{
try {
std::for_each(begin, end, [&x](const T& y) {
if (x == y)
throw std::runtime_error("something");
});
}
catch(std::runtime_error &e) {
return true;
}
return false;
}
答案 1 :(得分:7)
我如何用
for_each
和lambda编写相同的东西?
你不能(不考虑例外)。你的函数与for-each循环(=一种映射)并不是同构的,它就像那样简单。
相反,您的功能是通过缩减来描述的,因此如果您想使用高阶函数来替换它,请使用缩小而不是地图。
如果 C ++ 具有适当的通用reduce
,那么您的算法将如下所示:
template<typename Iter, typename T>
bool contains2(Iter begin, Iter end, const T& x)
{
return stdx::reduce(begin, end, [&x](const T& y, bool accumulator) {
return accumulator or x == y;
});
}
当然,如果缩减适当地专门用于布尔结果值,这只会提前退出,以便短路。
唉,就我看来,C ++并没有提供这样的功能。有accumulate
但不会短路(它不能--C ++不知道内部 lambda的操作是短路的,而且它不是递归实现的)。
答案 2 :(得分:2)
Lambdas是错误的抽象层次,因为它们的行为很像函数 - 至少在控制流程方面,这在这里很重要。你不希望某些东西像'封装'那样作为一个函数(或程序编程的程序),它在C ++中只能直接返回或抛出异常。在我看来,任何颠覆这种行为的企图都应该被认为是病态的 - 或者至少不应该伪装成一个程序。
对于执行流程的细粒度控制,诸如协同程序之类的东西可能是更适合的抽象级别和/或原始级别。不过,我担心最终结果与使用std::for_each
完全不同。
答案 3 :(得分:2)
使用自定义算法:
template<class I, class F>
bool aborting_foreach(I first, I last, F f) {
while(;first!=last;++first) {
if(!f(*first))
return false;
}
return true;
}
好的,这实际上是std :: all_of,但你明白了。 (参见“减少答案”)。如果您的函数需要返回某种类型,则可能需要使用某种变体类型:
// Optional A value
template<class A>
class maybe {
// ...
};
或
// Stores either a A result of a B "non local return"
template<class A, class B>
class either {
…
};
查看相应的Haskell类型。你可以使用C ++ 01“unrestricted union”来干净地实现它。
执行非本地退出的一种干净方法是使用continuation但是你没有在C ++中使用它们。
答案 4 :(得分:2)
使用std::any_of。
template<typename Iter, typename T>
bool contains2(Iter begin, Iter end, const T& x)
{
const bool contains = std::any_of(begin, end, [&x](const T& y)
{
return x == y;
});
return contains;
}
答案 5 :(得分:0)
在这种情况下,lambda就像从给定函数contains2()
调用的任何其他函数一样。从其他函数返回并不意味着您从给定函数返回。因此,不可能,这就是设计的方式。
对于给定示例的模式,抛出异常是不必要的开销。我会在lambda中设置一个bool
变量而不是return
(并设置begin = end;
)。可以检查此bool
是否从给定函数contains2()
返回。
答案 6 :(得分:0)
正如您和其他人所指出的那样for_each
不适合在这里使用。没有办法打破for_each
循环 - 除了异常(双关语) - 你必须完全运行它。
template<typename Iter, typename T>
bool contains2(Iter begin, Iter end, const T& x)
{
bool tContains = false;
std::for_each(begin, end, [&](const T& y) mutable {
tContains = tContains || x == y;
});
return tContains;
}