标准中是否有类似折叠的算法(或失败:提升)可用?

Is there a fold like algorithm in std (or failing that: boost) available?

本文关键字:失败 提升 可用 算法 是否 折叠 标准      更新时间:2023-10-16

一个非常简单的例子是乘法 - 假设我有一个向量:

std::vector<int> ints = {1,2,3,4};

使用天真的方法,我可以使用std::accumulate(或std::reduce(,它看起来像这样:

int result = std::accumulate(ints.begin(), ints.end(), int{}, [](const int &a, const int &b){return a*b;});

但由于初始值为零 - 结果也变为零(对于这种特定情况,我可以修复它的一种方法是将"1"作为初始(。

我宁愿使用一种算法来执行上述操作,但没有初始值"副作用"(即只需将向量中的数字相乘(。

在字符串处理中经常遇到类似的问题,其中必须在元素之间插入分隔符。

您所说的可以重新构建为范围的最后N-1个元素的accumulate的概括,第一个元素是初始值。

所以你可以写:

std::accumulate(std::next(std::begin(ints)), std::end(ints), *std::begin(ints), OP);

但是,您必须假设ints是非空的,这提出了我的主要观点:当范围为空时,假设的标准函数应该返回什么?它的结果是否应该只是不确定的?这合理吗?

(当前草案(237)accumulate类似于 APL 归约运算符和 Common Lisp 归约函数,但它避免了在空序列上定义归约结果的困难,因为它总是要求一个初始值

积累回避了这个问题,并通过按它的方式做事提供了大量的灵活性。我认为这是一件好事。

结合能够简单地为您的整个范围内的操作提供适当的初始值(如1(,我不相信标准中需要这种假设的替代方案。

可能也很难为它想出两个名字来反映已经不对称命名的"积累"和"减少"。


template <class InputIt, class T, class BinaryOperation>
T fold_if_you_really_want_to(InputIt first, InputIt last, BinaryOperation op)
{
// UB if range is empty. Whatevs.
T init = *first;
return std::accumulate(++first, last, std::move(init), std::move(op));
}

无论如何,或者类似的东西。请注意,这必然复制第一个元素;如果你不像我一样懒惰,你可以通过打电话给std::accumulate来避免这种情况。

除了@Lightness Races in Orbit的答案之外,考虑一下Haskell的情况: 对于像你描述的情况(最突出的是搜索列表中的最大元素(,Haskell提供了函数foldl1foldr1,它们在集合上执行折叠,并隐式地将第一个值作为初始值。 是的,对于空列表,这毫无意义,因此对于此问题,您必须提供一个至少包含一个元素的列表。