【问题标题】:Nesting ES6 array helper methods to generate an array of prime numbers嵌套 ES6 数组辅助方法以生成素数数组
【发布时间】:2017-05-16 17:02:09
【问题描述】:

所以我想编写一个函数,它返回所有素数之和,包括提供的数字。

我写了这个,它有效:

function sumPrimes(num) {
  const arr = Array.from({length: num+1}, (v, k) => k).slice(2);
  return arr.filter(element => {
  	for(let i = 2; i < element; i++) {
          if(element % i === 0) {
              return false;
          }
      }
      return element;
  }).reduce((previous, current) => {
  	return previous += current;
  }, 0);
}

sumPrimes(9);

我认为如果将 for 循环替换为另一个数组辅助方法,它会看起来更整洁。然而,我正在努力实现这一点。

这是我目前得到的:

function sumPrimes(num) {
  const arr = Array.from({length: num+1}, (v, k) => k).slice(2);
  return arr.filter(element => {
  	return arr.find(ref => {
      console.log("(" + element + " % " + ref + " === 0) " + (element % ref === 0));
    	if(element % ref === 0) { return false; }
      return true;
    });
  }).reduce((previous, current) => {
  	return previous += current;
  }, 0);
}

sumPrimes(20);

这样写,函数不再按预期工作——它不过滤任何数字,所以所有数字都由 .reduce 帮助器求和。控制台使它看起来好像 if 语句仍在按预期工作;我做错了什么?

【问题讨论】:

  • 不是你问的,但是,for(let i = 2; i &lt; element; i++) { 真的应该是for(let i = 2; i*i &lt;= element; i++) {
  • sieve of Sundaram 实现为您的助手会更快。 Here 是一个 JS 代码。
  • 感谢@WillNess!我认为没有必要担心平方根,但现在想想你说的,我可以看到它如何使函数更高效

标签: javascript arrays ecmascript-6 primes helpers


【解决方案1】:

您可以在 sqrt(n) 处缩小 n 的素性研究范围:

var isPrime = n => n===2 ? true : Array(Math.ceil(Math.sqrt(n))+1).fill().map((e,i)=>i).slice(2).every(m => n%m);
 
var sumPrimes = num => Array(num).fill().map((e,i)=>i+1).slice(1).filter(isPrime).reduce((a,b) => a+b);

console.log(sumPrimes(9));

【讨论】:

  • 9 以内的素数之和应为 17:(2+3+5+7)
  • @nem035 我刚刚修复了这个错误。感谢测试
【解决方案2】:

您的代码无法使用find 工作的原因是find 不是您的for 循环的适当替代品。您在此处拥有的 for 循环返回一个布尔值,指示是否找到除数。另一方面,find 返回除数本身。这意味着您的 filter 方法的所有条件都是大于 1 的数字,它们都评估为真,因此没有任何内容被过滤。

更适合您的用例的方法是someevery。 这些基本上像find 一样工作,只是在找到满足条件的元素后立即返回一个布尔值。

  • some 停止并在谓词函数为某个元素返回 true 时立即返回 true。 否则返回false

  • every 停止并在谓词函数为某些元素返回 false 时立即返回 false。 否则返回true

还有一个问题是,使用这样的帮助程序会降低代码效率,因为您现在检查的是所有数字,而不仅仅是当前数字。这意味着您的谓词函数也必须包含此相等性检查,否则您必须首先过滤数组以查找被检查元素下方的所有元素。

效率方面的另一个小改进是您不需要一直迭代到element - 1 来查找除数。迭代到sqrt(element) 就足够了,因为所有高于sqrt(element) 的除数元素都将在sqrt(element) 下方的某处有一个补数除数。

这是一种使用every 并过滤低于被检查元素平方根的元素的方法。

function sumPrimes(num) {
  const arr = Array.from({length: num+1}, (v, k) => k).slice(2);
  return arr.filter(element => {
    return arr
      .filter(ref => ref*ref <= element)  // filter elements less than sqrt(current)
      .every(ref => element % ref !== 0); // filter elements that have a divisor
  }).reduce((previous, current) => {
    return previous += current;
  }, 0);
}

console.log(sumPrimes(9)); // 17

也许一种功能较少但更有效(恕我直言同样干净)的方法是将您的 for 循环转换为辅助函数:

function isPrime(element) {
  for(let i = 2; i*i <= element; i++) {
    if(element % i === 0) {
      return false;
    }
  }
  return true;
}

function sumPrimes(num) {
  return Array
    .from({ length: num+1 }, (v, k) => k)
    .slice(2)
    .filter(isPrime)
    .reduce((previous, current) => previous + current, 0);
}

console.log(sumPrimes(9)); // 17

【讨论】:

  • 没问题,很高兴能帮上忙
  • 谢谢!这真的很有帮助。
  • 所以第二个 .filter 是必要的,因为否则 .every 函数将简单地循环遍历数组中的所有元素,超出父过滤器函数的当前元素。是的,我确实发现您提出的第二个解决方案实际上更容易阅读,谢谢!我有一种感觉,我可能过于复杂......
  • 是的,先生。如果您不想要第二个过滤器,则必须使用 if 语句或类似语句处理等于或大于 current 的元素,但这会将检查可除性的逻辑与检查允许的值范围混淆。最后一个例子当然更好。
猜你喜欢
  • 2018-05-08
  • 2016-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多