【问题标题】:How do I build a contains function using reduce instead of a for loop in JavaScript?如何在 JavaScript 中使用 reduce 而不是 for 循环构建包含函数?
【发布时间】:2016-01-19 12:33:34
【问题描述】:

我想这是两个问题。我仍然无法使用 reduce 方法,我得到了使用它的简单方法

reduce([1,2,3], function(a, b) { return a + b; }, 0); //6

将它与数字以外的任何东西一起使用真的让我感到困惑。那么如何使用 reduce 代替 for 循环来构建 contains 函数呢?评论将不胜感激。谢谢大家。

function contains(collection, target) {
  for(var i=0; i < collection.length; i++){
    if(collection[i] === target){
      return true;
    }
  }
  return false;
}
contains([1, 2, 3, 4, 5], 4);
//true

【问题讨论】:

  • 我不认为你可以用 Array.prototype.reduce 做到这一点,但你可以编写自己的 reduce 函数来理解 reduced 值(终止迭代)。
  • 嗯,Array.indexOf ?
  • 为什么要使用 reduce 来包含?
  • 实际上 reduce() 是有道理的,因为您想将许多转换为一个。
  • 几年后你可以使用Array.includes,注意MDN上有一个polyfill。

标签: javascript for-loop functional-programming reduce


【解决方案1】:

我如何使用 YX

通常,我以相同的方式处理所有这些问题:编程语言并不意味着是魔杖。如果您的语言没有内置功能或行为,您应该能够自己编写它。从那里开始,如果您后来了解到您的语言确实为这种行为提供了内置功能(或已添加),那么您可以根据需要重构您的代码。但无论如何,不​​要坐在那里等待魔杖挥动,等待你的代码神奇地工作。

可以根据需要使用Array.prototype.reduce,但考虑到它的工作方式,它总是会遍历数组的全部内容——即使在第一个元素中找到匹配项。所以这意味着你不应该在你的函数中使用Array.prototype.reduce

但是,您可以使用reduce 解决方案如果您编写了支持提前退出的reduce。下面是reducek,它将延续传递给回调。应用延续将继续减少,但返回一个值将执行提前退出。听起来和医生的命令完全一样……

此答案旨在配合 LUH3417 的答案向您展示,在您可能知道 Array.prototype.some 之前,您不应该坐等 ECMAScript 实现您需要的行为。此答案表明您可以使用缩减过程并且仍然具有提前退出行为。

const reducek = f=> y=> ([x,...xs])=>
  x === undefined ? y : f (y) (x) (y=> reducek (f) (y) (xs))

const contains = x=>
  reducek (b=> y=> k=> y === x ? true : k(b)) (false)

console.log(contains (4) ([1,2,3,4,5])) // true
console.log(contains (4) ([1,2,3,5]))   // false
console.log(contains (4) ([]))          // false

看到这里的reducek 和示例contains 函数,应该有点明显contains 可以泛化,这正是Array.prototype.some 的含义。

同样,编程并不是魔法,所以我将向您展示如果 Array.prototype.some 尚不存在,您将如何做到这一点。

const reducek = f=> y=> ([x,...xs])=>
  x === undefined ? y : f (y) (x) (y=> reducek (f) (y) (xs))

const some = f=>
  reducek (b=> x=> k=> f(x) ? true : k(b)) (false)

const contains = x=> some (y=> y === x)

console.log(contains (4) ([1,2,3,4,5])) // true
console.log(contains (4) ([1,2,3,5]))   // false
console.log(contains (4) ([]))          // false

【讨论】:

  • 我认为你不应该将y传递给f,并且延续不应该带参数
  • @Bergi,我很感兴趣 ^_^ 您能否链接一个与您给出的建议相符的代码粘贴?
  • 只需const reducek = f =&gt; y =&gt; xs =&gt; xs.length == 0 ? y : f(xs[0])(() =&gt; reducek(f)(y)(xs.slice(1))const contains = x =&gt; reducek (y =&gt; k =&gt; y === x || k())(false)
  • @LUH3417:是的,明确处理惰性对于提前退出是必要的,但请注意,您的示例也不适用于 naomik 的原始 reducek
  • 我打算在你想控制懒惰时专门使用reducek。 LUH3417 的 sum 示例可以使用 reducek (y=&gt; x=&gt; k=&gt; k (x + y)) (0) ([1,2,3]) //=&gt; 6,但在这种情况下使用 reducek 没有意义。相反,非惰性减少,reduce 会更合适。 const reduce = f=&gt; reducek (y=&gt; x=&gt; k=&gt; k (f (y) (x))) 其中reduce (y=&gt; x=&gt; x + y) (0) ([1,2,3]) //=&gt; 6 可以在没有任何提前退出能力的情况下工作。
【解决方案2】:

这是一个 ES2015 解决方案:

    const contains = (x, xs) => xs.some(y => x === y);
    let collection = [1,2,3,4,5];

    console.log(contains(4, collection)); // true;

Array.prototype.someArray.prototype.reduce 相比的最大优势在于前者在条件为true 时立即退出迭代,而后者总是遍历整个数组。这意味着contains(4, xs)xs 的第四个元素停止它的迭代。

【讨论】:

  • 好吧,OP 专门要求 reduce 解决方案...除此之外,您当然是对的
  • @le_m 你是对的。但有时我们需要做出与 OP 预期不同的反应——即使这意味着我们不会获得声誉:D
  • @LUH3417 我同意 100%
【解决方案3】:

这是你需要的:

function contains(collection, target) {
    return collection.reduce( function(acc, elem) {
       return acc || elem == target;
    }, false)
};

正如 adaneo 所说,对于这个特定问题可能有一种更简单的方法,但是您标记了这个“函数式编程”,所以我想您想在这种解决问题的方式上做得更好,我完全赞同。

【讨论】:

  • 您可以将目标绑定到 this 以终止包装器/闭包,在这种情况下不要忘记添加“use strict”。
  • 更简单的方法是collection.some(function(elem) { return elem == target; }),我们甚至可以使用reduce实现some
  • @FreddieCabrera 太好了,你能把问题标记为完成吗
  • 当然。 Reduce 采用带有起始值的 accumulator - 在这种情况下为 false - 并将其与列表中的每个 element 结合起来。结合这两者的函数为下一次迭代返回一个新值acc。最后将累加器的最终值返回给contains
  • 我猜你看到布尔逻辑开始为假并一直保持这种状态,直到elem == target acc 变为真并保持这种状态
猜你喜欢
  • 2021-04-20
  • 2018-02-22
  • 2022-11-12
  • 2020-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多