聊聊函数式编程中的“式”

当谈到函数式编程的"式"时,通常指的是函数的组合、转换和应用,以及处理数据的方式和风格。在函数式编程中,式是用来构建程序逻辑的基本单元。

下面更详细解释函数式编程中的几个关键式:

函数的组合:

函数式编程中,将多个函数组合成一个新的函数是常见的操作。函数组合可以通过函数的返回值作为另一个函数的输入来实现,实现函数的复用和组合。

示例:假设有两个函数 f 和 g ,函数 g 的输入为函数 f 的输出,可以通过函数的组合来实现: h = g(f(x))。

javascript 复制代码
const compose = f => g => x => f(g(x));
const f = compose (x => x * 4) (x => x + 3);
f(2) // 20

上面代码中,compose就是一个函数合成器,用于将两个函数合成一个函数。

函数的转换

函数式编程中,可以对函数进行一系列转换,例如柯里化(Currying)、部分应用 (Partial Application)等。这些转换可以将函数的参数进行重组或固定,不仅使得函数更灵活,还能简化函数调用的方式。

  • 柯里化指的是将一个多参数的函数拆分成一系列函数,每个拆分后的函数都只接受一个参数(unary)。
javascript 复制代码
function add (a, b) {
  return a + b;
}
add(1, 1) // 2

上面代码中,函数add接受两个参数ab

柯里化就是将上面的函数拆分成两个函数,每个函数都只接受一个参数。

javascript 复制代码
function add (a) {
  return function (b) {
    return a + b;
  }
}
// 或者采用箭头函数写法
const add = x => y => x + y;
const f = add(1);
f(1) // 2
  • 部分应用(Partial Application)是一种函数式编程的技术,它允许我们固定函数的一部分参数,并返回一个新的函数。这样做可以减少函数调用时需要提供的参数数量,从而使函数更加灵活和可重用。

举一个简单业务实例理解一下:

假设在我们的业务中,我们经常要给同一个用户发送邮件,主题和内容都是固定的,只需要提供收件人地址就可以了。我们可以使用部分应用来创建一个新的函数,该函数只需要提供收件人地址就可以调用 sendEmail,而无需每次都重复输入主题和内容。

javascript 复制代码
function sendEmail(to, subject, message) {
  // 发送电子邮件的逻辑代码
  console.log(`发送电子邮件给 ${to},主题为 ${subject},内容为 ${message}`);
}

const sendWelcomeEmail = sendEmail.bind(null, "hello@example.com", "欢迎加入我们");

sendWelcomeEmail("user1@example.com"); // 调用新函数,输出:发送电子邮件给 user1@example.com,主题为 欢迎加入我们,内容为 undefined
sendWelcomeEmail("user2@example.com"); // 调用新函数,输出:发送电子邮件给 user2@example.com,主题为 欢迎加入我们,内容为 undefined

在上述示例中,我们使用 bind() 方法将 sendEmail 函数的前两个参数(固定的主题和内容)绑定为 "hello@example.com""欢迎加入我们"。然后,我们创建一个新的函数 sendWelcomeEmail,该函数只需要提供收件人地址作为参数。

每当我们调用 sendWelcomeEmail 函数时,它会自动将绑定的参数 "hello@example.com""欢迎加入我们",以及传递的收件人地址一起传递给 sendEmail 函数来发送欢迎邮件。

函数的应用

函数式编程中,函数的应用是指将函数应用于输入数据,通过函数对数据进行转换、过滤、聚合等操作。函数的应用通常采用高阶函数的方式,即将函数作为参数传递给另一个函数。

示例:在函数式编程中,常用 map、reduce、filter 等高阶函数来对列表或集合中的元素进行转换、合并或筛选。

数据的处理方式和风格

函数式强调实用纯函数不可变性来处理数据。

函数式编程中,常常使用不可变数据结构来表示数据,并通过创建新的数据结构来进行操作和更新,而不是直接修改原来的数据。

  • 纯函数纯函数是指对于相同的输入,总是返回相同的输出,且没有任何副作用。
  1. 加法函数:
javascript 复制代码
function add(a, b) {
  return a + b;
}

这个函数是纯函数,因为它只是接收两个参数并返回它们的和。它没有副作用,不会修改任何外部状态,也不依赖于可变的数据。

  1. 平方函数:
javascript 复制代码
function square(x) {
  return x * x;
}

这个函数也是纯函数。对于相同的输入值,它总是返回相同的输出值。它没有副作用,不会改变任何外部环境,也不依赖于外部状态。

  1. 数组排序函数:
javascript 复制代码
function sortArray(arr) {
  return arr.sort();
}

这个函数不是纯函数,因为它直接修改了传入的数组,并且返回修改后的数组。它有副作用,修改了传入的参数,可能会影响到其他代码对该数组的引用。

  1. 获取当前时间函数:
javascript 复制代码
function getCurrentTime() {
  return new Date().getTime();
}

这个函数也不是纯函数,因为它会依赖外部状态(当前时间),每次调用都会返回不同的输出。它对外部的时间状态有依赖,因此在不同的时间点调用会返回不同的结果。

  • 不可变性是指数据一旦创建就无法被修改。在函数式编程中,强调使用不可变数据结构,这样可以避免副作用和意外的修改,从而使代码更可靠、可维护,并且具有更好的并发性。
  1. 字符串不可变性:
javascript 复制代码
const str = "Hello";
const newStr = str.toUpperCase();

console.log(str);      // 输出:"Hello"
console.log(newStr);   // 输出:"HELLO"

在这个示例中,toUpperCase 方法返回一个新的字符串,它将原始字符串的内容转换为大写。原始字符串 str 仍然保持不变,它始终是 "Hello"。这是因为字符串是不可变的,一旦创建就不能被修改。

  1. 数组不可变性:
javascript 复制代码
const arr = [1, 2, 3];
const newArr = arr.map(num => num * 2);

console.log(arr);      // 输出:[1, 2, 3]
console.log(newArr);   // 输出:[2, 4, 6]

在这个示例中,map 方法返回一个新的数组,其中每个元素都是原始数组中的元素乘以 2。原始数组 arr 保持不变,它仍然是 [1, 2, 3]。同样,这是因为数组是不可变的数据结构。

  1. 对象不可变性:
javascript 复制代码
const person = { name: "Alice", age: 30 };
const newPerson = { ...person, age: 31 };

console.log(person);      // 输出:{ name: "Alice", age: 30 }
console.log(newPerson);   // 输出:{ name: "Alice", age: 31 }

在这个示例中,使用展开运算符 ... 创建了一个浅拷贝的新对象 newPerson,其中修改了 age 属性的值。原始对象 person 仍然保持不变,它的值仍然是 { name: "Alice", age: 30 }。这是因为对象也是不可变的数据结构,一旦创建就不能被修改。

相关推荐
桦说编程12 天前
【硬核总结】如何轻松实现只计算一次、惰性求值?良性竞争条件的广泛使用可能超过你的想象!String实际上是可变的?
后端·函数式编程
Oberon1 个月前
从零开始的函数式编程(2) —— Church Boolean 编码
数学·函数式编程·λ演算
桦说编程1 个月前
CompletableFuture 超时功能有大坑!使用不当直接生产事故!
java·性能优化·函数式编程·并发编程
桦说编程1 个月前
如何安全发布 CompletableFuture ?Java9新增方法分析
java·性能优化·函数式编程·并发编程
桦说编程1 个月前
【异步编程实战】如何实现超时功能(以CompletableFuture为例)
java·性能优化·函数式编程·并发编程
鱼樱前端2 个月前
Vue3之ref 实现源码深度解读
vue.js·前端框架·函数式编程
RJiazhen2 个月前
前端项目中的函数式编程初步实践
前端·函数式编程
再思即可4 个月前
sicp每日一题[2.77]
算法·lisp·函数式编程·sicp·scheme
桦说编程4 个月前
把 CompletableFuture 当做 monad 使用的潜在问题与改进
后端·设计模式·函数式编程
蜗牛快跑2134 个月前
面向对象编程 vs 函数式编程
前端·函数式编程·面向对象编程