Advanced-Frontend/Daily-Interview-Question

第 84 题:请实现一个 add 函数,满足以下功能。

yygmind opened this issue · 91 comments

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

之前写过 2 篇文章,可以参考一二。
1、【进阶 6-1 期】JavaScript 高阶函数浅析
2、【进阶 6-2 期】深入高阶函数应用之柯里化

其中第一篇文章给出了前三个功能的实现,并没有覆盖到后面三种。
第二篇文章实现了一个通用的柯里化函数,覆盖实现了所有功能。

实现 1:

function currying(fn, length) {
  length = length || fn.length; 	// 注释 1
  return function (...args) {			// 注释 2
    return args.length >= length	// 注释 3
    	? fn.apply(this, args)			// 注释 4
      : currying(fn.bind(this, ...args), length - args.length) // 注释 5
  }
}

实现 2:

const currying = fn =>
    judge = (...args) =>
        args.length >= fn.length
            ? fn(...args)
            : (...arg) => judge(...args, ...arg)

其中注释部分

  • 注释 1:第一次调用获取函数 fn 参数的长度,后续调用获取 fn 剩余参数的长度

  • 注释 2:currying 包裹之后返回一个新函数,接收参数为 ...args

  • 注释 3:新函数接收的参数长度是否大于等于 fn 剩余参数需要接收的长度

  • 注释 4:满足要求,执行 fn 函数,传入新函数的参数

  • 注释 5:不满足要求,递归 currying 函数,新的 fn 为 bind 返回的新函数(bind 绑定了 ...args 参数,未执行),新的 length 为 fn 剩余参数的长度

const curry = (fn, arity = fn.length, ...args) =>
  arity <= args.length ? fn(...args) : curry.bind(void 0, fn, arity, ...args);
function add() {
  let args = [].slice.call(arguments);
  let fn = function(){
   let fn_args = [].slice.call(arguments)
   return add.apply(null,args.concat(fn_args))
 }
fn.toString = function(){
  return args.reduce((a,b)=>a+b)
}
return fn
}
function add(){
	let args = [...arguments];
	let addfun = function(){
		args.push(...arguments);
		return addfun;
	}
	addfun.toString = function(){
		return args.reduce((a,b)=>{
			return a + b;
		});
	}
	return addfun;
}

还有一种方法

function add(){
	if(arguments.length === 0){
		let num = 0;
		add.args.forEach(v=>{
			num += v;
		});
		add.args = null;
		return num;
	}else{
		add.args = add.args ? add.args : [];
		add.args = add.args.concat([...arguments]);
		return add;
	}
}
add(1)(2)(3)();
add(1, 2)(3)(8)();
function curry (fn) {
  const finalLen = fn.length
  let args = [].slice.call(this,1)
  return function currying () {
    args = args.concat(Array.from(arguments))
    const len = args.length
    return len >= fn.length ? fn.apply(this, args) : currying
  }
}
function add (a,b,c) {
  return a+b+c
}
const add1 = curry(add)
console.log(add1(1, 2)(3))
function add(...args) {
  let sum = 0;
  const innerAdd = (...args) => {
    args.forEach(i => (sum += i));
    return innerAdd;
  };
  innerAdd.toString = () => sum;
  return innerAdd(...args);
}

如果要做成高阶函数以便支持任意迭代器,累加器的初始值设定要麻烦一点
尤其是考虑到任意长度输入和空输入,要多一些判断逻辑。

写了一个支持以上所有特性的

const curryReducer = (fn) => {
  return (...args) => {
    let runned = false;
    const chain = (...args) => {
      if (!args.length) return chain;
      chain.acc = (runned ? [chain.acc] : []).concat(args).reduce(fn);
      !runned && (runned = true);
      return chain;
    };
    chain.acc = undefined;
    chain.toString = () => chain.acc;
    return chain(...args);
  };
};
// * ---------------- simple add function

const add = curryReducer((a, e) => a + e);

console.log('' + add(1, 2, 3)()(4, 5)(6)(7)(8, 9, 10));
const method = {
  add: (a, e) => a + e,
  minus: (a, e) => a - e,
  times: (a, e) => a * e,
  devide: (a, e) => a / e,
};
Object.values(method).forEach((e) => {
  console.log('batch test -------------- method is: ', e);
  const chainFn = curryReducer(e);
  console.log('' + chainFn());
  console.log('' + chainFn(6));
  console.log('' + chainFn(6, 2));
  console.log('' + chainFn(6)(2));
  console.log('' + chainFn()(6)(2));
  console.log('' + chainFn(6, 2, 3));
  console.log('' + chainFn(6, 2)(3));
  console.log('' + chainFn(6)(2)(3));
});
function add(...a) {
    let sum = a.reduce((p, n) => p + n);

    function next(...b) {
        let _sum = b.reduce((p, n) => p + n);
        sum = sum + _sum;
        return next;
    }

    next.toString = function () {
        return sum;
    };

    return next;
}
function currying(fn, length) {
  length = length || fn.length; 	// 注释 1
  return function (...args) {			// 注释 2
    return args.length >= length	// 注释 3
    	? fn.apply(this, args)			// 注释 4
      : currying(fn.bind(this, ...args), length - args.length) // 注释 5
  }
}
const sum = function(t,y,u){
    let args = [].slice.call(arguments)
    return args.reduce((a, b) => a + b)
} ;
    const add = currying(sum);
    console.log(add(1,2)(2)(9)) ;  	// Uncaught TypeError: add(...)(...) is not a function
请问这个调用这个柯里化函数sum中的形参还要自己手动补吗?
我看这个形参少于实际调用add函数时就会报错,还是我调用的方式错了呢?
@yygmind

@wjryours sum 函数定义的参数长度为 3,调用时参数为 4,所以问题出在这里

@wjryours sum 函数定义的参数长度为 3,调用时参数为 4,所以问题出在这里

柯里化生成的 add 函数若是存储的形参个数达不到定义的参数长度, 则是返回 [Function]

那么如题 add(1) ==>1 , add(1)(2) ==> 3 则不是没有实现吗

@wjryours sum 函数定义的参数长度为 3,调用时参数为 4,所以问题出在这里

柯里化生成的 add 函数若是存储的形参个数达不到定义的参数长度, 则是返回 [Function]

那么如题 add(1) ==>1 , add(1)(2) ==> 3 则不是没有实现吗

我想表达的就是这个意思,按照你这个写法那些基本的功能都通过不了,可以解答一下吗@yygmind

@wjryours sum 函数定义的参数长度为 3,调用时参数为 4,所以问题出在这里

柯里化生成的 add 函数若是存储的形参个数达不到定义的参数长度, 则是返回 [Function]
那么如题 add(1) ==>1 , add(1)(2) ==> 3 则不是没有实现吗

我想表达的就是这个意思,按照你这个写法那些基本的功能都通过不了,可以解答一下吗@yygmind

我觉得题目出的不好(描述有歧义)。
我的理解里,题目本身和柯里化是有差异的,
题目更像是迭代器而不是柯里化

如果是柯里化,那么缺少参数的时候是等待所有参数到齐则再开始进行运算,并返回计算结果。
(因为不是所有方法都像 add 一样简单,能支持部分参数计算,不然因为缺少参数,计算逻辑报错了怎么办?)
参数不足的时候返回一个中间 function,以便继续调用。

至于直接能读到(toString),这是一个 trick。
对于柯里化这个高阶方法来说,我觉得不应该一起实现。
就像我说的,是应该等参数到齐再计算(固定参数 function 的 length),
可以参考一下 lodash 或者 ramda 里的 curry

至于如何实现题目本身
(任意长度也能返回计算结果,也就是说,调用一次执行一次)
(像我上面说的,更像是实现一个迭代器,add 只是一个算子)
可以参考一下我的答案
#134 (comment)
Inspired by @CoffeeWorm
#134 (comment)

B2D1 commented
const curry = fn => {
    const len = fn.length;
    return function curried(...args) {
        if (args.length === len) {
            return fn.apply(null, args);
        }
        return (..._args) => {
            return curried.apply(null, [...args, ..._args]);
        };
    };
};

const sum = (x, y, z) => x + y + z;
const add = curry(sum);

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

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

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

// 6
add(1)(2)(3);
 // 第一步,先实现加和运算

            var add = function (a, b) {
                return a + b;
            }

            var currying = function (fn, defineVal = 0) {
                return function (...args) { // 第一次调用的是这个函数
                    // 每次执行前先进行和的初始化
                    var sum = defineVal;

                    function func(...argts) { // 第二次之后调用的是这个函数
                        if (args.length === 0) {
                            return func.toString();
                        } else {
                            argts.unshift(sum);
                            sum = argts.reduce(fn);
                            return func;
                        }
                    }
                    func.toString = () => sum;
                    return func(...args);
                }
            }

            var add = currying(add);
            console.info(add(1)); // => 1
            console.info(add(1)(2)); // => 3
            console.info(add(1)(2)(3)); // => 6
            console.info(add(1, 2)(3)); // => 6
            console.info(add(1)(2, 3)); // => 6
            console.info(add(1, 2, 3)); // => 6
            console.info(add(1, 2, 3)(4)); // => 10
function add(...x) {
			var sum = x.reduce((a,b)=>a+b,0)
			var tmp = function(...y) {
				sum =sum+y.reduce((a,b)=>a+b,0)
				return tmp;
			};
			tmp.toString = function() {
				return sum;
			};
			return tmp;
		}
		
		console.log(+add(1)(2)(3))   // 6
		console.log(+add(1)(2,3))    // 6
		console.log(+add(1,2)(3))    // 6
		console.log(+add(1)(2)(3,4))   //10
		
		
		add(1)(2)(3).valueOf
		//ƒ valueOf() { [native code] }
		add(1)(2)(3).valueOf()
		//ƒ 6
		+add(1)(2)(3).valueOf()
		//6
		+""+add(1)(2)(3).valueOf()
		//6
	</script>

`

function add() {

    var sum = function (arr) {
        var curSum = 0;
        for (var i = 0; i < arr.length; i++) {
            var obj = arr[i];
            if(obj){
                curSum = curSum + obj;
            }
        }
        return curSum;
    };

    var result = function () {
        var xx = Array.from(arguments);
        xx.push(result._lastSum);
        return add.apply({},xx);
    };

    result.valueOf = function () {
        return result._lastSum;
    };
    result.toString = function () {
        return result._lastSum ;
    };

    result._lastSum  = sum(arguments);

    return result;
}

`

function add() {
  return Array.from(arguments).reduce((pre, item) => pre + item, 0)
}

function curry(fn) {
  let params = [];

  function result() {
    params.push(...arguments)

    return result;
  }

  result.toString = () => { 
    const tempParams = params;
    params = [];

    return fn.call(null, ...tempParams) 
  }

  return result;
}
let timer = null
function add(...arg1){
	return function(...arg2){
		let arg = [...arg1,...arg2]
		clearTimeout(timer)
		timer = setTimeout(()=>{
			console.log(arg.reduce((p,c)=>p+c))
		},0)
		return add(...arg);
	}
}

add(2,3)(4)(5,6)(9)()(1) //30
function add(...args) { 
  add.params = add.params.concat(args)
  return add;
}
add.params = []
add.toString = function() {
  var val = add.params.reduce((a,b) => a+b)
  add.params = []
  return val;
}
function add(...args) {
  var f = function(...args1) {
    return add.apply(null, [...args, ...args1])
  }
  f.toString = () => args.reduce((a,b)=>a+b)
  return f
}

诸位真的是大佬---膜拜了 我看了半天算是看懂函数柯里化的应用了

function Add(a, b, c) {
  return a + b + c;
}
function cuuring(fn) {
  let finallen = fn.length;
  var args = []
  return function digui() {
    var innerargs = Array.prototype.slice.call(arguments);
    args = args.concat(innerargs);
    return args.length >= finallen ? fn.apply(null, args) : digui;
  }
}
var add1 = cuuring(Add);
console.log(add1(1)(2)(3))
let timer = null
function add(...arg1){
	return function(...arg2){
		let arg = [...arg1,...arg2]
		clearTimeout(timer)
		timer = setTimeout(()=>{
			console.log(arg.reduce((p,c)=>p+c))
		},0)
		return add(...arg);
	}
}

add(2,3)(4)(5,6)(9)()(1) //30

看来半天 不明白 toString 是怎么调用的...
看你的回答 太真实了

感觉关键点在重写函数toString方法啊,我就一直在想,怎么又返回函数,而最后又输出结果,真棒!

function add(...num) {
    let res = 0 //第一次调用函数时生成一个闭包来存储结果
    num.forEach(item => res += item) //遍历输入参数加到res上

    let ret = function (...num) {
      num.forEach(item => res += item)
      return ret
    }

    ret.toString = function () {
      return res
    }

    ret.valueOf = function () {
      return res
    }

    return ret
  }
  console.log(add(1)); // 1
  console.log(add(1)(2)); // 2
  console.log(add(1)(2)(3)); // 6
  console.log(add(1)(2)(3,7)(4,5,6));// 28 

使用了一个闭包完成了这个效果

function add(...num) {
    let res = 0 //第一次调用函数时生成一个闭包来存储结果
    num.forEach(item => res += item) //遍历输入参数加到res上

    let ret = function (...num) {
      num.forEach(item => res += item)
      return ret
    }

    ret.toString = function () {
      return res
    }

    ret.valueOf = function () {
      return res
    }

    return ret
  }
  console.log(add(1)); // 1
  console.log(add(1)(2)); // 2
  console.log(add(1)(2)(3)); // 6
  console.log(add(1)(2)(3,7)(4,5,6));// 28 

使用了一个闭包完成了这个效果

你这里输出的结果其实是 f 1f 6...

image

function add(...firstArgs) {
const result = firstArgs.reduce((pre, now) => pre + now);
const fn = (...args) => {
return add(...args, result);
};
fn.toString = () => result;
return fn;
}

学到了,fn.toString();

function add(...num) {
    let res = 0 //第一次调用函数时生成一个闭包来存储结果
    num.forEach(item => res += item) //遍历输入参数加到res上

    let ret = function (...num) {
      num.forEach(item => res += item)
      return ret
    }

    ret.toString = function () {
      return res
    }

    ret.valueOf = function () {
      return res
    }

    return ret
  }
  console.log(add(1)); // 1
  console.log(add(1)(2)); // 2
  console.log(add(1)(2)(3)); // 6
  console.log(add(1)(2)(3,7)(4,5,6));// 28 

使用了一个闭包完成了这个效果

你这里输出的结果其实是 f 1f 6...

image

在不知道会一直调用多少次的情况下, 不返回一个函数, 怎么能够继续调用呀

var add = function() {
this.arr = [];
var print = () => {
if(this.arr.length > 0) {
console.log(arr[arr.length-1]);
this.arr = [];
}
}
this.arr.push([...arguments].reduce((a,b)=>a+b,0));
setTimeout(print,0);
return add.bind(this,...arguments);
}

const Curry = fn => {
	return function judge (...firstParma){
		if (firstParma.length >= fn.length) {
			return fn(...firstParma);
		} else {
			// 这里的 ...secondParma 函数的第二次或者更多次调用时的参数, ...firstParma 是第一次传入的参数,这里的意思是
			// 当函数只有一个的时候就直接返回一个 rest 参数后的函数
			// 当函数有多个的时候也就是 fn.length > firstParma.length  的时候
			// 这时候就需要把第二个函数的参数和第一个函数参数合起来,然后做递归
			// 这里的 ...secondParma, ...firstParma 两个都可以 rest 的原因是这里他们都已经是值了,不再是参数了
			return (...secondParma) => judge(...secondParma, ...firstParma);
		}
	};
};

// Test
const fn = Curry(function (a, b, c){
	console.log([a, b, c]);
});

fn(2)(3)(1); // 有三个函数,参数有三个
fn(2,3,1);// 只有一个函数,参数也是三个

我把@yygmind 给的方法做下翻译

function add(...arg) {
  let arr = [];
  function fn(...arg) {
    arr.push(...arg);
    return fn;
  }
  fn.toString = () => {
    return arr.reduce((acc, cur) => acc + cur);
  };
  return fn.call(add, ...arg);
}
如果可以把arr定义在外部就更简单了
let arr = [];
function add(...arg) {
  arr.push(...arg);
  add.toString = () => {
    return arr.reduce((acc, cur) => acc + cur);
  };
  return add;
}
function add(...num) {
    let res = 0 //第一次调用函数时生成一个闭包来存储结果
    num.forEach(item => res += item) //遍历输入参数加到res上

    let ret = function (...num) {
      num.forEach(item => res += item)
      return ret
    }

    ret.toString = function () {
      return res
    }

    ret.valueOf = function () {
      return res
    }

    return ret
  }
  console.log(add(1)); // 1
  console.log(add(1)(2)); // 2
  console.log(add(1)(2)(3)); // 6
  console.log(add(1)(2)(3,7)(4,5,6));// 28 

使用了一个闭包完成了这个效果

ret.toString跟ret.valueOf是什么新操作?为什么要这样写呢?
ret.toString = function () {
return res
}
ret.valueOf = function () {
return res
}

function add(...rest){
	let result = rest.reduce((prev,cur) => {
            return prev + cur
        },0)
	let f = function(...arg){
            var argArr = [...arg,...rest];
	    return argArr.reduce((p,c) => p + c,0)
	}
	f.toString = function(){
		return result
	}
	return f
}
const curry = fn => {
    const len = fn.length;
    return function curried(...args) {
        if (args.length === len) {
            return fn.apply(null, args);
        }
        return (..._args) => {
            return curried.apply(null, [...args, ..._args]);
        };
    };
};

const sum = (x, y, z) => x + y + z;
const add = curry(sum);

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

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

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

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

这个add(1)
add(1)(2)
就不行啊大佬

function add(a,b,c){
return [a,b,c];
}
function curry(fn){
var length = fn.length;
var args = [];
return _curr;
function _curr(){
var len = arguments.length;
length -=len;
args=[...args,...arguments];
if(length){
return _curr;
}else{
return fn(...args);
}
}
}
var _add = curry(add);
var _add2 = curry(add);
var _add3 = curry(add);
console.log(_add(1,2,3));
console.log(_add2(1,2)(3));
console.log(_add3(1)(2)(3));

function add() {
  let args = [].slice.call(arguments);
  let fn = function(){
   let fn_args = [].slice.call(arguments)
   return add.apply(null,args.concat(fn_args))
 }
fn.toString = function(){
  return args.reduce((a,b)=>a+b)
}
return fn
}

不行啊你这个 都带了f 都是函数类型

感觉大家的办法都有问题啊。。。这题是不是出的不太好

诸位真的是大佬---膜拜了 我看了半天算是看懂函数柯里化的应用了

function Add(a, b, c) {
  return a + b + c;
}
function cuuring(fn) {
  let finallen = fn.length;
  var args = []
  return function digui() {
    var innerargs = Array.prototype.slice.call(arguments);
    args = args.concat(innerargs);
    return args.length >= finallen ? fn.apply(null, args) : digui;
  }
}
var add1 = cuuring(Add);
console.log(add1(1)(2)(3))

你这个也不行兄弟。。。

function add(...args1) {
    function innerAdd(...args2) {
        args1 = [...args1, ...args2];
        return innerAdd;
    }

    innerAdd.toString = function() {
        return args1.reduce((sum, cur) => sum + cur, 0);
    }

    return innerAdd;
}

厉害了,看似柯里化,其实考的是...

Hjw52 commented

会这道的都是大佬吧...

// 定义累加函数
const add = function(a, b) {
  return a + b;
};

// 定义累乘函数
const mul = function(a, b) {
  return a * b;
};

const currying = function(fn, initVal) {
  const fns = function(...args) {
    const func = function(...args2) {
      return fns.call(this, ...args, ...args2);
    };
    // 当执行console后才开始计算并输出最终的值
    func.toString = () => {
      return args.reduce(fn, initVal);
    };
    return func;
  };
  return fns;
};

const addC = currying(add, 0);
const mulC = currying(mul, 1);

console.log(addC(1)); // => 1
console.log(addC(1)(2)); // => 3
console.log(addC(1)(2)(3)); // => 6
console.log(addC(1, 2)(3)); // => 6
console.log(addC(1)(2, 3)); // => 6
console.log(addC(1, 2, 3)); // => 6

console.log(mulC(1)); // => 1
console.log(mulC(1)(2)); // => 2
console.log(mulC(1)(2)(3)); // => 6
console.log(mulC(1, 2)(3)); // => 6
console.log(mulC(1)(2, 3)); // => 6
console.log(mulC(1, 2, 3)); // => 6
function add(...args) {
  if(args.length === 3) return args.reduce((a,b) => a+b, 0)
  return add.bind(null, ...args)
}
function add (...args1) {
  let result = args1.reduce((pre, current) => pre + current, 0)

  function sum (...args2) {
    result = args2.reduce((prev, item) => prev + item, result)
    return sum
  }

  sum.toString = function () {
    return result
  }

  return sum
}

console.log(add(1))
console.log(add(1)(2))
console.log(add(1)(2)(3))
console.log(add(1, 2)(3, 4)(5))
console.log(add(1, 2, 3)(4)(5)(6))
// https://wsvincent.com/javascript-currying/
  const curry = (fn, ...args) =>
    (fn.length <= args.length) ?
      fn(...args) :
      (...more) => curry(fn, ...args, ...more);
function redu (a, b, c) {
  console.log(Array.from(arguments).reduce((a, b) => a + b))
}

function currying (fn) {
  var refn
  return function name (...arg) {
    refn = (refn || fn).bind(null, ...arg)
    if (refn.length == 0) {    // 如果传入的length  大于 剩余需要的length
      refn()
      refn = null
    }
    return name
  }
}

var add = currying(redu)

// 返回的add 实际上是我的  name  name 会访问父作用域的 refn  refn 就是我已经绑定了 多次传入了参数的fn函数

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

function sum () {
var arr = Array.from(arguments)
addsum = function() {
arr.push(...arguments)
return addsum
}
addsum.toString = function () {
arr.push(...arguments)
return arr.reduce((a,b)=>a+b)
}
return addsum;
}

看不懂 toString 的建议看看这篇文章 https://www.cnblogs.com/coco1s/p/6509141.html

const add = (...args) => {
const sum = (...args2) => {
args = args.concat(args2)
return sum
}

sum.toString = () => {
  return args.reduce((a, b) => a +b, 0)
}  

return sum

}

add(1)(2)(3, 4)

闭包的方式也可以实现

    function add () {
      let eventList = []
      for (let i = 0; i < arguments.length; i++) {
        eventList.push(arguments[i])
      }
      function a () {
        for (let i = 0; i < arguments.length; i++) {
          eventList.push(arguments[i])
        }
        return a
      }
      setTimeout(() => {
        log(eventList.reduce((a,b) => a + b))
      },0)
      return a
    }

//加() 立即执行的
const curry = (fn, arr = []) =>
(...args) => ((a, b) => b.length === 0 // 判断
? fn(...a)
: curry(fn, a))([...arr, ...args], [...args])

let curryPlus = curry((...x) => x.reduce((a, b) => a + b)) // 函数累加
console.log(curryPlus(1, 2, 3)());
     function autoCurry (fn, ...args) {
        if (fn.length <= args.length) {
          return fn(...args)
        } else {
          return function (...args2) {
            return autoCurry(fn, ...args, ...args2)
          }
        }
      }
      function add (x, y, z) {
        return x+y+z;
      }
      var addCurry = autoCurry(add, 1)
      console.log(addCurry(2)(3)) // 6
      console.log(addCurry(2,3)) // 6 
      console.log(addCurry(2)()) // f (...args2) { ... } 

自动柯里化,通用函数

实现 1:

function currying(fn, length) {
  length = length || fn.length; 	// 注释 1
  return function (...args) {			// 注释 2
    return args.length >= length	// 注释 3
    	? fn.apply(this, args)			// 注释 4
      : currying(fn.bind(this, ...args), length - args.length) // 注释 5
  }
}

实现 2:

const currying = fn =>
    judge = (...args) =>
        args.length >= fn.length
            ? fn(...args)
            : (...arg) => judge(...args, ...arg)

其中注释部分

  • 注释 1:第一次调用获取函数 fn 参数的长度,后续调用获取 fn 剩余参数的长度
  • 注释 2:currying 包裹之后返回一个新函数,接收参数为 ...args
  • 注释 3:新函数接收的参数长度是否大于等于 fn 剩余参数需要接收的长度
  • 注释 4:满足要求,执行 fn 函数,传入新函数的参数
  • 注释 5:不满足要求,递归 currying 函数,新的 fn 为 bind 返回的新函数(bind 绑定了 ...args 参数,未执行),新的 length 为 fn 剩余参数的长度

你这写的必须最后加个()执行,不然得不到结果

hjiog commented
function add(...arg) {
    let res = 0;
    function fn(...arg) {
        res += arg.reduce((total, v) => total + v, 0);
        return fn;
    }
    fn.toString = function () {
        return res
    }
    return fn(...arg)
}

console.log(add(1))
console.log(add(1)(2))
console.log(add(1)(2)(3))
console.log(add(1, 2)(3))
console.log(add(1, 2, 3))

我觉得题目不严谨

function add() {
  let res = [...arguments].reduce((a, b) => a + b, 0);
  const fn = function () {
    res = [...arguments].reduce((a, b) => a + b, res);
    return fn;
  };
  fn.toString = fn.valueOf = () => res;
  return fn;
}
const add = (...rest) => {
  const args = rest

  const addFunc = (...nextRest) => {
    args.push(...nextRest)

    return addFunc
  }

  addFunc.toString = () => {
    return args.reduce((a, b) => a + b)
  }

  return addFunc
}
m7yue commented
const add = (...args) => {
  const _add = (...args1) => {
    return add(...args, ...args1)
  }
  _add.toString = () => [...args].reduce((t, c) => t+c,  0)

  return _add
}
add(1,2)(3)(4,5)(6)

toString实在过于hacky,这题本质和各种括号加减运算差不多

function add() {
    const params = [...arguments]

    recur.toString = acc;

    return recur()

    function acc() {
        return params.reduce((acc, item) => {
            acc += item;

            return acc;
        }, 0);
    }

    function recur() {
        params.push(...arguments);

        if (params.length >= 3) {
            return acc()
        } else {
            return recur
        }
    }
}

console.log("debug-", add(1, 2));
console.log("debug-", add(1));
console.log("debug-", add(1, 2)(3));
console.log("debug-", add(1)(2, 3));
console.log("debug-", add(1)(2)(3));
console.log("debug-", add(1, 2, 3));
function add() {
  let args = [].slice.call(arguments);
  let fn = function(){
   let fn_args = [].slice.call(arguments)
   return add.apply(null,args.concat(fn_args))
 }
fn.toString = function(){
  return args.reduce((a,b)=>a+b)
}
return fn
}

这返回的是函数啊??

orime commented

单纯从题意触发,add调用时候必须有toString之类的触发条件;用外部变量保存每次执行累计的参数列表,执行完toString之后清空即可:

// * 柯理化函数
let concatArgs = []
function add(...args) {
  concatArgs = concatArgs.concat(args)
  return add
}
add.toString = function () {
  const res = concatArgs.reduce((pre, cur) => pre + cur, 0)
  concatArgs = []
  return res
}

console.log(add(1)(2)(3) + "") // 6
console.log(add(1)(2, 3) + "") // 6
console.log(add(1, 2)(3) + "") // 6
console.log(add(1, 2, 3) + "") // 6
console.log(add(1).toString()) // 1
console.log(add(1)(2).toString()) // 3
console.log(add(1)(2)(3).toString()) // 6
console.log(add(1)(2, 3).toString()) // 6
console.log(add(1, 2)(3).toString()) // 6
console.log(add(1, 2, 3).toString()) // 6

题目不严谨,有歧义。

在实际项目中如果有这样的需求,或者用 toString 这种 hack 的方式实现。

感谢同事不杀之恩吧。

const money = add(1)(2)
typeof money  // function
// 同事:“... wtf ?”


const money = add(1)(2)
if (money < 100) money(3)
// 同事:“???”


const money = add(1)(2)
console.log(money)  // f 3
// 此处省略 100+ 行代码...
console.log(money)  // money:“猜猜我变没变”

// ...卒
function add(...args) {
    if (Number.prototype.add !== add) Number.prototype.add = add;
    return args.reduce(
        (pre, now) => pre + now,
        this instanceof Number ? this : 0
    );
}

修改Number满足吗

let timer = null
function add(...arg1){
	return function(...arg2){
		let arg = [...arg1,...arg2]
		clearTimeout(timer)
		timer = setTimeout(()=>{
			console.log(arg.reduce((p,c)=>p+c))
		},0)
		return add(...arg);
	}
}

add(2,3)(4)(5,6)(9)()(1) //30

但是这个好像不能执行add(1,2,3)这种

做一个可循环调用add函数,执行时把arg1arg2内外传入的两个参数加到一块。函数的toString方法在函数被alert或者console的时候会被调用,可以用这个方法来确定是否需要输出总结果。

function add (...arg1) {
  let t = function (...arg2) {
    return add(Array.from(arg1).concat(Array.from(arg2)).reduce((a, b)=> {return a+b}))
  }
  t.toString = ()=>{
    return Array.from(arg1).reduce((a, b)=> {return a+b})
  }
  return t
}
add(1); 	// 1
add(1)(2);  // 3
add(1)(2)(3); // 6
add(1)(2, 3); // 6
add(1, 2)(3); // 6
add(1, 2, 3); // 6
function add(...args) {
  let final = [...args];
  setTimeout(() => {
    console.log(final.reduce((sum, cur) => sum + cur));
  }, 0);
  const inner = function (...args) {
    final = [...final, ...args];
    return inner;
  }
  return inner;
}

function add() {
var a = 0;
var out = arguments;

function sum() {
    for(var i = 0; i < arguments.length; i++) {
        a += arguments[i]
    }
    return sum
}

sum.toString = function() {
    for(var i = 0; i < out.length; i++) {
        a += out[i]
    }
    return a;
}

return sum;

}

const add=(a,b,c)=>a+b+c
    const currying=(fn,length)=>{
        length=length||fn.length
        return (...args)=>args.length>=length?fn(...args):currying(fn.bind(this,...args),length-args.length)

    }
    const sum=currying(add)
    console.log(sum(1)(2)(3));
    console.log(sum(1,2)(3));
    console.log(sum(1)(2,3));
    console.log(sum(1,2,3));
function add() {
  let args = [].slice.call(arguments);
  let fn = function(){
   let fn_args = [].slice.call(arguments)
   return add.apply(null,args.concat(fn_args))
 }
fn.toString = function(){
  return args.reduce((a,b)=>a+b)
}
return fn
}
const add=(...args)=>{
        const fn=(...params)=>add(...args,...params)
        fn.toString=()=>args.reduce((a,b)=>a+b)
        return fn
    }
    console.log(add(1,2)(3).toString());
function add(...args) {
  let nums = [...args];

  function _add(...argsNext) {
    if (argsNext.length === 0) {
      // 不再有参数传入作为计算结果的信号
      return nums.reduce((pre, cur) => {
        return pre + cur;
      }, 0);
    } else {
      nums.push(...argsNext);
      return _add;
    }
  }

  return _add;
}
function add(...a){
   let sum = (...b) => add(...[...a,...b]);  //合并参数
   let result = a.reduce((x,y) => x+y);  //对所有参数进行累加
   sum.toString = () => result;  //将结果返回
   return sum;
}
console.log(add(1,2,3)(2,2).toString());  //10

为什么console的时候不会自动调用 toString方法呢,上面的答案在我的chrome控制台里返回的都是函数

function add(...args1) {
    let x = args1.reduce((c,r)=>{return c+r},0)
    console.log(x)
    return  function(...args2){
            return add(...args2,x);
    };
}
add(1,2)(2)(1,3,4)

可以实现连续不定参数的调用,返回参数和

function add () {
  let sum
  let arg = [].concat(...arguments)
  const fn = function () {
    arg = arg.concat(...arguments)
    sum = arg.reduce((pre, next) => pre + next)
    return fn
  }
  fn.toString = function () {
    return sum
  }
  return fn()
}
console.log('//' + add(1, 2)(3)()(4, 5, 6))
 function addFn(...args) {
        return args.reduce((acc, cur) => acc + cur)
    }

    function currying(fn) {
        let args = []

        return function demo(...arg) {
            console.log(args);
            if (arg) {
                args = [...args, ...arg]
                return demo
            } else {
                return fn.apply(this, args)
            }
        }
    }

    const add = currying(addFn)
    console.log(add(1)(2)(3)());
    console.log(add(1)(2,3));

 var twoSum = function(nums, target) {
    const map = {}

    for (let i = 0 ;i < nums.length; i ++ ){
        if(map[target - nums[i] ] >= 0) {
            return [map[target - nums[i]],i]
        }
        map[nums[i]] = i;   
    }
};

为什么console的时候不会自动调用 toString方法呢,上面的答案在我的chrome控制台里返回的都是函数

我也遇到了类似的问题,toString的方案在新版的Chrome(我的版本是98)似乎不起作用,打印出来的是一个函数,需要显式调用才能返回结果

function add(a) {
    var curSum = a;
    function res(b) {
        curSum += b
        return res
    }
    res.toString = function () {
        return curSum
    }
    return res
}

建议把题目描述清楚

sum = (...nums)=>{
    let res = 0;
    sum2 = (...nums2)=>{
        res = nums2.reduce((acc,cur)=>acc + cur, res);
        console.log(res)
        return sum2;
    }
    return sum2(...nums)
}

//   实现 sum 函数
//sum(1)(2)(3)
//== 6; // true
//sum(1, 2, 3) //== 6; // true

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

如果只是打印结果不返回值的话,用个宏任务

function add() {
    let sum = {current: 0}
    sum.current = [...arguments].reduce((a, b) => a + b)

    setTimeout(() => {
        console.log(sum.current)
    })
    
    function _add() {
        sum.current += [...arguments].reduce((a, b) => a + b)
        return _add
    }
    return _add
}

add(1, 2)(3, 4)(5)
add(1)(2)(3)(4, 5, 6)
add(1, 2, 3)

上面那些toString的都是隐式调用的,结果拼接个字符串,或者非全等比较,就这样

add(1, 2)(3) + ''
add(1, 2)(3) == 6

题目有让你们调用toString?个个在这乱抄,就不会运行一下?

题目有让你们调用toString?个个在这乱抄,就不会运行一下?

这位同学,不必用这种口气说话

function add(...args) {
  const fn = (...newArgs) => add(...args, ...newArgs);
  fn.valueOf = () => args.reduce((acc, curr) => acc + curr, 0);
  return fn;
}

function add(){
let sum = 0
function innerAdd(...args){
if(args.length===0){
return sum;
}
sum+=args.reduce((a,b)=>a+b,0)
return innerAdd;
}
return innerAdd(...arguments)
}

// 函数求和
function sumFn(...rest) {
    return rest.reduce((a, b) => a + b);
}
// 柯里化函数
var currying = function (func) {
    // 保存所有传递的参数
    const args = [];
    return function result(...rest) {
        // 最后一步没有传递参数,如下例子
        if(rest.length === 0) {
            return func(...args);
        } else {
            // 中间过程将参数push到args
            args.push(...rest);
            return result; // 链式调用
        }
    }
}
// 测试
currying(sumFn)(1)(2)(3)(4)(); // 10
currying(sumFn)(1, 2, 3)(4)(); // 10
function curry(fn, acc, initValue) {
  let args = [];

  fun.toString = function () {
    if (args.length === fn.length) {
      return fn.apply(this, args);
    }

    const result = args.reduce(acc, initValue);

    args = [];

    return result;
  };

  return fun;

  function fun() {
    let result = fun;

    args = args.concat([...arguments]);

    if (args.length === fn.length) {
      result = fn.apply(this, args);
      args = [];
    }

    return result;
  }
}

var add = curry(
  (a, b, c) => {
    return a + b + c;
  },
  (acc, a) => acc + a,
  0
);

console.log(add(1).toString()); // 1
console.log(add(1)(2).toString()); // 3
console.log(add(1)(2)(3)); // 6
console.log(add(1)(2, 3)); // 6
console.log(add(1, 2)(3)); // 6
console.log(add(1, 2, 3)); // 6