std 中是否有类似折叠的算法(或失败:boost)可用?

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

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

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”作为初始值)。

我宁愿使用执行上述操作但没有初始值的算法 'side-effect'(即,只需将向量中的数字相乘)。

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

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

所以你可以写:

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

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

(current draft) 237) accumulate is similar to the APL reduction operator and Common Lisp reduce function, but it avoids the difficulty of defining the result of reduction on an empty sequence by always requiring an initial value

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

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

可能也很难想出 两个 名称来反映已经不对称命名的 "accumulate" 和 "reduce"。


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,它们对集合执行折叠并隐式获取第一个值作为初始值。 是的,对于空列表这是没有意义的,因此对于这个问题你必须提供一个至少包含一个元素的列表。