vlambda博客
学习文章列表

函数式编程(二):一等公民



前一篇文章()中,我们讨论了主要的编程范式及其基本差异。在本文中,我们将讨论支持函数式编程的语言中最重要的特性之一(可能是最重要的),一等公民。

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 = [12345]

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


                                                         少爷,点个关注和在看呗!