ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

当我们谈Currying时,我们在谈些什么

2021-10-22 11:34:03  阅读:216  来源: 互联网

标签:function 谈些 return 函数 add Currying Array 我们 fn


在数学中,函数是集合到集合的映射,即集合 A 中的元素,经过处理后,映射到集合 B 中的元素。可以简单表示如下:

f(A)->B

函数式编程,顾名思义,就是用函数的方式去编程。关于函数式编程的起源可以追溯到20世纪初的λ演算。在λ演算中,函数只有一个参数,为了实现多参数,于是出现了函数的柯里化(currying),本质上是单参数函数的语法糖。

所谓柯里化,可以理解为把多参数的函数,转换成单参数的函数链,简单表示如下:

f(X1, X2, …, Xn)->->g(X1)(X2)…(Xn)

在前端日常开发中柯里化并不常用,但在面试中时常会被问到,个人猜测,最初问这个问题的人,也许是为了考查面试者对函数式编程的理解。

这里我们以一道经典的面试题入手:

请实现加法运算函数add(),使其满足:

add(1, 2, 3); // 6
add(1, 2)(3); // 6
add(1)(2)(3); // 6

首先,要实现链式调用,add的返回值毫无疑问也是个函数;其次,因为这里连续调用的都是同样的加法运算,很容易想到调用自身的递归。

先来实现个简单版本的:

let sum = 0;
function add() {
    sum += Array.from(arguments).reduce((x, y) => x + y);
    add.toString = () => sum; // console.log时实际调用的是Function.toString()
    return add;
}

这样写当然没毛病,但是在调用的时候,每次都需要手动将sum归零,否则sum就会被之前的计算污染。

我们很容易想到利用闭包来解决这个问题,实现纯函数版本的add()

function add() {
    // 第一次调用时,就要对参数求和并保存到total中
    let total = Array.from(arguments).reduce((x, y) => x + y);
    // 利用闭包特性,保存total
    const _add = function () { // 注意此处不能写成箭头函数
        total += Array.from(arguments).reduce((x, y) => x + y);
        return _add;
    }
    _add.toString = () => total;
    return _add;
}

这里额外提一句,不要手里拿着锤子就到处找东西锤:

柯里化函数可用于任何支持闭包的编程语言;然而,出于效率原因,通常首选非柯里化函数,因为大多数函数调用可以避免部分应用程序和闭包创建的开销。
—— 维基百科

上面的 add() 函数还不够优雅,你看 reduce 重复调了两次,实际上我们可以把所有参数都先收集起来,最后调用一次就行:

function add() {
    // 把参数保存到一个数组里
    const arr = Array.from(arguments);
    // 利用闭包特性,保存total
    const _add = function () { // 注意此处不能写成箭头函数
        arr.push(...Array.from(arguments));
        return _add;
    }
    _add.toString = () => Array.from(arr).reduce((x, y) => x + y);
    return _add;
}

让我们再抽象一层,把普通函数转换成柯里化函数的函数:

function fnToCurry(fn) {
    const curry = function () {
        // 第一次调用时,就把参数保存到数组里
        const arr = Array.from(arguments);
        // 利用闭包特性,保存total
        const _fn = function () { // 注意此处不能写成箭头函数
            arr.push(...Array.from(arguments));
            return _fn;
        }
        _fn.toString = () => fn(...arr);
        return _fn;
    }
    return curry; // 最后返回柯里化后的函数
}

用法示例:

function f1() {
    return Array.from(arguments).reduce((x, y) => x + y);
}
// 将f1柯里化
const f2 = fnToCurry(f1);
console.log(f2(1, 2, 3)); // 6
console.log(f2(1)(2)(3)); // 6

上面这个柯里化后的函数会在调用时就立即执行,如果我们希望柯里化后的函数不立即执行,比如,只有当我们传入的参数包含 "exec" 的时候才执行,那么我们可以这样写:

function fnToDelayCurry(fn) {
    const curry = function () {
        const arr = Array.from(arguments)
        // 利用闭包特性,保存total
        const _fn = function () { // 注意此处不能写成箭头函数
            // 当输入的参数中有exec标志时,立即执行
            // 主要就是在这里去对传入的参数作判断,来判断是否执行
            // 这里包含exec的参数不会被保存到arr中,若要保存exec,则把else中的push挪到外层即可
            if (Array.from(arguments).indexOf("exec") > -1) {
                _fn.toString = () => fn(...arr);
            } else {
                _fn.toString = Function.toString;
                arr.push(...Array.from(arguments));
            }
            return _fn;
        }
        return _fn;
    }
    return curry;
}

用法示例:

function delayFn() {
    const args = Array.from(arguments);
    return args.join("~");
}
console.log(fnToDelayCurry(delayFn)(1)(2)(3)(4, 5)); // 这里打印的是函数_fn
console.log(fnToDelayCurry(delayFn)(1)(2)(3)(4, 5)("exec")); // 1~2~3~4~5

以上Demo代码都在这里:https://jsrun.net/ciUKp/edit

参考资料:

  1. 柯里化[wiki]:https://en.wikipedia.org/wiki/Currying
  2. 闭包:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures
  3. λ演算-函数式语言的起源:https://zhuanlan.zhihu.com/p/164700404

往期文章

现代包管理工具:pnpm

硬核!手撕源码第一弹: UpdateNotifier

前端转后端是一种怎样的体验

当程序员遇到会写代码的产品经理…

手摸手写个webpack plugin

手摸手写个webpack loader

这锅我背了…

ES2021新特性

用魔法打败魔法:前端代码规范化

手摸手教你搭个脚手架

手摸手教你搭建npm私有库

requestAnimationFrame

标签:function,谈些,return,函数,add,Currying,Array,我们,fn
来源: https://blog.csdn.net/qq_24734285/article/details/120902474

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有