函数式编程思想

相关问题

什么是纯函数?为什么它们在函数式编程中很重要?

纯函数是一种函数,其返回值仅由其输入参数决定,不产生任何可观察的副作用,如修改全局对象或外部状态。纯函数具有以下特性:

  • 确定性:相同的输入永远得到相同的输出。
  • 无副作用:函数执行过程中不会改变任何外部状态。

纯函数在函数式编程中非常重要,因为它们提供了可预测性和可测试性。由于纯函数不依赖于且不修改外部环境的状态,它们在并发环境下特别有用,可以减少在多线程环境中由状态改变引起的错误。此外,纯函数的可组合性使得开发者能够构建更加模块化和可重用的代码。

解释函数式编程中的高阶函数并给出一个例子

高阶函数是至少满足下列一个条件的函数:

  • 接受一个或多个函数作为输入。
  • 返回一个函数作为输出。

高阶函数在函数式编程中用于抽象或隔离行为,不直接操作数据,而是创建包含操作的函数,这增强了代码的可重用性和模块化。例如,.map()和.filter()都是数组的高阶函数,因为它们接受一个函数作为参数。

函数式编程思想

函数式编程思想vs 面向对象编程思想

  • 面向对象编程(00P)是一种强大的编程范式,它使用类和对象来组织代码,以模拟现实世界的实体和交互。尽管 OOP 在许多方面非常有用,例如在编写大型软件系统时提供了清晰的模块化结构,但它也存在一些问题,特别是与状态管理和副作用相关的问题。
  • 函数式编程,关注的是函数,我们在封装设计的时候,要将问题的求解方法抽离到各个函数中,通过**函数组合 **提供问题的解法

面向对象编程存在的问题

  • 状态管理复杂
    • 在OOP 中,对象通常包含状态(数据),状态可以通过方法(行为)来改变。当应用程序复杂时,对象的状态可能在多个方法中被不同的方式改变,导致状态管理变得复杂和困难。
  • 副作用和可预测性
    • 对象方法可能会改变全局状态或者对象的内部状态,这些副作用会使得程序的行为变得不可预测和难以理解。
  • 继承的问题
    • 继承可以导致高度耦合的代码,使得修改和理解代码更加困难。子类依赖于父类的实现细节,父类的改动可能会影响到所有子类。

:::info 面向对象状态管理和UI逻辑,复用性差

副作用,不可预测

继承

:::

函数式编程思想能够解决什么问题

  • 不可变性
    • 数据结构是不可变的,这意味着一旦创建,数据就不能更改。所有的变化都通过返回新的数据副本来实现。
  • 无副作用的函数
    • 函数不修改外部状态,它们只依赖于输入参数,返回新的数据而不是修改已有数据
  • 使用纯函数
    • 纯函数增加了代码的可测试性和可预测性。

函数式编程核心概念

  • 函数为一等公民
  • 纯函数(Pure Functions)
  • 不可变性 (Immutability)
  • 函数组合 (Function Composition)
  • 高阶函数(Higher-Order Functions)

函数为一等公民 (First-Class Functions)

:::info 当函数在一个语言中被视为一等公民时,这意味着函数可以像任何其他数据类型一样被传递和操作。它们可以作为变量存储、作为参数传递给其他函数、作为其他函数的返回值或者赋值给对象的属性。

:::

纯函数 (Pure Functions)

:::info 纯函数是这样一种函数,它的返回值仅由其输入参数决定,并且在执行过程中不产生副作用(如修改外部变量或对象状态)

:::

不可变性(Immutability)

:::info 不可变性指的是数据状态一旦创建,就不能被改变。在函数式编程中,任何数据变更都应通过创建和返回新的数据副本来实现,而不是直接修改原始数据。

:::

函数组合 (Function Composition)

:::info 函数组合是将两个或多个函数组合成一个单一函数的过程。在组合中,一个函数的输出成为另一个函数的输入。

:::

高阶函数(Higher-Order Functions)

:::info 高阶函数是指至少满足以下一个条件的函数:接受一个或多个函数作为参数,或者返回一个函数作为结果。

:::

纯函数

什么是纯函数?

:::color3 相同的入参,得到相同的结果,且无副作用

:::

纯函数是这样一种函数,即相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。

比如数组的 slice 和 splice,这两个函数的作用并无二致---一但是注意,它们各自的方式却大不同,但不管怎么说作用还是一样的。

  • slice 符合纯函数的定义:因为对相同的输入它保证能返回相同的输出;
  • splice 却不同:会产生可观察到的副作用,即这个数组永久地改变了;

副作用

"副作用"的关键部分在于"副"。就像一潭死水中的"水"本身并不是幼虫的培养器,"死"才是生成虫群的原因。同理,副作用中的"副"是滋生 bug的温床。

副作用是在计算结果的过程中,系统状态的一种变化,或者与外部世界进行的可观察的交互。副作用可能包含,但不限于:

  1. 更改文件系统;

  2. 往数据库插入记录;

    3.发送一个 http 请求;

  3. 可变数据;

  4. 打印/log;

  5. 获取用户输入;

  6. DOM 查询;

  7. 访问系统状态...

概括来讲,只要是跟函数外部环境发生的交互就都是副作用---一这一点可能会让你怀疑无副作用编程的可行性。函数式编程的哲学就是假定副作用是造成不正当行为的主要原因。

这并不是说,要禁止使用一切副作用,而是说,要让它们在可控的范围内发生。

副作用让一个函数变得不纯是有道理的:从定义上来说,纯函数必须要能够根据相同的输入返回相同的输出;如果函数需要跟外部事物打交道,那么就无法保证这一点了。

纯函数的特点

可缓存性(Cacheable)

  • 首先,纯函数总能够根据输入来做缓存。实现缓存的一种典型方式是 memoize 技术

可移植性 / 自文档化 (Portable / Self-Documenting)

  • 纯函数是完全自给自足的,它需要的所有东西都能轻易获得。仔细思考思考这一点⋯.这种自给自足的好处是什么呢?

可测试性(Testable)

  • 纯函数让测试更加容易。我们不需要伪造一个"真实的"支付网关,或者每一次测试之前都要配置、之后都要断言状态 (assert the state)。只需简单地给函数一个输入,然后断言输出就好了。

合理性(Reasonable)

  • 很多人相信使用纯函数最大的好处是引用透明性 (referential transparency)。如果一段代码可以替换成它执行所得的结果,而且是在不改变整个程序行为的前提下替换的,那么我们就说这段代码是引用透明的。

并行代码

  • 我们可以并行运行任意纯函数
  • 因为纯函数根本不需要访问共享的内存,而且根据其定义,纯函数也不会因副作用而进入竞争态(race condition).
  • 并行代码在服务端js 环境以及使用了 web worker 的浏览器那里是非常容易实现的,因为它们使用了线程 (thread)。不过出于对非纯函数复杂度的考虑,当前主流观点还是避免使用这种并行。

函数柯里化

:::color3 将多参数转为一系列单参数函数的过程

:::

柯里化是函数式编程中的一个技术,它涉及将一个多参数的函数转换成一系列使用一个参数的函数。柯里化的函数通常返回另一个接受剩余参数的函数,这个过程一直持续,直到所有参数都被消耗掉。

柯里化的用途

  • 参数复用:柯里化可以创建可以被多次调用的函数,每次调用使用部分参数来执行任务,这有助于減少重复代码和提高代码复用
  • 延迟计算:通过柯里化,函数的执行可以被延迟。只有当接收到所有必要的参数之后,才会进行计算
  • 动态生成函数:可以根据柯里化的中间步骤动态生成新的函数,这些函数可以适用于特定的情况,从而增强了函数的适应性和灵活性
  • 简化函数的调用:柯里化有助于减少函数调用时需要的参数数量,使得函数调用更加简洁

函数组合

函数式编程中的函数组合(Function Composition)是一个核心概念,它允许我们将多个函数链接在一起,创建出新的函数。这种方法的强大之处在于它提供了一种模块化方式来构建程序,使得每个部分都可以独立测试和重用。

特性

  • 模块性:通过将小而专一的函数组合成复杂的行为,增强代码的模块性。
  • 可读性:适当的函数组合可以使代码更加直观和易于理解。
  • 复用性:独立的函数可以在多个地方被复用,減少代码重复。
  • 声明性:通过组合方式,代码更加声明性,聚焦于"做什么"而非"怎么做"。

不可变性

在函数式编程中,不可变性是一个核心概念,指的是数据一旦被创建就不能改变。任何对数据的修改或更新操作都会生成一个新的数据副本,而不是改变原有的数据。这种做法有助于避免副作用和数据状态的不一致性,使得程序的行为更可预测,简化了复杂程序中的状态管理。这一特性在 React 中用途颇广。单向数据流状态的更改需要我们使用这一特性来实现。

不可变性的优势

提高程序的可预测性:由于数据不会被意外修改,函数的行为和输出更加可预测。

简化并发编程:不可变数据结构自然是线程安全的,因为没有线程可以修改数据,从而避免了锁和竞态条件的需求。

便于历史数据追踪:保持历史版本的数据不被改变,可以轻松地实现撤销和重做功能。

React 中的不可变性

在 React 中,组件的状态应当是不可变的。这意味着在更新状态时,你应该创建状态的一个新副本而不是直接修改状态。这种做法有助于优化 React 应用的性能,尤其是在利用 shouldComponentUpdate、PureComponent 或 React.memo等性能优化手段时,因为 React 可以快速地检查 props 或 state 是否发生变化,从而決定是否需要重新渲染组件。

Immer 使用

Immer 是一个流行的库,用于简化不可变数据结构的处理。它允许你编写看似"可变"的代码,但实际上会产生不可变的数据更新。在 React 中,使用Immer 可以简化复杂状态的更新逻辑。

相关资料

相关推荐
崔庆才丨静觅9 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606110 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了10 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅10 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅11 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment11 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅11 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊11 小时前
jwt介绍
前端
爱敲代码的小鱼11 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax