gogoend / blog

blogs, ideas, etc.
MIT License
9 stars 2 forks source link

柯里化,什么鬼? #33

Open gogoend opened 4 years ago

gogoend commented 4 years ago

有点意思

函数柯里化,这个词有时经常听说,一直想看,但有时却总是想不起来,直接就忘了这茬;

今天想起来,就还是在这里记一下吧,万一有用呢~

参考资料

https://www.zhangxinxu.com/wordpress/2013/02/js-currying/

gogoend commented 4 years ago

一言难尽

搜罗了百度上关于柯里化的相关资料,无奈各种措辞过于高深(比如柯里化这三个字。。。)……我发现我已经晕了~~~这到底是什么玩意儿?

从一个简单的函数看起

下面这个函数用于输出某人的个人信息。

        // 基本函数
        function baseLogInfo(name,gender,age){
            console.log(`我叫${name},性别${gender},年龄${age}。`)
        }

经过调用后的结果:

image

在这里,函数接受了一堆参数,参数信息依次填入字符串模板对应的地方。当然,类似这样传值可能稍有一些麻烦,每次执行该函数都需要输入一大堆参数,参数没法复用。

因此,此处进行一些调整,每次传入一个参数,执行后都可以返回一个函数;如果所传入参数数量已经足够,那么就执行最终的函数。

        // 柯里化改写1
        function curryLogInfo1(name){
            return function(gender){
                return function(age){
                    console.log(`我叫${name},性别${gender},年龄${age}。`)
                }
            }
        }

经过调用后的结果:

image

我们在执行的时候不必传入全部参数,仅仅传入部分参数。

当传入部分参数时,返回一个函数,该函数用于接收接下来还未传入的其他参数。

当传入全部参数时,将会直接执行最终的函数。

实现任意函数的柯里化

上面的代码我们实现了logInfo这个函数的柯里化,这里我们来对任何一个函数实现柯里化。我们发现,柯里化后函数执行的过程似乎就是一个函数参数不断传入,直到有了足够参数后再执行真实函数的过程。 因此,接下来我们开始编写这样一个柯里化函数的函数。

函数接收一个待柯里化的函数(下面简称fn)作为函数。

function curry( fn ) {
    // ...
    return function temp(){
        // ...
    }
}

首先我们记录一下fn接收多少个参数,这里用fn.length即可得到。

function curry( fn ) {
    let len = fn.length
    return function temp(){
        // ...
    }
}

然后我们需要记录下我们向函数中传入过的所有的参数。正如上面所说,参数数足够多时,直接执行fn,否则继续收集参数。

function curry( fn ) {
    let len = fn.length
    return function temp(){
        let args = [ ...arguments ]
        if(args.length>=len){
            // 参数足够多,可以执行
            return fn( ...args )
        } else {
            // 参数不够,继续返回一个包含之前已接受到参数的函数,收集余下的参数
            return function(){
                // args:当前已接收过的参数
                // arguments:新传入的参数
                temp( ...args, ...arguments )
            }
        }
    }
}

在这里我们可以注意一下函数收集参数的方式。这里有些相当于函数的递归调用,args是包含当前temp函数已接收过的参数,参数不够时我们返回的函数中递归调用了temp函数,同时将已有参数以及新的参数传入,下次调用时即可在args里取到所有参数。