资讯专栏INFORMATION COLUMN

从一道面试题谈谈函数柯里化(Currying)

cppprimer / 503人阅读

摘要:忍者秘籍一书中,对于柯里化的定义如下在一个函数中首先填充几个参数然后再返回一个新函数的技术称为柯里化。回到我们的题目本身,其实根据测试用例我们可以发现,函数的要求就是接受单一函数,例如但是与柯里化不同之处在于,柯里化返回的一个新函数。

  欢迎大家再一次来到我的文章专栏:从面试题中我们能学到什么,各位同行小伙伴是否已经开始了悠闲的春节假期呢?在这里提前祝大家鸡年大吉吧~哈哈,之前有人说,学面试题不会有什么长进,其实我觉得这个就像是我们英语考试中的阅读理解,带着问题去看文章反而更有利于自己的学习。
  之前的两篇文章:

一道颇有难度的JavaScript题

一个小小的JavaScript题目

都在稀土掘金和Segmentfault都获得了非常多的点击量,没有看的小伙伴们可以点击了解一下,今天为大家带来一道关于闭包和函数的柯里化方面的编程题目,各位小伙伴有没有开始跃跃欲试呢?
  编程题目的要求如下,完成plus函数,通过全部的测试用例。

"use strict";
function plus(n){
  
}
module.exports = plus

测试用例如下

"use strict";
var assert = require("assert")

var plus = require("../lib/assign-4")

describe("闭包应用",function(){
  it("plus(0) === 0",function(){
    assert.equal(0,plus(0).toString())
  })
  it("plus(1)(1)(2)(3)(5) === 12",function(){
    assert.equal(12,plus(1)(1)(2)(3)(5).toString())
  })
  it("plus(1)(4)(2)(3) === 10",function(){
    assert.equal(10,plus(1)(4)(2)(3).toString())
  })
  it("方法引用",function(){
    var plus2 = plus(1)(1)
    assert.equal(12,plus2(1)(4)(2)(3).toString())
  })
})

  实话说刚开始拿到这道题的时候我并没有完全的做出来,但是具体的思路是有的,肯定是关于函数的柯里化(Currying)方面的,应该是想考察一下面试者的闭包理解能力.
  那么首先介绍一下什么是函数的柯里化(Currying)。《JavaScript忍者秘籍》一书中,对于柯里化的定义如下:
  

在一个函数中首先填充几个参数(然后再返回一个新函数)的技术称为柯里化(Currying。

维基百科中关于其定义如下:

在计算机科学中,柯里化(Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。这个技术由克里斯托弗·斯特雷奇以逻辑学家哈斯凯尔·加里命名的。

  首先我们举个例子来具体的解释一下以上的概念。
  例如一个最简单的加法函数:

//函数定义
function add(x,y){
    return x + y;
}
//函数调用
add(3,4);//5

  如果采用柯里化是怎样将接受两个参数的函数变成接受单一参数的函数呢,其实很简单如下:

//函数表达式定义
var add = function(x){
    return function(y){
        return x + y;
    }
};
//函数调用
add(3)(4);

  这样理解起来其实是不是就很简单了,其实实质利用的就是闭包的概念(大家可以在我的另一篇文章浅谈JavaScript闭包看一下)。本质上讲柯里化(Currying)只是一个理论模型,柯里化所要表达是:如果你固定某些参数,你将得到接受余下参数的一个函数,所以对于有两个变量的函数y^x,如果固定了y=2,则得到有一个变量的函数2^x。这就是求值策略中的部分求值策略。
  柯里化(Currying)具有:延迟计算、参数复用、动态生成函数的作用。例如如果我们需要创建一个通用的DOM事件绑定函数,不使用柯里化的写法如下(该示例来自于博客园Tong Zeng):

//第四个参数用来标识是在冒泡阶段还是在捕获阶段执行函数
var addEvent = function(el,type,fn,capture){
    if (window.addEventListener) {
         el.addEventListener(type, function(e) {
             fn.call(el, e);
         }, capture);
     } else if (window.attachEvent) {
         el.attachEvent("on" + type, function(e) {
             fn.call(el, e);
         });
     } 
}

  但是在使用了柯里化(Currying)的情况下,不再需要每次添加事件处理都要执行一遍if...else...判断,只需要在浏览器中判定一次就可以了,把根据一次判定之后的结果动态生成新的函数,以后就不必重新计算。其实在实际使用中使用最多的一个柯里化的例子就是Function.prototype.bind()函数,我们也一并给出一个较为简单的Function.prototype.bind()函数的实现方式。

Function.prototype.bind = function(){
    var fn = this;
    var args = Array.prototye.slice.call(arguments);
    var context = args.shift();

    return function(){
        return fn.apply(context,
            args.concat(Array.prototype.slice.call(arguments)));
    };
};

  回到我们的题目本身,其实根据测试用例我们可以发现,plus函数的要求就是接受单一函数,例如:

plus(1)(4)(2)(3).toString()

但是与柯里化不同之处在于,柯里化返回的一个新函数。我们观察其实最后的求值是通过toString函数得到的,那么我们就很容易想到,我们可以给返回的函数增加一个toString属性就可以了。我自己写出的答案如下:

/**
 * Created by lei.wang on 2017/1/22.
 */

"use strict";

function plus(num) {
    var adder = function () {
        var _args = [];
        var _adder = function _adder() {
            [].push.apply(_args, [].slice.call(arguments));
            return _adder;
        };

        _adder.toString = function () {
            return _args.reduce(function (a, b) {
                return a + b;
            });
        }

        return _adder;
    }
    return adder()(num);
}

module.exports = plus;

  运行一下,通过全部的测试用例,需要注意的是由于题目的要求运行在严格模式下,所以我们在_adder函数内部是不能引用arguments.callee,这时我们采用的方法是给函数表达式中函数本身起名_adder,这样就解决的这个问题。
  再次感谢大家阅读本篇文章,希望大家能从中或多或少学到一些东西,入行资历甚浅,不足之处请多指教,欢迎大家在去我的博客MrErHu中留言打赏,愿大家一同进步。
  
  

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/81316.html

相关文章

  • 一道看透函数柯里

    摘要:对于函数的柯里化应该不陌生,简单来说技术是一种通过把多个参数填充到函数体中,实现将函数转换为一个新的经过简化的使之接受的参数更少函数的技术。 对于函数的柯里化(currying)应该不陌生,简单来说 Currying 技术是一种通过把多个参数填充到函数体中,实现将函数转换为一个新的经过简化的(使之接受的参数更少)函数的技术。当发现正在调用同一个函数时,并且传递的参数绝大多数都是相同的,...

    marser 评论0 收藏0
  • 高级函数技巧-函数柯里

    摘要:如果你对函数式编程有一定了解,函数柯里化是不可或缺的,利用函数柯里化,可以在开发中非常优雅的处理复杂逻辑。同样先看简单版本的方法,以方法为例,代码来自高级程序设计加强版实现上面函数,可以换成任何其他函数,经过函数处理,都可以转成柯里化函数。 我们经常说在Javascript语言中,函数是一等公民,它们本质上是十分简单和过程化的。可以利用函数,进行一些简单的数据处理,return 结果,...

    shixinzhang 评论0 收藏0
  • 开开心心做几道JavaScript机试 - 01

    摘要:碰到这种面试官,你只有是个题霸,再加上眼缘够才能顺利入围。只要按照我题目的思路,甚至打出来测试用例看看,就能实现这个题目了。答案根据的,对答案做出修正。另我的答案绝不敢称最佳,随时欢迎优化修正。但了解总归是好的。 我们在长期的面试过程中,经历了种种苦不堪言,不诉苦感觉不过瘾(我尽量控制),然后主要聊聊常见JavaScript面试题的解法,以及面试注意事项 忆苦 面试第一苦,面试官的土 ...

    liujs 评论0 收藏0
  • Redux:Middleware你咋就这么难

    摘要:接下来的函数就有点难度了,让我们一行一行来看。上面实际的含义就是将数组每一个执行的返回值保存的数组中。需要注意的是,方法返回值并不是数组,而是形如初始值的经过叠加处理后的操作。从而实现异步的。   这段时间都在学习Redux,感觉对我来说初学难度很大,中文官方文档读了好多遍才大概有点入门的感觉,小小地总结一下,首先可以看一下Redux的基本流程:showImg(https://segm...

    superPershing 评论0 收藏0
  • Javascript currying柯里详解

    摘要:面试题实现结果,题的核心就是问的的柯里化先说说什么是柯里化,看过许多关于柯里化的文章,始终搞不太清楚,例如柯里化是把接受多个参数的函数变换成接受一个单一参数最初函数的第一个参数的函数,并且返回接受余下的参数且返回结果的新函数的技术。 面试题:实现add(1)(2)(3) //结果 = 6,题的核心就是问的js的柯里化 先说说什么是柯里化,看过许多关于柯里化的文章,始终搞不太清楚,例如...

    mengbo 评论0 收藏0

发表评论

0条评论

cppprimer

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<