Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

有赞: sum(2, 3)实现sum(2)(3)的效果(一面) #21

Open
IWSR opened this issue Apr 30, 2019 · 1 comment
Open

有赞: sum(2, 3)实现sum(2)(3)的效果(一面) #21

IWSR opened this issue Apr 30, 2019 · 1 comment
Assignees

Comments

@IWSR
Copy link

IWSR commented Apr 30, 2019

To:

https://github.com/IWSR

面试公司:

有赞

面试环节:

网上找的

问题:

sum(2, 3)实现sum(2)(3)的效果

@IWSR
Copy link
Author

IWSR commented May 1, 2019

思路

这是一个典型的函数柯里化的知识点,其定义为将接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数。

此题的要求为sum(2)(3)实现与sum(2, 3)的效果【暂且这么认为吧,知识点就是柯理化】
由题意可归纳为以下要求

  • sum函数的返回值应该是一个函数,否则没法实现连续调用多次(由题意暂且限定为两次)的功能
  • 多次(由题意暂且限定为两次)调用时传入的参数需要与前几次传入的参数联动

此时可以大致归纳为要求实现一个方法,该方法需要返回另外一个方法,且返回的方法需要保持对原有的函数作用域内某变量的引用,很熟悉的要求,就是个闭包的结构。
以下放出初步的实现

function sum() {
  function add(a, b) {
    return a + b
  }
  // slice会返回一个新数组,这不会影响到arguments
  let args = Array.prototype.slice.call(arguments);
  // 返回一个函数可以提供二次调用
  return function() {
    // 此处的arguments为第二次调用时传入的参数
    let newArgs = args.concat(Array.prototype.slice.call(arguments))
    return add.apply(this, newArgs)
  }
}

但是这样的写法是局限的且不怎么好用,理由如下:

  • 功能有限,操作只能限定为add

为了解决上面的问题,思路也很清晰,应该将add操作作为一个fn的形参交给使用者自己去定义,因此可得到下面的变种

// 改名为curry了
function curry(fn) {
  // slice会返回一个新数组,这不会影响到arguments
  let args = Array.prototype.slice.call(arguments, 1);
  // 返回一个函数可以提供二次调用
  return function() {
    // 此处的arguments为第二次调用时传入的参数
    let newArgs = args.concat(Array.prototype.slice.call(arguments))
    return fn.apply(this, newArgs)
  }
}

虽说fn成为了参数可以被自定义,但是缺陷也出现了,curry(add, a)(b)可满足sum(a, b) => sum(a)(b)的要求,但如果sum(a, b, c)那就只能凉凉,换句话说我们需要判断场景(fn的参数个数,以及当前已经传入的元素个数)来决定是返回一个可调用的函数还是fn的执行结果。

function sub_curry(fn) {
    let args = [].slice.call(arguments, 1);
    return function() {
        return fn.apply(this, args.concat([].slice.call(arguments)));
    };
}

function curry(fn, length) {
    // 初始化时赋值为fn的形参个数,用以标示剩余需要传入参数的个数
    length = length || fn.length;

    const slice = Array.prototype.slice;

    return function() {
        if (arguments.length < length) {
            const combined = [fn].concat(slice.call(arguments));
            // length - arguments.length用以计数当前已传入的参数
            return curry(sub_curry.apply(this, combined), length - arguments.length);
        } else {
            return fn.apply(this, arguments);
        }
    };
}

@acodercc acodercc changed the title To IWSR: sum(2, 3)实现sum(2)(3)的效果(有赞) 有赞: sum(2, 3)实现sum(2)(3)的效果(一面) May 6, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants