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

编写一个通用的柯里化函数currying #33

Open
YvetteLau opened this issue Jun 21, 2019 · 14 comments
Open

编写一个通用的柯里化函数currying #33

YvetteLau opened this issue Jun 21, 2019 · 14 comments

Comments

@YvetteLau
Copy link
Owner

No description provided.

@KRISACHAN
Copy link

KRISACHAN commented Jun 21, 2019

常规法

  const currying = fn => {
    if (typeof fn !== 'function') {
      return new Error('No function provided')
    }
    return function curriedFn(...args){
      if (args.length < fn.length) {
        return function () {
          return curriedFn.apply(null, args.concat([].slice.call(arguments)))
        }
      }
      return fn.apply(null, args)
    }
  }

支持多调用法

      const currying = (func, count = func.length, ...args) => count <= args.length ? func(...args) : currying.bind(null, func, count, ...args)
      const sum = (...args) => args.reduce((acc, cur) => acc + cur, 0)
      const add = currying(sum, 2)
      console.log(add(1)(2))

@ChasLui
Copy link

ChasLui commented Jun 21, 2019

  const currying = fn => {
    if (typeof fn !== 'function') {
      return new Error('No function provided')
    }
    return function curriedFn(...args){
      if (args.length < fn.length) {
        return function () {
          return curriedFn.apply(null, args.concat([].slice.call(arguments)))
        }
      }
      return fn.apply(null, args)
    }
  }

再来个左向, 自动柯里化

@jinsong5
Copy link

来个ES5的

var currying = function (fn) {
    if (typeof fn !== 'function') {
        return new Error('need function')
    }
    var args = []
    return function doCurrying() {
        if (arguments.length === 0) {
            return fn.apply(this, args);
        } else {
            [].push.apply(args, arguments);
            return doCurrying;
        }
    }
}

@Tcdian
Copy link

Tcdian commented Jun 21, 2019

类似 lodash 的 curry,支持占位符,支持 new 调用时忽略 this

function curry(func, arity = func.length, guard, partial = []) {
    // guard 守卫, 防止传入多余参数
    partial = guard === void 0 ? partial : []
    const placeholder = curry.placeholder
  
    const boundFunc = function(...args) {
        const argsLen = args.filter(arg => arg !== placeholder).length
        // _replaceHolders 函数, 处理占位符的情况
        const finalArgs = _replaceHolders(partial, args, placeholder)
      
        if (argsLen >= arity) {
            // _executeBound 函数, 处理 new 调用boundFunc ,this应被忽略问题
            return _executeBound(func, boundFunc, this, this, finalArgs)
        } else {
            return curry(func, arity - argsLen, void 0, finalArgs)
        }
    }
    return boundFunc
}
  
// 设置占位符, 默认 '__'
curry.placeholder = '__'
  
// 整合 partials 和 args 为一个 完整的参数数组, 将partials中的 placeholder替换为 args中元素, args中剩余元素放到 数组结尾
function _replaceHolders(partials, args, placeholder) {
    let separator = 0
    const combinedArgs = partials.map(partial => {
        if (partial === placeholder) {
            return args[separator++]
        }
        return partial
    })
    return [...combinedArgs, ...args.slice(separator)]
}
  
function _executeBound(func, boundFunc, thisArg, context, args) {
    if (!(context instanceof boundFunc)) {
        return func.call(thisArg, ...args)
    }
    const instance = Object.create(func.prototype)
    const result = func.call(instance, ...args)
    if (isObject(result)) {
        return result
    }
    return instance
}

function isObject(obj) {
    const type = typeof obj
    return obj !== null && (type == 'object' || type == 'function')
}

@HappyChenchen
Copy link

函数柯里化是逐步传参、逐步缩小函数的适用范围、逐步求解的过程,它将多变量函数拆解为单变量的多个函数的依次调用。

var currying = function (fn) {
    var args = [];
    return function () {
      if (arguments.length === 0) {
        return fn.apply(this, args);
      } else {
        [].push.apply(args, arguments);
        return arguments.callee;
      }
    }
};

函数柯里化的主要作用和特点:

  • 参数复用 – 复用最初函数的第一个参数
  • 提前返回 – 返回接受余下的参数且返回结果的新函数
  • 延迟执行 – 返回新函数,等待执行

@shenanheng
Copy link

shenanheng commented Jun 21, 2019

const currying = fn => {
    return function curriedFn(...args){
      if (args.length < fn.length) {
        return function () {
          return curriedFn.apply(null, args.concat([].slice.call(arguments)))
        }
      }
      return fn.apply(null, args)
    }
  }

@AILINGANGEL
Copy link

function curry(fn) {
    let oldArgs = Array.prototype.slice.call(arguments, 1);
    return function() {
        let newArgs = Array.prototype.slice.call(arguments);
        let args = newArgs.concat(oldArgs);
        if (args.length >= fn.length) {
            return fn.apply(this, args);
        } else {
            return curry.call(this, fn, ...args);
        }
    }
}

@YvetteLau
Copy link
Owner Author

YvetteLau commented Jun 21, 2019

在开始之前,我们首先需要搞清楚函数柯里化的概念。

函数柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

const currying = (fn, ...args) =>
    args.length < fn.length
        //参数长度不足时,重新柯里化该函数,等待接受新参数
        ? (...arguments) => currying(fn, ...args, ...arguments)
        //参数长度满足时,执行函数
        : fn(...args);
function sumFn(a, b, c) {
    return a + b + c;
}
var sum = currying(sumFn);
console.log(sum(2)(3)(5));//10
console.log(sum(2, 3, 5));//10
console.log(sum(2)(3, 5));//10
console.log(sum(2, 3)(5));//10

函数柯里化的主要作用:

  • 参数复用
  • 提前返回
  • 延迟执行

@yelin1994
Copy link

函数柯里化

函数柯里化,就是见一个接受多个参数的函数转化为接受单一参数的函数的技术.代码如下,亲测可用

const cury = function (fn) {
    const args = Array.prototype.slice.call(arguments, 1)
    const argArr = [...args]
    return function currying() {
        argArr.push(...arguments)
        if (argArr.length < fn.length) {
            return cury.call(this, fn, ...argArr)
        } else {
            return fn.call(null, ...argArr)
        }
    }
}
function add(a, b, c) {
   return  a+ b+ c
}
const curyAdd = cury(add)
console.log(curyAdd(1)(2,3))

@Cain-kz
Copy link

Cain-kz commented Jun 24, 2019

柯里化函数:一个currying的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。
特点:1.延迟计算 2.参数复用 3.动态生成函数的作用

var curryings = function(fn){
    var args = [];
    //存储到curring函数中除了fn之外的其它参数,并存储args函数中
    args = args.concat([].slice.call(arguments,1));
    return function(){
      if(arguments.length===0){
        return fn.apply(this,args);
      }else{
        //将fn中的参数展开,然后在存储到args数组中
        [].push.apply(args,arguments);
      }
    }
  }
  var costss = (function(){
    var money = 0;
    return function () {
      for (var i = 0, l = arguments.length; i < l; i++) {
        money += arguments[i]
      }
      return money;
    }
  })()
  var costst = curryings(costss,100,200)// 转化成 currying 函数
  costss(100,200)
  console.log(costst()) //600

@into-piece
Copy link

/**
 * 利用闭包的特性让参数暂时保存,让传入的参数与原函数所需参数数量进行比较,未达到则递归进行存储,待达到则一次性执行
 * @param {*} fn 需要被柯里化的函数
 * @param  {...any} oldArgs  所存入的闭包的参数值
 */
const currying = (fn, ...oldArgs) => (...newArgs) => {
  let args = [...oldArgs, ...newArgs];
  if (args.length >= fn.length) {
    return fn(...args);
  } else {
    return currying(fn, ...args);
  }
};

function sumFn(a, b, c) {
  return a + b + c;
}
var sum = currying(sumFn);
console.log(sum(1)(2)(3));
console.log(sum(1, 2)(3));
console.log(sum(1, 2, 3));

@jodiezhang
Copy link

const progressCurrying=(fn,...args) =>{
  
          var len = fn.length
          //var args = args || []
      
          return function() {
              var _args = Array.prototype.slice.call(args)
              _args.push(...arguments)
      
              // 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
              if (_args.length < len) {
                  return progressCurrying.call(this,fn,...args,..._args)
              }else{
              // 参数收集完毕,则执行fn
              return fn.apply(this, _args)
              }
      
          }
      }
      
      let test =  progressCurrying(function(a, b, c) {
      console.log(a + b + c)
      })
      test(1, 2, 3)
      test(45, 12)(13)
      test(11)(21)(31)

@ZadaWu
Copy link

ZadaWu commented Jul 4, 2019

什么是柯里化函数

curry 的概念很简单:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
你可以一次性地调用 curry 函数,也可以每次只传一个参数分多次调用。

例子:

var add = function(x) {
  return function(y) {
    return x + y;
  };
};

var increment = add(1);
var addTen = add(10);

increment(2);
// 3

addTen(2);
// 12

柯里化实现

1- 这里利用的是bind的this参数后面的会使一个函数拥有预设的初始参数。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置

var fn = function () {
    return fn.bind(null, ...aruguments)
}

2- 在1的基础上限制参数的个数,比如你想要的是5个

var numOfRequiredArguments = 5;
var fn = function() {
  if (arguments.length < numOfRequiredArguments) {
    return fn.bind(null, ...arguments);
  } else {
    console.log('we already collect 5 arguments: ', [...arguments]);
    return null;
  }
}
存在的问题:
1. 我们希望将收集到的参数传递给需要它们的目标函数,而不是通过将他们传递给console.log()打印
2. 变量numOfRequiredArguments不应该是写死的,它应该是目标函数所期待的参数个数

3- 因此,除去存储参数以外,我们还需要在某处存储对于目标函数的引用

function magician(targetfn) {
    var numOfArgs = targetfn.length;
    return function fn(){
        if (arguments.length < numOfArgs) {
            return fn.bind(null, ...arguments);
        } else {
            return targetfn.apply(null, arguments);
        }
    }
}

magician 函数的作用是:它接收目标函数作为参数,然后返回‘参数收集器’函数,与上例中 fn 函数作用相同。唯一的不同点在于,当收集的参数数量与目标函数所必需的参数数量相等时,它将把收集到的参数通过 apply 方法给到该目标函数,并返回计算的结果。这个方法通过将其存储在 magician 创建的闭包当中来解决第一个问题(引用目标函数)。

4- 使用magician函数本身作为参数收集器

function magician(targetfn) {
    var numOfArgs = targetfn.length;
    if (arguments.length - 1< numOfArgs) {
        return magician.bind(null, ...arguments)
    } else {
        return targetfn.apply(null, Array.prototype.slice.call(arugments, 1))
    }
}

因为magician接受目标函数作为它的第一个参数,因此收集到的参数将始终包含该函数作为arguments[0]。这就导致,我们在检查有效参数的总数时,需要减去第一个参数。
因为目标函数时递归传递给magician函数的,因此我们可以通过传入第一个参数显示地引用目标函数,以代替使用闭包保存目标函数的引用

@MissNanLan
Copy link

function progressCurrying(fn, args) {

    var _this = this
    var len = fn.length;
    var args = args || [];

    return function() {
        var _args = Array.prototype.slice.call(arguments);
        Array.prototype.push.apply(args, _args);

        // 如果参数个数小于最初的fn.length,则递归调用,继续收集参数
        if (_args.length < len) {
            return progressCurrying.call(_this, fn, _args);
        }

        // 参数收集完毕,则执行fn
        return fn.apply(this, _args);
    }
}

const sum = (...args) => args.reduce((acc, cur) => acc + cur, 0)
const add = currying(sum, 2)
console.log(add(1)(2))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests