编写分段函数子函数_我从编写六个函数中学到的全部都做相同的事情

编写分段函数子函数

A couple weeks ago, a camper started an unofficial algorithm competition on Free Code Camp’s Forum.

几周前,一名露营者在Free Code Camp的论坛上发起了一项非正式的算法竞赛。

The challenge seemed simple enough: return the sum of all multiples of 3 or 5 that are below a number N, where N is an input parameter to the function.

这个挑战似乎很简单:返回小于或等于数字N的3或5的所有倍数之和,其中N是函数的输入参数。

But instead of just finding any solution, P1xt’s competition required you to focus on efficiency. It encouraged you to write your own tests, and to benchmark the performance of your solutions.

但是P1xt的竞争不仅仅是寻找任何解决方案, 而是要求您专注于效率。 它鼓励您编写自己的测试,并基准测试解决方案的性能。

This is a breakdown of every function I tried and tested, including my tests and benchmark scripts. At the end, I’ll show the function that blew all of my own out of the water, and taught me a valuable lesson.

这是我尝试和测试的每个功能的细分,包括我的测试和基准脚本。 最后,我将展示该功能使我自己的所有功能如鱼得水,并且教了我一个宝贵的教训。

功能1:数组,推入,递增 (Function 1: Array, push, increment)

function arrayPushAndIncrement(n) {
	var array = [];
    var result = 0;
    for (var i = 1; i < n; i ++) {
        if (i % 3 == 0 || i % 5 == 0) {
            array.push(i);
        }
    }
    for (var num of array) {
        result += num;
    }
    return result;
}

module.exports = arrayPushAndIncrement; // this is necessary for testing

For problems like this, my brain defaults to: build an array, then do something to that array.

对于这样的问题,我的大脑默认为:建立一个数组,然后对该数组执行操作。

This function creates an array and pushes any numbers that meet our condition (divisible by 3 or 5) into it. It then loops through that array, adding all the values together.

此函数创建一个数组,并将满足我们条件(被3或5整除)的任何数字压入其中。 然后,它遍历该数组,将所有值加在一起。

设置测试 (Setting up testing)

Here are the automated tests for this function, which use Mocha and Chai, running on NodeJS.

这是针对此功能的自动化测试,这些测试使用在NodeJS上运行的Mocha和Chai。

If you want more information about installing Mocha and Chai, I’ve written a detailed guide on Free Code Camp’s forum.

如果您想了解有关安装Mocha和Chai的更多信息,我已经在Free Code Camp的论坛上写了详细的指南

I wrote a simple testing script using the values P1xt provided. Notice that in the script below, the function is included as a module:

我使用提供的值P1xt编写了一个简单的测试脚本。 请注意,在下面的脚本中,该函数作为模块包含在内:

// testMult.js

var should = require( 'chai' ).should();
var arrayPushAndIncrement = require( './arrayPushAndIncrement' );

describe('arrayPushAndIncrement', function() {
    it('should return 23 when passed 10', function() {
    	arrayPushAndIncrement(10).should.equal(23);
    })
    it('should return 78 when passed 20', function() {
    	arrayPushAndIncrement(20).should.equal(78);
    })
    it('should return 2318 when passed 100', function() {
    	arrayPushAndIncrement(100).should.equal(2318);
    })
    it('should return 23331668 when passed 10000', function() {
    	arrayPushAndIncrement(10000).should.equal(23331668);
    })
    it('should return 486804150 when passed 45678', function() {
    	arrayPushAndIncrement(45678).should.equal(486804150);
    })
})

When I ran the test using mocha testMult.js it returned the following:

当我使用mocha testMult.js运行测试时,它返回以下内容:

For all future functions in this article, assume they passed all tests. For your own code, add tests for each new function you try.

对于本文中所有将来的功能,假定它们已通过所有测试。 对于您自己的代码,为尝试的每个新功能添加测试。

功能2:数组,推入,减少 (Function 2: Array, push, reduce)

function arrayPushAndReduce(n) {
	var array = [];
    for (var i = 1; i < n; i ++) {
    	if (i % 3 == 0 || i % 5 == 0) {
        	array.push(i);
        }
	}  
    return array.reduce(function(prev, current) {
    	return prev + current;
    });
}

module.exports = arrayPushAndReduce;

So this function uses a similar approach to my previous one, but instead of using a for loop to construct the final sum, it uses the fancier reduce method.

因此,此函数使用了与我以前的方法类似的方法,但是它没有使用for循环来构造最终的总和,而是使用了比较好的reduce方法。

设置性能基准测试 (Setting up performance benchmark testing)

Now that we have two functions, we can compare their efficiency. Again, thanks to P1xt for providing this script in a previous forum thread.

现在我们有了两个功能,我们可以比较它们的效率了。 再次感谢P1xt在先前的论坛线程中提供了此脚本。

// performance.js

var Benchmark = require( 'benchmark' );
var suite = new Benchmark.Suite;

var arrayPushAndIncrement = require( './arrayPushAndIncrement' );
var arrayPushAndReduce = require( './arrayPushAndReduce' );

// add tests
suite.add( 'arrayPushAndIncrement', function() {
		arrayPushAndIncrement(45678)
    })
    .add( 'arrayPushAndReduce', function() {
    	arrayPushAndReduce(45678)
    })
    // add listeners
    .on( 'cycle', function( event ) {
    	console.log( String( event.target ));
    })
    .on( 'complete', function() {
    	console.log( `Fastest is ${this.filter( 'fastest' ).map( 'name' )}`);
    })
    // run async
    .run({ 'async': true });

If you run this with node performance.js you’ll see the following terminal output:

如果使用node performance.js运行此命令,则将看到以下终端输出:

arrayPushAndIncrement x 270 ops/sec ±1.18% (81 runs sampled)
arrayPushAndReduce x 1,524 ops/sec ±0.79% (89 runs sampled)
Fastest is arrayPushAndReduce

So using the reduce method gave us a function that was 5 times faster!

因此,使用reduce方法可以使我们的功能快5倍

If that isn’t encouraging enough to continue with more functions and testing, I don’t know what is!

如果这样做不足以继续进行更多功能和测试,那么我不知道这是什么!

功能3:While,数组,Reduce (Function3: While, Array, Reduce)

Now since I always reach for the trusty for loop, I figured I would test a while loop alternative:

现在,由于我一直都喜欢可信赖的for循环,因此我认为我可以测试while循环替代方法:

function whileLoopArrayReduce(n) {
    var array = [];
    while (n >= 1) {
    	n--;
        if (n%3==0||n%5==0) {
        	array.push(n);
        }  
    }  
    return array.reduce(function(prev, current) { 
    	return prev + current;
    });
}

module.exports = whileLoopArrayReduce;

And the result? A tiny bit slower:

结果呢? 慢一点:

whileLoopArrayReduce x 1,504 ops/sec ±0.65% (88 runs sampled)

功能4:同时,总和,无数组 (Function4: While, sum, no arrays)

So, finding that the type of loop didn’t make a huge difference, I wondered what would happen if I used a method that avoided arrays altogether:

因此,发现循环的类型没有太大变化,我想知道如果我使用一种完全避免数组的方法会发生什么:

function whileSum(n) {
    var sum = 0;
    while (n >= 1) {
        n--;
        if (n%3==0||n%5==0) {
            sum += n;
        }
    }  
    return sum;
}

module.exports = whileSum;

As soon as I started thinking down this track, it made me realize how wrong I was for always reaching for arrays first…

当我开始思考这条路时,它使我意识到总是首先接触阵列是多么错误……

whileSum x 7,311 ops/sec ±1.26% (91 runs sampled)

Another massive improvement: nearly 5 times faster again, and 27 times faster than my original function!

另一个重大改进:再次了将近5倍 ,比我原来的功能27倍

功能#5:求和 (Function#5: For, sum)

Of course, we already know that a for loop should be a little faster:

当然,我们已经知道for循环应该快一点:

function forSum(n) {
    n = n-1;
    var sum = 0;
    for (n; n >= 1 ;n--) {
        (n%3==0||n%5==0) ? sum += n : null;
    }  
    return sum;
}

This uses the ternary operator to do the condition checking, but my testing showed that a non-ternary version of this is the same, performance-wise.

这使用三元运算符进行条件检查,但是我的测试表明,此性能的非三元版本是相同的。

forSum x 8,256 ops/sec ±0.24% (91 runs sampled)

So, a little faster again.

因此,再快一点。

My final function ended up being 28 times faster than my original.

我的最终功能最终比原来28倍

I felt like a champion.

我觉得自己像个冠军。

I was over the moon.

我在月球上。

I rested on my laurels.

我桂冠安息。

输入数学 (Enter Maths)

The week passed and the final solutions from everyone were posted, tested, and collated. The function that performed the fastest avoided loops altogether and used an algebraic formula to crunch the numbers:

一周过去了,每个人的最终解决方案都已发布,测试和整理。 执行最快避免循环的函数总共使用了代数公式来对数字进行运算:

function multSilgarth(N) {
    var threes = Math.floor(--N / 3);  
    var fives = Math.floor(N / 5);
    var fifteen = Math.floor(N / 15);
    return (3 * threes * (threes + 1) + 5 * fives * (fives + 1) - 15 * fifteen * (fifteen + 1)) / 2;
}

module.exports = multSilgarth;

Wait for it…

等等...

arrayPushAndIncrement x 279 ops/sec ±0.80% (83 runs sampled)
forSum x 8,256 ops/sec ±0.24% (91 runs sampled)
maths x 79,998,859 ops/sec ±0.81% (88 runs sampled)
Fastest is maths

最快的是数学 (Fastest is maths)

So the winning function was roughly 9,690 times faster than my best effort, and 275,858 times faster than my initial effort.

因此,获胜功能比我的最大努力了约9,690倍 ,比我最初的努力275,858倍

If you need me, I’ll be over at Khan Academy studying math.

如果您需要我,我将在可汗学院学习数学。

Thanks to everyone that participated and shared their solutions in the spirit of helping other campers learn new methods.

感谢所有参加并分享其解决方案的人本着帮助其他营员学习新方法的精神。

If you’re curious, here is P1xt’s write up of the competition, and all of the testing and benchmark data:

如果您感到好奇,这是P1xt的比赛记录,以及所有测试和基准数据:

P1xt/algo-oct-17algo-oct-17 - JavaScript Algorithm Challenge - October 9 through 16github.com

P1xt / algo-oct-17 algo-oct-17-JavaScript算法挑战-10月9日至16日 github.com

翻译自: https://www.freecodecamp.org/news/what-i-learned-from-writing-six-functions-that-all-did-the-same-thing-b38fd48f0d55/

编写分段函数子函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值