在JavaScript中使用reduceRight的真实世界示例

Aad*_*hah 12 javascript iteration reduce haskell fold

不久前,我在StackOverflow上发布了一个问题,表明JavaScript中的本机实现reduceRight很烦人.因此,我创建了一个Haskell风格的foldr函数作为补救措施:

function foldr(array, callback, initial) {
    var length = array.length;

    if (arguments.length < 3) {
        if (length > 0) var result = array[--length];
        else throw new Error("Reduce of empty array with no initial value");
    } else var result = initial;

    while (length > 0) {
        var index = --length;
        result = callback(array[index], result, index, array);
    }

    return result;
}
Run Code Online (Sandbox Code Playgroud)

但是,我从未使用过这个foldr函数,因为我从不需要从右到左遍历数组.这让我思考,为什么foldr我不像在Haskell中那样使用JavaScript,以及foldr在JavaScript 中使用什么样的真实世界示例?

我可能是错的,但我相信该foldr函数在Haskell中被广泛使用,原因是:

  1. 懒惰评估(foldl是尾递归,那么foldr如何比foldl运行得更快?)
  2. 短切融合使用foldr/ build(捷径融合的正确性:foldr/build)

这可以解释为什么foldrreduceRight没有在JavaScript中广泛使用.我还没有看到现实世界foldr仅使用它从右到左的迭代顺序.

这让我想到了两个问题:

  1. reduceRight在JavaScript 中使用的一些真实世界示例是什么?也许您已经在npm包中使用过它.如果您可以将我链接到您的代码并解释为什么需要使用reduceRight而不是代码,那将会很棒reduce.
  2. 为什么reduceRight不像reduceJavaScript 一样广泛使用?我已就此事提供了两分钱.我认为这foldr主要仅用于它的懒惰,这就是为什么reduceRight在JavaScript中不是很有用.但是,我可能是错的.

对于第一个问题,我试图找到一些reduceRight在JavaScript中使用的真实世界示例.但是,我没有找到任何令人满意的答案.我发现的唯一例子是微不足道的和理论上的:

什么时候使用reduce和reduceRight?

我正在寻找的是一个实际的例子.什么时候reduceRight在JavaScript中使用而不是reduce

对于第二个问题,我理解这主要是基于意见的,这就是为什么如果你不回答它就没问题.这篇文章的主要焦点是第一个问题,而不是第二个问题.

Jam*_*xon 7

要回答您的第一个问题,reduceRight当您想要以从左到右的方式指定项目但以从右到左的方式执行时,它会非常方便.

考虑这个从左到右接受参数但从右到左读取和执行的组合函数的天真实现:

var compose = function () {
    var args = [].slice.call(arguments);

    return function (initial) {
        return args.reduceRight(function (prev, next) {
            return next(prev);
        }, initial);
    }
}
Run Code Online (Sandbox Code Playgroud)

不是通过调用reverse数组来占用时间/空间,而是更简单,更容易理解reduceRight调用.

使用此compose函数的示例如下所示:

var square = function (input) {
    return input * input;
};

var add5 = function (input) {
    return input + 5;
};

var log = function (input) {
    console.log(input);
};

var result = compose(log, square, add5)(1); // -> 36
Run Code Online (Sandbox Code Playgroud)

我确信还有更多reduceRight有用的技术示例,这只是一个.

  • @ [Jamie Dixon]你是对的,它是一个方便实用工具,代理Array.reverse (2认同)

Dan*_*ton 4

你是完全正确的,以至于我不完全确定这是一个真正的问题。懒惰和融合都是Haskell 受到青睐的重要原因。foldrJavaScript 的数组中不存在这两件事,因此实际上没有理由在现实世界的 JavaScript 中使用 reduceRight。我的意思是,您可以设计一种情况,通过将事物推到末尾来构造一个数组,然后您希望在累积结果时从最新到最旧的方式迭代它们。但在我看来,这是非常做作的。


只是为了说明 Haskell 的一面。请注意,在 Haskell 中,右折叠实际上并不执行从右到左的计算。您可以考虑从右到左对评估进行分组,但由于懒惰,这不是计算的内容。考虑:

foldr (\a _ -> Just a) undefined [1..]
Run Code Online (Sandbox Code Playgroud)

我已经undefined为累加器提供了一个起始值,以及要折叠的无限自然数列表。哦亲爱的。但这一点也不重要。这个表达式很高兴地计算为Just 1

从概念上讲,分组的工作方式如下:

let step a _ = Just a
let foldTheRest = foldr step undefined [2..]
step 1 foldTheRest
Run Code Online (Sandbox Code Playgroud)

考虑到分组,我们“折叠其余部分”,然后将该step函数应用于两个参数:“列表的第一个元素”和“折叠列表的其余部分得到的任何内容”。但是,由于步进函数甚至不需要累加器参数,因此永远不会评估这部分计算。评估这个特定折叠所需的只是“列表的第一个元素”。


重申一下,JavaScript 数组没有保留Haskell 所享有的任何好处foldr,因此实际上没有理由使用reduceRight. (相比之下,Haskell 有时有充分的理由使用严格的左折叠。)

注意:我不同意你的另一个问题,你得出的结论是“reduceRight 的本机实现是错误的”。我同意他们选择的论证顺序很烦人,但这并不是本质上错误的