函数式编程(三)—— 柯里化

  • 柯里化正则表达式

    • 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函数,里面使用了闭包,记住了咱们给传递的正则表达式的参数)
  • 这是一种对函数参数的'缓存'(使用了闭包)
  • 让函数变的更灵活,让函数的粒度更小
  • 能够把多元函数转换成一元函数,能够组合使用函数产生强大的功能

函数式编程整体设计

相关文章
相关标签/搜索