函数式编程(二):一等公民
前一篇文章()中,我们讨论了主要的编程范式及其基本差异。在本文中,我们将讨论支持函数式编程的语言中最重要的特性之一(可能是最重要的),一等公民。
1. 什么是一等公民?
当一种编程语言对函数的创建和使用没有限制时,它被认为具有一等公民特性。
一般来说,编程语言对程序元素的行为有限制。限制最少的元素被认为是一等公民。一等公民的一些权利如下:
-
可以分配给常规变量 -
作为参数传递给函数 -
作为函数的结果返回 -
包含在任何数据结构中
1.1分配给常规变量
函数可以分配给任何变量名,例如:
const string = "Foo"
const num = 2
const bool = false
const greet = (name) => `Hello ${name}`
greet('John') // Hello John
1.2作为参数传递给函数
函数可以像任何参数一样传递给函数,例如:
const nums = [1, 2, 3, 4, 5]
const addOne = (n) => n + 1
const addedOne = nums.map(addOne) // [2, 3, 4, 5, 6]
我们有一个addOne函数,它被视为变量并传递给map函数。也就是说,addOne函数确实是一个一等公民。
1.3作为函数的结果返回
函数可以返回另一个函数,例如:
const makeCounter = () => {
let count = 0
return () => ++count
}
const counter = makeCounter()
counter() // 1
counter() // 2
counter() // 3
counter() // 4
makeCounter函数返回一个函数,我们将其分配给counter变量。
1.4包含在任何数据结构中
当函数可以存储在任何数据结构中时,例如:
const wakeUp = name => `${name}, wake up early!`
const takeShower = name => `${name}, take shower!`
const workout = name => `${name}, workout!`
const shutUp = name => `${name}, shut up!`
const morningRoutines = [
wakeUp,
takeShower,
workout,
shutUp
]
morningRoutines.forEach(routine => routine('John'))
// John, wake up early!
// John, take shower!
// John, workout!
// John, shut up!
我们可以将函数存储在数组中,正如您所想的,我们也可以将它们存储在对象中。
2.为什么一等公民很重要
函数式编程(FP)深受数学的影响。函数式编程希望每一行代码都包含数学知识。虽然数学只是用函数和变量建立起来的,但它仍然非常强大,表达能力也很强。这就是函数式编程想要做的。只使用一个个函数解决每一个问题(这也是老大哥(数学)擅长的)。这就是为什么函数的自由(使其成为一等公民)很重要的原因。当你可以把编程语言中的函数当作一个变量来处理时,这种语言会更加灵活,并且有很多改进的空间。正如我们前面提到的,函数式编程将使您的代码更加可预测、可测试、可重用、可定制、可缓存、可维护、可组合和可读。
2.1或许,你会问自己:“好吧,我理解函数式编程和数学之间的关系,但是一等公民如何使所有这些好处成为现实?”
好问题。既然函数式编程完全依赖于函数的自由度。一等公民是所有函数式编程概念的基石,一旦你有了基石,你就可以在上面构建这些很棒的概念(本系列接下来的部分中将尝试这样做)。
2.2在编程语言中拥有一等公民,就有可能拥有一些很棒的模式,例如:
2.2.1.高阶函数
当函数将函数作为参数(如大多数数组函数.map
, .filter
, .reduce
, .every
)或函数作为结果返回时,它们被视为高阶函数。
2.2.2.闭包
闭包是由“父”函数返回的函数,可以访问父函数的内部状态。就像我们前面的makeCounter示例一样。为了更详细地阐述,让我们再举一个例子:
const add = (x) => (y) => x + y
const add5 = add(5) // add5 = (y) => 5 + y
const add10 = add(10) // add10 = (y) => 10 + y
add5(1) // 6
add10(1) // 11
让我们逐行解释:
第1行:add是一个接受第一个参数x的函数,返回一个接受第二个参数y的匿名函数,并返回x+y。
第2行:执行add(5)将返回一个包含5的函数。编译器将编译为如下:
const add5 = (y) => 5 + y
第3行:和第2行一模一样。执行add(10)将返回一个包含10的函数。编译器将编译为如下:
const add10 = (y) => 10 + y
第4行和第5行:它们只是对之前“动态”创建的add5和add10函数的普通调用。
好的,很酷。了解每一行的功能后,那么add、add5和add10的实际技术术语是什么:
-
add是一个高阶函数。为什么?因为它返回一个函数 -
但是add5和add10是闭包。为什么?因为它们的值5和10分别被封闭(绑定)在其父词法范围内,并且仍然可以被它们访问。
注:闭包和高阶函数之间的关系就像父函数和子函数一样,如果没有高阶函数,我们将无法得到闭包
2.2.3 柯里化
后面的文章我们会详细讨论…
3.总结
一等公民不是一种模式,它只是编程语言中的一种特性。该特性将函数视为变量,并在不受限制的情况下工作。拥有这一特性将使该语言更强大。这使得,我们可以使用该特性构建强大的程序,比如高阶函数、闭包、curry等等…
译自:https://blog.bitsrc.io/functional-programming-part-1-first-class-functions-791103984dfb
少爷,点个关注和在看呗!