这是使用 ES6 最有效地查找无循环因子的方法吗?

Is this the most efficient use of ES6 to find factors without a loop?

我正在尝试找到最不冗长的方法来查找数组中每个数字的因数而不使用循环。我有一段 ES6 代码,我可以在 .map 中使用它来避免我认为的循环,但我不知道它在第二行中做了什么。

我查看了 MDN 上的 .filter 和 .from 方法,所以我们只是从一个可迭代对象中浅层复制一个数组实例,通过调用 Array() 看似空的,但后来我不知所措之后用英文描述,让我感到不安。

let evens = [2,4,6,80,24,36];

这是我尝试 deconstruct/explain 英文版的 ES6 片段

const factor = number => Array
    .from(Array(number), (_, i) => i)
    .filter(i => number % i === 0)

所以我把它放到这个 .map 中,就像这样

const factors = evens.map((number => {
    return factors(number)
}))

console.log(factors)

我得到了一组因子数组,如下所示

[ [ 1 ],
  [ 1, 2 ],
  [ 1, 2, 3 ],
  [ 1, 2, 4, 5, 8, 10, 16, 20, 40 ],
  [ 1, 2, 3, 4, 6, 8, 12 ],
  [ 1, 2, 3, 4, 6, 9, 12, 18 ] ]

所以...它有效,但是第二行发生了什么?我喜欢它的简洁,但是当我尝试将其逆向工程为非 ES6 时,我感到很疑惑。

提前谢谢你,高级 ES6 人。

Array(number) 创建一个空数组,长度为 number。现在因为它完全是空的,长度还不是真正有用的......如果你在它上面调用 Array.from,它会遍历所有索引(直到 number)然后调用传递的回调并构建使用 return 值创建一个新数组。 (_, i) => i 取前一个值的索引(一直是 undefined)并将其 return 作为值。因此你得到以下结果:

 number | Array.from
 0            | []
 1            | [0]
 2            | [0, 1]
 5            | [0, 1, 2, 3, 4]

如您所见,这会生成从 0 到 number 所有数字 。现在你只需要过滤掉那些均匀划分 number 的那些,这可以通过再次检查模运算的结果是否为零来轻松完成:

  1 % 2 -> 1
  2 % 2 -> 0
  3 % 2 -> 1
  4 % 2 -> 0

我认为更有效的方法是使用生成器。

一个好处是我认为代码更容易理解。

let evens = [2,4,6,80,24,36];

function* factor(n) {
  for (let l = 1; l < n; l += 1) {
    if (n % l === 0) yield l;
  }
}

const factors = evens.map((number => {
  return Array.from(factor(number)).join(", ");
}));

console.log(factors);

这里有很多东西要打开。

首先,"without using loops."你能解释一下你提出这个要求的原因吗?并不是说我没有同情心,因为我通常会避免显式循环,但您真的应该能够解释 为什么 您想要这样做。处理有序集合有两种根本不同的方法:迭代循环和递归。如果您不使用递归代码,则可能某处隐藏了一个循环。它可能被埋在 mapfilter 等内部,这通常是一种改进,但该循环仍然存在。

其次,此代码段的布局相当具有误导性:

const factor = number => Array
    .from(Array(number), (_, i) => i)
    .filter(i => number % i === 0)

通常当多行开始时 .methodName(...) 这些方法中的每一个都对前一行提供的数据进行操作。但是这里的from只是Array的静态方法;像这样将它们分开是令人困惑的。这些中的任何一个都会更好,许多其他布局也是如此:

const factor = number => 
    Array.from(Array(number), (_, i) => i)
    .filter(i => number % i === 0)
const factor = number => Array.from(
    Array(number), 
    (_, i) => i
).filter(i => number % i === 0)

第三,正如评论和另一个答案所指出的,Array.from 接受一个可迭代函数和一个映射函数,returns 一个数组,而 Array(number) 会给你一个没有值,但报告其长度为 number,因此将用作适当的可迭代对象。有许多等效的写法,例如:

Array.from({length: number}, (_, i) => i)
[...Array(number)].map((_, i) => i)

第四,你提这个:

const factors = evens.map((number => {
    return factor(number)
}))

(错别字已修复)

虽然这并没有什么错,但您可能想要认识到,这可以写得更干净,如

const factors = evens.map(factor)

最后,该分解代码缺少主要的性能调整。当您真的可以成对找到因子时,您测试每个可能的值直到 n,最多只测试 sqrt(n)。这是一个主要的区别。没有已知的有效分解技术,这可能是一件好事,因为现代加密取决于这是一个难题。但您很可能不想让它变得更糟。