纯函数、柯里化以及组合函数

纯函数

在程序设计中,若一个函数符合以下条件,那么这个函数被称为纯函数:

  • 此函数在 相同的输入值 时,需产出 相同的输出
  • 函数和输出和输入值以外的其他隐藏信息和状态无关,也和由于 I/O设备产生的外部输出无关。
  • 该函数不能有语义上可观察到的函数副作用,诸如“触发事件”,使输出设备输出,或更改输出值以外物件的内容。

确定的输入一定会产生确定的输出
函数执行的过程中,不会产生任何的副作用

副作用:
在执行一个函数时,除了返回函数数值之外,还对调用函数产生了附加的影响,比如 修改了全局变量,修改参数或者改变外部存储。

柯里化

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

只传递一部分参数调用函数,并让它返回一个函数去处理剩余的参数的过程就叫做柯里化

function add (x, y, z) {
  return x + y + z
}

const result = add(1, 2, 3)

console.log(result)

// add 函数的柯里化实现

function addC (x) {
  return function (y) {
    return function (z) {
      return x + y + z
    }
  }
}

const resultC = addC(1)(2)(3)
console.log(resultC)

// sum 函数的柯里化简化版本
const addCS = x => {
  return y => {
    return z => {
      return x + y + z
    }
  }
}
console.log(addCS(1)(2)(3))

const addCS2 = x => y => z => x + y + z
console.log(addCS2(1)(2)(3))

柯里化存在的原因(思想?)

  • 在函数式编程中,我们希望一个函数处理的问题尽可能单一,所以将每次传入的参数在单一的函数中进行处理,处理完毕之后在下一个函数中再使用处理后的结果。
  • 并且,柯里化还有助于我们进行逻辑的复用:
function log (date, type, message) {
  console.log(`[${date.getHours()}:${date.getMinutes()}][${type}]:[${message}]`)
}

log(new Date(), 'DEBUG', '查找到轮播图的bug')
log(new Date(), 'DEBUG', '查询菜单的bug')
log(new Date(), 'DEBUG', '查询数据的bug')

// 柯里化使得逻辑复用,减少传参数目

const logC = date => type => message => {
  console.log(`[${date.getHours()}:${date.getMinutes()}][${type}]:[${message}]`)
}

const nowLog = logC(new Date())
nowLog("DEBUG")("测试查找BUG")

const nowAndDebugLog = logC(new Date())('DEBUG')
nowAndDebugLog('查找轮播图BUG')

手写柯里化函数

function add (x, y, z) {
  return this.a + x + y + z
}

function Currying (fn) {
  function curried (...args) {
    if (args.length >= fn.length) {
      // 这里使用 fn.apply 是为了当 f.call({ a: 3 }, 10, 20, 30) 
      // 时将 {a: 3} 作为 fn 的 this
      return fn.apply(this, args)
    } else {
      return function (...args2) {
        return curried.apply(this, [...args, ...args2])
      }
    }
  }
  return curried
}

const f = Currying(add)
console.log(f.call({ a: 3 }, 10, 20, 30)) // 63

组合函数

假设我们现在要对某一个数据进行函数调用,需要依次执行两个函数 fn1、fn2,我们将这两个函数组合起来自动依次调用的过程就称为组合函数。

function double (num) {
  return num * 2
}

function square (num) {
  return num ** 2
}

let count = 10
let result = square(double(count))
console.log(result)

// 组合函数
function composeFn (f, g) {
  return function (count) {
    console.log(g(f(count)))
  }
}

composeFn(double, square)(10)

通用组合函数

function hyCompose (...fns) {
  const length = fns.length
  for (let i = 0; i < length; i++) {
    if (typeof fns[i] !== 'function') {
      throw new TypeError('要求都是函数类型')
    }
  }

  return function (...args) {
    let idx = 0
    let result = length ? fns[idx].apply(this, args) : args
    while (++idx < length) {
      result = fns[idx].call(this, result)
    }
    return result
  }
}

function double (m) {
  return m * 2
}

function square (n) {
  return n ** 2
}

var newFn = hyCompose(double, square)
console.log(newFn(10))
posted @ 2023-06-27 14:39  脏猫  阅读(42)  评论(0)    收藏  举报