javascript - array.reduce 通过递归实现

标签 javascript algorithm

我正在尝试通过递归实现 Array.reduce() 方法,它对我有用

var index = 0;
function reduce(arr, fn, initial) {
    if (index >= arr.length) {
        return initial;
    }
    if (!initial) {
        initial = arr[index];
        index++;
    }
    initial = fn(initial, arr[index], index, arr);
    index++;
    return reduce(arr, fn, initial);
}

但验证者不接受我的回答。我做错了什么?

最佳答案

一些问题:

  • 您定义了index作为一个永远不会重置为 0 的全局变量,所以如果 reduce被多次调用,它将具有错误的值。对此有几种解决方案。我建议使用 this跟踪您所在位置的值(value)。
  • initial参数是可选的,但您的代码使用 !initial 对此进行测试这还不够好:如果 initial 的值是 0 或任何其他虚假值,不应将其视为不存在。使用arguments.length相反。
  • 当您增加索引时,因为 initial未提供参数,您不会检查该索引是否变得太大。绝不应该使用无效索引来调用回调函数。交换两个if声明。
  • 当传递一个没有初始值的空数组时,reduce返回undefined ,但它应该抛出一个错误,就像 native reduce 的情况一样方法。
  • 当数组有“间隙”(即它是稀疏数组)时,该函数仍然对那些缺失的索引调用回调函数。相反,不应迭代那些不存在的条目。您可以使用数组迭代器来完成此操作,该迭代器仅产生有效的索引。

以下是处理这些问题的方法:

function reduce(arr, fn, initial) {
    // Get iterator for the indexes from `this`. 
    // The first time it will not be an iterator, so get it from arr.keys().
    const iter = Object(this).next ? this : arr.keys(); 
    let item = iter.next();
    if (arguments.length < 3) { // Solid check for presence of initial argument
        // Throw error when there's nothing to reduce
        if (item.done) {
            throw new TypeError("reduce of empty array with no initial value"); 
        }
        initial = arr[item.value];
        item = iter.next();
    }
    // Check end of array only after having dealt with missing initial value
    if (item.done) return initial;
    initial = fn(initial, arr[item.value], item.value, arr);
    return reduce.call(iter, arr, fn, initial); // Pass on the index via `this`
}

// Some test cases
console.log(reduce([1, 2, 3], (a, b) => a+b, 0), "should be 6");
console.log(reduce([1, 2, 3], (a, b) => a+b, 0), "should be 6");
console.log(reduce([1, 2, 3], (a, b) => a+b), "should be 6");
console.log(reduce([1], (a, b) => a+b), "should be 1");
console.log(reduce([1], (a, b) => a+b, 2), "should be 3");
console.log(reduce([], (a, b) => a+b, 1), "should be 1");
try {
    console.log(reduce([], (a, b) => a+b), "error");
} catch(e) {
    console.log("Error was raised as expected");
}

this引用是函数私有(private)的,调用者无法看到,因此使用它来传播当前状态(迭代器)似乎是理想的选择。替代方案是:

  • 使用额外参数
  • 向数组或回调函数参数添加属性。

最后一个选项的缺点是调用者可以检测到此突变。在调用者卡住这些对象的极端情况下,代码甚至会因异常而失败。

所有解决方案都有一个缺陷:原始调用者可以通过初始调用传递状态。因此,对于每个解决方案,调用者可以传递索引(或迭代器)的特定值,如下所示:

  • 传递一个带有值的额外参数 - 比方说 5。
  • 设置特殊index传递给 reduce 的函数的属性值为 5。
  • 调用 reducecallapply ,为其提供特定的this值(迭代器)。

对此我们无能为力,因为要求是使用递归。由于递归要求,该函数在递归调用中无法执行原始调用者无法执行的操作。

关于javascript - array.reduce 通过递归实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50582420/

相关文章:

javascript - %2F 而不是 url 中的斜杠?

javascript - 在两个现有元素之间放置并适配 n 个元素

javascript - 如何使用动态长度链接 Promise 函数?

javascript - AngularJS Controller 在注入(inject)工厂时抛出错误

c++ - 链表中的返回遍历问题

algorithm - 在不泄露来源的情况下比较 secret 数据

algorithm - 将增量对转换为度数

javascript - 如何获取 <td> 标签中的值

algorithm - 我如何计算 "true"仍然是多少?加权投票与他们发生的时间?

java - java中的MD5算法解密