函数式编程(定义&柯里化)|青训营笔记

134 阅读5分钟

这篇文章里,你会学到, 什么是函数式编程,函数式编程的特点有哪些,什么是柯里化,以及柯里化的强化例子。可以根据目录查看你需要的部分

概念

什么是函数式编程?

  • 函数式编程是一种编程范式,它的核心思想是将计算过程看作一系列数学函数的组合,避免使用可变状态和可变数据,通过函数的输入和输出来描述程序的行为。

函数式编程的特点有哪些?

  • 函数是一等公民:函数可以作为变量、参数和返回值进行传递和操作。
  • 纯函数:纯函数是指输入相同,输出也相同,而且没有任何副作用(即不会修改外部状态)的函数。
  • 不可变数据:在函数式编程中,数据是不可变的,即一旦创建就不可修改。
  • 延迟执行:函数式编程中通常采用惰性求值(也称为延迟执行)的方式,即只有在需要结果时才会计算表达式的值。

函数式编程通常采用高阶函数、闭包、柯里化、函数组合等技术,它的目标是编写简洁、可复用、易于维护和并发执行的程序。


柯里化

什么是柯里化?

  • 柯里化(Currying)是一种将接受多个参数的函数转换成一系列接受单一参数的函数的技术。
  • 换句话说,柯里化就是将一个函数转换成一个返回新函数的高阶函数的过程。

例如,可以用柯里化来实现一个通用的加法函数:

function add(x: number): (y: number) => number {
  return function(y: number): number {
    return x + y;
  };
}

let add1 = add(1); // 返回一个函数,加1
let add2 = add(2); // 返回一个函数,加2

console.log(add1(10)); // 11
console.log(add2(10)); // 12
  1. 定义了一个 add 函数,它接受一个数字类型的参数 x
  2. 返回一个函数类型的值 (y: number) => number,这个返回的函数也接受一个数字类型的参数 y
  3. 返回值为两个参数的和
  4. 使用 add 函数来分别创建两个新函数 add1add2

这里我们分别传入参数 12 来调用 add 函数,得到两个新函数 add1add2add1 表示加 1 的操作,add2 表示加 2 的操作

  1. 分别调用 add1add2 函数,并传入参数 10 来执行加法运算
  2. 分别使用这两个新函数来执行加法运算并输出结果,使用 console.log 函数来输出结果

柯里化例子

举一个通用的柯里化函数

  • 可以接受任意个数的参数
type CurryFunc<T> = (arg: T) => CurryFunc<T> | T;

function curry<T>(fn: (...args: T[]) => any): CurryFunc<T> {
  return function curried(...args: T[]): CurryFunc<T> | T {
    if (args.length >= fn.length) {
      return fn(...args);
    } else {
      return function(arg: T): CurryFunc<T> | T {
        return curried(...args, arg);
      };
    }
  };
}

定义了一个泛型函数 curry,它接受一个任意个数的参数,并返回一个柯里化函数

  • 这个柯里化函数可以接受任意个数的参数
  • 并在最后一个参数传入后执行原始函数

对curry 做解释

  1. 定义了一个泛型类型 CurryFunc<T>,它表示柯里化函数的类型
  2. 接受一个泛型类型为 T 的参数 arg,并返回一个联合类型 (CurryFunc<T> | T) 这个联合类型表示,柯里化函数可以继续返回另一个柯里化函数,也可以返回一个最终结果 T
  3. 函数签名中的 <T> 表示这个函数是一个泛型函数
  4. 函数的参数 fn 是一个接受多个泛型类型的参数 T 的函数,返回值类型可以是任意类型 any
  5. 函数体内部的 return 语句返回了一个新的函数 curried
  6. 接受参数 ...args,也就是一个不定长的参数数组
  7. curried 函数中,首先通过 args.lengthfn.length 的比较来判断是否已经传入了足够的参数
  8. 如果已经传入了足够的参数,就直接调用原函数 fn 并返回它的返回值
  9. 如果还没有传入足够的参数,就返回一个新的函数,这个函数接受单个参数 arg,然后将 arg 与之前已经传入的参数合并为一个新的参数数组,并递归调用 curried 函数,直到所有参数都传入为止
  10. 最后,整个函数的返回类型是 CurryFunc<T>,这个类型是一个联合类型,可以是函数 curried 或者 T 类型的任意值
function add(x: number, y: number, z: number): number {
  return x + y + z;
}
  • 定义了一个简单的原始函数 add,它接受三个数字类型的参数,并返回它们的和
let curriedAdd = curry(add);
let add1 = curriedAdd(1);
let add2 = add1(2);

console.log(add2(3)); // 6
console.log(curriedAdd(1)(2)(3)); // 6
  • 使用 curry 函数来创建一个新的柯里化函数 curriedAdd 它接受任意个数的数字类型的参数,并返回一个新的柯里化函数或执行原始函数
  • 分别使用 curriedAdd 函数来创建两个新的函数 add1add2
  • 分别接受一个和两个数字类型的参数,并执行加法运算
  • 这些新函数可以链式调用,并使用不同数量的参数来执行加法运算
  • 使用这些新函数来执行加法运算并输出结果