柯里化函數的實現

記錄柯里化函數實現的學習過程:

柯里化通常也稱部分求值,其含義是給函數分步傳遞參數,每次傳遞參數後部分應用參數,並返回一個更具體的函數接受剩下的參數,這中間可嵌套多層這樣的接受部分參數函數,直至返回最後結果。

如果要實現下面這個方法:

add(2)(1, 3, 4)(2, 3)(3)(4, 6)(7, 98)() // 133

上面這個函數當參數爲空的時候執行了內部參數所有值的相加,所以我們應該考慮當參數不爲空的時候將緩存起來,在爲空的時候再相加,這樣的思路會用閉包的方式來實現。下面是實現方法:

function add () {
  // 用來緩存所有的arguments值  
  let args = [].slice.call(arguments);
  // 新建currying函數實現柯里化  
  let currying = function () {
    // 如果參數爲空,那麼遞歸停止,返回執行結果
    if (arguments.length === 0) {
      return args.reduce((a, b) => a + b);
    } else {
      // 否則將參數保存到args裏面,返回currying方法
      args.push(...arguments);
      return currying
    }      
  }
  return currying
}
add(2)(1, 3, 4)(2, 3)(3)(4, 6)(7, 98)() // 133

上面有需要注意的一點,因爲currying函數裏面使用arguments,所以currying不能使用箭頭函數,箭頭函數內部的arguments的用法與箭頭函數內部的this差不多,它取的是上一級函數的arguments值。如果想用箭頭函數,currying函數可以這樣改動:

  let currying = (...rest) => {
    // 如果參數爲空,那麼遞歸停止,返回執行結果
    if (rest.length === 0) {
      return args.reduce((a, b) => a + b);
    } else {
      // 否則將參數保存到args裏面,返回currying方法
      args.push(...rest);
      return currying
    }      
  }

我們返回的currying函數還可以使用callee來實現,原理相同,但是嚴格模式下不能使用:

function add () {
  // 用來緩存所有的arguments值  
  let args = [].slice.call(arguments);
  // 新建currying函數實現柯里化  
  return function () {
    // 如果參數爲空,那麼遞歸停止,返回執行結果
    if (arguments.length === 0) {
      return args.reduce((a, b) => a + b);
    } else {
      // 否則將參數保存到args裏面,返回currying方法
      args.push(...arguments);
      return arguments.callee
    }      
  }
}
add(2)(1, 3, 4)(2, 3)(3)(4, 6)(7, 98)() // 133

對普通函數進行柯里化:

// 柯里化函數的構造方法
function curry (fn) {
  // 緩存除第一個參數的所有參數
  let args = [].slice.call(arguments, 1);  
  let _fn = function () {
    if (arguments.length === 0) {
      return fn.apply(this, args)
    } else {
      args.push(...arguments);
      return _fn
    }
  }
  return _fn
}
function add () {
  return [].reduce.call(arguments, (a, b) => a + b)
}
console.log(curry(add, 2)(1, 3, 4)(2, 3)(3)(4, 6)(7, 98)()) // 133

舉例柯里化函數思想實現的場景:

如減少重複傳遞的參數

function simpleURL(protocol, domain, path) {
    return protocol + "://" + domain + "/" + path;
}

我們使用的時候將會這樣:

var myurl = simpleURL('http', 'mysite', 'home.html');
var myurl2 = simpleURL('http', 'mysite', 'aboutme.html');

我們可以用柯里化的思想改寫:

function curry (fn) {
  // 緩存除第一個參數的所有參數
  let args = [].slice.call(arguments, 1);  
  return function () {return fn.apply(this, args.concat(...arguments))
  }
}


// 避免每次調用重複傳參
let myURL1 = curry(simpleURL, 'https', 'mysite');
let res1 = myURL1('home.html');    //

console.log(res1);//https://mysite/home.html

let myURL2 = curry(simpleURL, 'http', 'mysite');
let res2 = myURL2('aboutme.html');    //

console.log(res2);//http://mysite/aboutme.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章