函數式編程(三)—— 柯里化

  • 柯里化正則表達式

    • Lodash中的柯里化 —— curry()
    • 案例
    • 柯里化原理模擬
    • 柯里化總結
  • 【函數式編程整體設計】
以前講了函數的前置知識 函數式編程(一)—— 前置知識

還有純函數的知識 函數式編程(二)—— 純函數編程

下面咱們能夠進行函數式編程的基礎內容 —— 柯里化。segmentfault

柯里化

解決硬編碼的問題數組

// 下面這段代碼是解決了不純的函數的問題,可是裏面出現了硬編碼
function checkAge (age) { 
    let mini = 18
    return age >= mini 
}


// 普通的純函數
function checkAge (min, age) {
    return age >= min
}
console.log(checkAge(18, 20))  //true
console.log(checkAge(18, 24))  //true
console.log(checkAge(20, 24))  //true
// 常常使用18,這段代碼是重複的。避免重複
function checkAge (min) {
    return function (age) {
        return age >= min
    }
}

let checkAge18 = checkAge(18)
let checkAge20 = checkAge(20)

console.log(checkAge18(20)) //true
console.log(checkAge18(24)) //true

柯里化:當函數有多個參數的時候,咱們能夠對函數進行改造。咱們能夠調用一個函數,只傳遞部分的參數(這部分參數之後永遠不變),而後讓這個函數返回一個新的函數。新的函數傳遞剩餘的參數,而且返回相應的結果。緩存

// ES6
let checkAge = min => (age => age >= min)
// 輸出相同

Lodash中的柯里化 —— curry()

_.curry(func)閉包

  • 功能:建立一個函數,該函數接收一個或多個 func的參數,若是 func 所須要的參數都被提供則執行 func 並返回執行的結果。不然繼續返回該函數並等待接收剩餘的參數。
  • 參數:須要柯里化的函數
  • 返回值:柯里化後的函數
const _ = require('lodash')

// 參數是一個的爲一元函數,兩個的是二元函數
// 柯里化能夠把一個多元函數轉化成一元函數
function getSum (a, b, c) {
  return a + b + c
}

// 定義一個柯里化函數
const curried = _.curry(getSum)

// 若是輸入了所有的參數,則當即返回結果
console.log(curried(1, 2, 3)) // 6

//若是傳入了部分的參數,此時它會返回當前函數,而且等待接收getSum中的剩餘參數
console.log(curried(1)(2, 3)) // 6
console.log(curried(1, 2)(3)) // 6

案例

判斷字符串中有沒有空白字符,或者提取字符串中全部空白字符,可使用字符串的match方法:
''.match(/\s+/g)函數式編程

可是咱們要是寫一個數組的去處空白字符的方法,上面的代碼就沒法重用。那咱們如何用函數式方法去寫函數

function match(reg, str) {
  return str.match(reg)
}

reg的表達式是重複的,上面的函數如何柯里化,思路是這樣的:ui

//柯里化處理
const _ = require('lodash')

//利用lodash的curry函數,第一個參數是匹配規則,第二個參數是字符串,生成一個match函數
const match = _.curry(function (reg, str) {
  return str.match(reg)
})

// 根據規則haveSpace是一個匹配空格的函數
const haveSpace = match(/\s+/g)

console.log(haveSpace("hello world")) //[ ' ' ]
console.log(haveSpace("helloworld")) //null
// 由此能夠判斷字符串裏面有沒有空格

// 那若是是數字的話怎麼辦呢?
// 根據規則haveNumber是一個匹配數字的函數
const haveNumber = match(/\d+/g)
console.log(haveNumber('abc')) // null

// 對於數組怎麼匹配元素中有沒有空格
const filter = _.curry(function(func, array) {
  return array.filter(func)
})

// filter函數,第一個參數傳遞匹配元素中有沒有空格
//第二個參數是指定的數組
console.log(filter(haveSpace, ['John Connor','John_Donne'])) // [ 'John Connor' ]

// 若是上述寫仍是比較麻煩,那麼能夠再封裝一個函數出來
// filter能夠傳一個參數,而後返回一個函數
// 這個findSpace就是匹配數組元素中有沒有空格的函數
const findSpace = filter(haveSpace)
console.log(findSpace(['John Connor','John_Donne'])) // [ 'John Connor' ]

下面對上面的思路作一個小的總結,柯里化的好處就是咱們能夠最大程度的重用咱們的函數編碼

const _ = require('lodash')

//match函數是根據一些正則,匹配字符串,返回匹配結果
const match = _.curry(function (reg, str) {
  return str.match(reg)
})

//haveSpace函數是一個匹配空格的函數
const haveSpace = match(/\s+/g)

//haveNumber函數是一個匹配數字的函數
const haveNumber = match(/\d+/g)

//filter函數是定義一個數組和過濾規則,返回符合匹配規則的數組
const filter = _.curry(function(func, array) {
  return array.filter(func)
})

//findSpace函數是匹配數組元素中有空格並返回符合狀況的數組的函數
const findSpace = filter(haveSpace)

柯里化原理模擬

咱們找一個以前作過的例子分析一下

const _ = require('lodash')

function getSum (a, b, c) {
  return a + b + c
}

const curried = _.curry(getSum)

console.log(curried(1, 2, 3))  // 6
console.log(curried(1)(2, 3))  // 6
console.log(curried(1, 2)(3))  // 6

實現一個柯里化轉換函數要進行分析

  1. 入參出參:調用傳遞一個純函數的參數,完成以後返回一個柯里化函數
  2. 入參狀況分析:
  • 若是curried調用傳遞的參數和getSum函數參數個數相同,那麼當即執行並返回調用結果
  • 若是curried調用傳遞的參數是getSum函數的部分參數,那麼須要返回一個新的函數,而且等待接收getSum的其餘參數
  1. 重點關注:
  • 獲取調用的參數
  • 判斷個數是否相同
// 模擬柯里化函數
function curry (func) {
  // 取名字是爲了下面實參小於形參的時候用的
  return function curriedFn(...args) {
    // 判斷實參和形參的個數
    if(args.length < func.length) {
      return function() {
        // 等待傳遞的剩餘參數,若是剩餘函數的參數加上以前的參數等於形參,那麼就返回func
        // 第一部分參數在args裏面,第二部分參數在arguments裏面,要將兩個合併而且展開傳遞(使用...)
        // concat函數要合併兩個數組,arguments爲僞數組,因此用Array.from進行轉換
        return curriedFn(...args.concat(Array.from(arguments)))
      }
    }
    // 若是實參大於等於形參的個數
    // args是剩餘參數,是個數組形式,而返回的時候要展開(使用...)
    return func(...args)
  }
}


// test
const curriedTest = curry(getSum)

console.log(curriedTest(1, 2, 3))  // 6
console.log(curriedTest(1)(2, 3))  // 6
console.log(curriedTest(1, 2)(3))  // 6

柯里化總結

  • 柯里化可讓咱們給一個函數傳遞較少的參數獲得一個已經記住了某些固定參數的新函數(好比match函數新生成了haveSpace函數,裏面使用了閉包,記住了咱們給傳遞的正則表達式的參數)
  • 這是一種對函數參數的'緩存'(使用了閉包)
  • 讓函數變的更靈活,讓函數的粒度更小
  • 能夠把多元函數轉換成一元函數,能夠組合使用函數產生強大的功能

函數式編程整體設計

相關文章
相關標籤/搜索