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

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

Open
yygmind opened this issue May 30, 2019 · 35 comments

Comments

@yygmind
Copy link
Contributor

@yygmind yygmind commented May 30, 2019

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
@yygmind

This comment has been minimized.

Copy link
Contributor Author

@yygmind yygmind commented May 30, 2019

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

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

@yygmind

This comment has been minimized.

Copy link
Contributor Author

@yygmind yygmind commented May 30, 2019

实现 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 剩余参数的长度

@ZodiacSyndicate

This comment has been minimized.

Copy link

@ZodiacSyndicate ZodiacSyndicate commented May 30, 2019

const curry = (fn, arity = fn.length, ...args) =>
  arity <= args.length ? fn(...args) : curry.bind(void 0, fn, arity, ...args);
@zhouyangit

This comment has been minimized.

Copy link

@zhouyangit zhouyangit commented May 30, 2019

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
}
@ablikim915

This comment has been minimized.

Copy link

@ablikim915 ablikim915 commented May 30, 2019

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;
}
@ablikim915

This comment has been minimized.

Copy link

@ablikim915 ablikim915 commented May 30, 2019

还有一种方法

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)();
@GitHdu

This comment has been minimized.

Copy link

@GitHdu GitHdu commented May 30, 2019

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))
@CoffeeWorm

This comment has been minimized.

Copy link

@CoffeeWorm CoffeeWorm commented May 30, 2019

function add(...args) {
  let sum = 0;
  const innerAdd = (...args) => {
    args.forEach(i => (sum += i));
    return innerAdd;
  };
  innerAdd.toString = () => sum;
  return innerAdd(...args);
}
@seognil

This comment has been minimized.

Copy link

@seognil seognil commented May 30, 2019

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

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

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));
});
@linchengzzz

This comment has been minimized.

Copy link

@linchengzzz linchengzzz commented May 30, 2019

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;
}
@wjryours

This comment has been minimized.

Copy link

@wjryours wjryours commented May 31, 2019

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
@yygmind

This comment has been minimized.

Copy link
Contributor Author

@yygmind yygmind commented May 31, 2019

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

@ryanZiegler

This comment has been minimized.

Copy link

@ryanZiegler ryanZiegler commented May 31, 2019

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

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

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

@wjryours

This comment has been minimized.

Copy link

@wjryours wjryours commented May 31, 2019

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

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

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

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

@seognil

This comment has been minimized.

Copy link

@seognil seognil commented May 31, 2019

@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

This comment has been minimized.

Copy link

@B2D1 B2D1 commented Jun 20, 2019

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);
@woyiweita

This comment has been minimized.

Copy link

@woyiweita woyiweita commented Jun 25, 2019

 // 第一步,先实现加和运算

            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
@libin1991

This comment has been minimized.

Copy link

@libin1991 libin1991 commented Jul 9, 2019

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>
@linushp

This comment has been minimized.

Copy link

@linushp linushp commented Jul 11, 2019

`

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;
}

`

@doudounannan

This comment has been minimized.

Copy link

@doudounannan doudounannan commented Jul 14, 2019

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;
}
@harryliuy

This comment has been minimized.

Copy link

@harryliuy harryliuy commented Jul 17, 2019

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
@gitHber

This comment has been minimized.

Copy link

@gitHber gitHber commented Jul 18, 2019

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
}
@Yxiuchao

This comment has been minimized.

Copy link

@Yxiuchao Yxiuchao commented Jul 19, 2019

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

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))
@xiabeizi

This comment has been minimized.

Copy link

@xiabeizi xiabeizi commented Jul 19, 2019

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 是怎么调用的...
看你的回答 太真实了

@PatrickLh

This comment has been minimized.

Copy link

@PatrickLh PatrickLh commented Jul 21, 2019

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

@xtxt5555

This comment has been minimized.

Copy link

@xtxt5555 xtxt5555 commented Jul 28, 2019

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 

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

@w3cmark

This comment has been minimized.

Copy link

@w3cmark w3cmark commented Aug 5, 2019

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

@caihaihong

This comment has been minimized.

Copy link

@caihaihong caihaihong commented Aug 13, 2019

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();

@xtxt5555

This comment has been minimized.

Copy link

@xtxt5555 xtxt5555 commented Aug 14, 2019

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

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

@ToBeNumeroOne

This comment has been minimized.

Copy link

@ToBeNumeroOne ToBeNumeroOne commented Aug 19, 2019

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);
}

@XJawher

This comment has been minimized.

Copy link

@XJawher XJawher commented Aug 21, 2019

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 给的方法做下翻译

@yaodongyi

This comment has been minimized.

Copy link

@yaodongyi yaodongyi commented Sep 12, 2019

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;
}
@dongkeng001

This comment has been minimized.

Copy link

@dongkeng001 dongkeng001 commented Oct 10, 2019

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
}

@lalalaxx

This comment has been minimized.

Copy link

@lalalaxx lalalaxx commented Oct 31, 2019

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
}
@dbfterrific

This comment has been minimized.

Copy link

@dbfterrific dbfterrific commented Nov 9, 2019

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)
就不行啊大佬

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.