【发布时间】:2018-11-16 16:26:01
【问题描述】:
据我了解,range-v3 库的视图操作(目前需要 C++17,但要成为 C++20 中 STL 的正式部分)提供了可链接的类似 STL 的算法,这些算法是延迟评估的。作为一个实验,我创建了以下代码来评估前 4 个完美数字:
#include <iostream>
#include <range/v3/all.hpp>
using namespace std;
int main(int argc, char *argv[]) {
auto perfects = ranges::view::ints(1)
| ranges::view::filter([] (int x) {
int psum = 0;
for (int y = 1; y < x; ++y) {
if (x % y == 0) psum += y;
}
return x == psum;})
| ranges::view::take(3);
std::cout << "PERFECT NUMBERS:" << std::endl;
for (int z : perfects) {
std::cout << z << std::endl;
}
std::cout << "DONE." << std::endl;
}
代码以可能无限范围的数字 (ranges::view::ints(1)) 开头,但由于视图算法以 ranges::view::take(3) 结尾,它应该在找到通过过滤算法的前三个数字后停止(用于过滤的蛮力算法出完美的数字,故意不那么有效)。由于前三个完美数字 —— 6、28 和 496 —— 相当小,我希望这段代码能够快速找到它们,打印“DONE”。并终止。这正是发生的事情:
coliru -- taking 3 perfect numbers works just fine
但是,假设我想打印前 4 个完全数字,它们仍然很小——6、28、496 和 8128。打印 8128 后程序没有停止,最终不得不终止;大概是徒劳地试图计算第五个完美数,33550336,这超出了这种蛮力算法的有效查找能力。
coliru -- taking 4 perfect numbers tries to take 5+
这对我来说似乎不一致。如果两个测试都失败了,我会理解的(结论是我误解了 range-v3 视图算法的惰性评估),但是 take(3) 成功并停止而 take(4) 对我来说似乎不是一个错误,除非我理解错了。
我已经在 wandbox 上使用多个编译器进行了尝试,它似乎是持久的(尝试过 clang 6.0.1 和 7.0.0,g++ 8.1.0 和 8.2.0)。至少在我最初发现问题的本地计算机上,使用的是 range-v3 的 0.3.6 版本,但我不确定 coliru 和 wandbox。
【问题讨论】:
-
只有一条评论可以帮助人们避免徒劳无功的查询:如果你在 lambda 函数体中添加 "std::cout
-
另一个提示:找到不完美的数字也会做一些奇怪的事情。保留 cout 语句,将 lambda 的结果改为 !(x==psum),取前 8124 个非完美数(最后一个是 8127,以弥补不包括 6、28 或 426) ,在 for 循环停止之前,仍会针对 8128 和 8129 评估 lambda 函数。我猜这个视图需要一个结束迭代器,所以它会继续评估 lambda 函数,直到它返回“true”来获得一个结束迭代器。
-
确认上述内容的简单方法,使用更简单的 lambda 函数,对于 x = 米:wandbox link。 lambda 函数被清楚地调用,直到它返回 true,即使在前 n 次成功之后也是如此。现在的问题是为什么它会这样做。这不是我所期望的。它可能会导致意外行为(例如,如果 lambda 函数体不是纯函数体并更改捕获的对象)并且可能会对性能产生重大影响。
-
我还在 range-v3 的文档中看到一些 cmets 过滤器已被弃用;但只是想确认 remove_if 的行为方式相同。
-
请发布有关您问题的所有必需信息,不要链接到外部资源。
标签: c++ c++17 lazy-evaluation range-v3