函数式编程简介 (Introduction to Functional Programming)
Functional Programming (FP) is a programming paradigm with some particular techniques.
函数式编程(FP)是具有某些特定技术的编程范例。
In programming languages, you’ll find purely functional programming languages as well as programming languages that support functional programming techniques.
在编程语言中,您会发现纯函数式编程语言以及支持函数式编程技术的编程语言。
Haskell, Clojure and Scala are some of the most popular purely functional programming languages.
Haskell,Clojure和Scala是一些最受欢迎的纯函数式编程语言。
Popular programming languages that support functional programming techniques are JavaScript, Python, Ruby and many others.
支持功能编程技术的流行编程语言是JavaScript ,Python,Ruby和许多其他语言。
Functional Programming is not a new concept, actually its roots go back o the 1930’s when lamda calculus was born, and has influenced many programming languages.
函数式编程不是一个新概念,实际上其起源可以追溯到lamda演算的1930年代,它影响了许多编程语言。
FP has been gaining a lot of momentum lately, so it’s the perfect time to learn about it.
FP最近获得了很大的发展动力,所以现在是学习它的最佳时机。
In this course I’ll introduce the main concepts of Functional Programming, by using in the code examples JavaScript.
在本课程中,我将通过在代码示例中使用JavaScript来介绍函数式编程的主要概念。
一流的功能 (First class functions)
In a functional programming language, functions are first class citizens.
在函数式编程语言中,函数是一等公民。
可以将它们分配给变量 (They can be assigned to variables)
const f = (m) => console.log(m)
f('Test')
Since a function is assignable to a variable, they can be added to objects:
由于函数可以分配给变量,因此可以将它们添加到对象中:
const obj = {
f(m) {
console.log(m)
}
}
obj.f('Test')
as well as to arrays:
以及数组:
const a = [
m => console.log(m)
]
a[0]('Test')
它们可以用作其他函数的参数 (They can be used as an argument to other functions)
const f = (m) => () => console.log(m)
const f2 = (f3) => f3()
f2(f('Test'))
它们可以通过函数返回 (They can be returned by functions)
const createF = () => {
return (m) => console.log(m)
}
const f = createF()
f('Test')
高阶函数 (Higher Order Functions)
Functions that accept functions as arguments or return functions are called Higher Order Functions.
接受函数作为参数或返回函数的函数称为高阶函数 。
Examples in the JavaScript standard library include Array.map()
, Array.filter()
and Array.reduce()
, which we’ll see in a bit.
JavaScript标准库中的示例包括Array.map()
, Array.filter()
和Array.reduce()
,我们将稍后介绍。
声明式编程 (Declarative programming)
You may have heard the term “declarative programming”.
您可能已经听说过“声明式编程”一词。
Let’s put that term in context.
让我们将该术语放在上下文中。
The opposite of declarative is imperative.
声明 式的反义性 势在必行 。
声明式与命令式 (Declarative vs Imperative)
An imperative approach is when you tell the machine (in general terms), the steps it needs to take to get a job done.
当您告诉机器(一般而言)时,完成工作所需采取的步骤是当务之急。
A declarative approach is when you tell the machine what you need to do, and you let it figure out the details.
声明性方法是当您告诉机器您需要做什么时,让它弄清楚细节。
You start thinking declarative when you have enough level of abstraction to stop reasoning about low level constructs, and think more at a higher UI level.
当您具有足够的抽象级别来停止对低级构造的推理时,便开始考虑声明式,而在较高的UI级别上进行更多思考。
One might argue that C programming is more declarative than Assembly programming, and that’s true.
有人可能会说C编程比Assembly编程更具声明性,这是事实。
HTML is declarative, so if you’ve been using HTML since 1995, you’ve actually being building declarative UIs since 20+ years.
HTML是声明性的,因此,如果您从1995年开始就使用HTML,那么实际上您已经从20多年开始构建了声明性UI。
JavaScript can take both an imperative and a declarative programming approach.
JavaScript可以采用命令式和声明式编程方法。
For example a declarative programming approach is to avoid using loops and instead use functional programming constructs like map
, reduce
and filter
, because your programs are more abstract and less focused on telling the machine each step of processing.
例如,声明性编程方法是避免使用循环 ,而应使用诸如map
, reduce
和filter
类的功能性编程构造,因为您的程序更加抽象,并且较少关注于告诉机器的每个处理步骤。
不变性 (Immutability)
In functional programming data never changes. Data is immutable.
在功能编程中,数据永不变。 数据是不可变的 。
A variable can never be changed. To update its value, you create a new variable.
变量永远不能更改。 要更新其值,请创建一个新变量。
Instead of changing an array, to add a new item you create a new array by concatenating the old array, plus the new item.
要添加新项,而不是更改数组,您可以通过串联旧数组和新项来创建新数组。
An object is never updated, but copied before changing it.
对象从不更新,但在更改之前先进行复制。
const
(const
)
This is why the ES2015 const
is so widely used in modern JavaScript, which embraces functional programming concepts: to enforce immutability on variables.
这就是为什么ES2015 const
在现代JavaScript中如此广泛地被使用的原因,它包含了功能性编程概念:对变量实施不变性。
Object.assign()
(Object.assign()
)
ES2015 also gave us Object.assign()
, which is key to creating objects:
ES2015还为我们提供了Object.assign()
,这是创建对象的关键:
const redObj = { color: 'red' }
const yellowObj = Object.assign({}, redObj, {color: 'yellow'})
concat()
(concat()
)
To append an item to an array in JavaScript we generally use the push()
method on an array, but that method mutates the original array, so it’s not FP-ready.
为了将项目附加到JavaScript中的数组中,我们通常在数组上使用push()
方法,但是该方法会更改原始数组,因此不支持FP。
We instead use the concat()
method:
相反,我们使用concat()
方法:
const a = [1, 2]
const b = [1, 2].concat(3)
// b = [1, 2, 3]
or we use the spread operator:
或者我们使用点差运算符 :
const c = [...a, 3]
// c = [1, 2, 3]
filter()
(filter()
)
The same goes for removing an item from an array: instead of using pop()
and splice()
, which modify the original array, use array.filter()
:
从数组中删除项目也是如此:不要使用pop()
和splice()
来修改原始数组,而要使用array.filter()
:
const d = a.filter((v, k) => k < 1)
// d = [1]
纯度 (Purity)
A pure function:
一个纯函数 :
- never changes any of the parameters that get passed to it by reference (in JS, objects and arrays): they should be considered immutable. It can of course change any parameter copied by value 永远不要更改通过引用传递给它的任何参数(在JS,对象和数组中):应将它们视为不可变的。 当然,它可以更改按值复制的任何参数
- the return value of a pure function is not influenced by anything else than its input parameters: passing the same parameters always result in the same output 纯函数的返回值不受其输入参数的影响:传递相同的参数始终会导致相同的输出
- during its execution, a pure function does not change anything outside of it 在执行过程中,纯函数不会更改其外部的任何内容
数据转换 (Data Transformations)
Since immutability is such an important concept and a foundation of functional programming, you might ask how can data change.
由于不变性是一个非常重要的概念,也是函数式编程的基础,因此您可能会问如何改变数据。
Simple: data is changed by creating copies.
简单: 通过创建副本即可更改数据 。
Functions, in particular, change the data by returning new copies of data.
函数特别是通过返回新的数据副本来更改数据。
Core functions that do this are map and reduce.
做到这一点的核心功能是map和reduce 。
Array.map()
(Array.map()
)
Calling Array.map()
on an array will create a new array with the result of a function executed on every item of the original array:
在数组上调用Array.map()
将创建一个新数组,该函数的结果将在原始数组的每个项目上执行:
const a = [1, 2, 3]
const b = a.map((v, k) => v * k)
// b = [0, 2, 6]
Array.reduce()
(Array.reduce()
)
Calling Array.reduce()
on an array allows us to transform that array on anything else, including a scalar, a function, a boolean, an object.
在数组上调用Array.reduce()
允许我们将数组转换成其他任何形式,包括标量,函数,布尔值,对象。
You pass a function that processes the result, and a starting point:
您传递一个处理结果的函数和一个起点:
const a = [1, 2, 3]
const sum = a.reduce((partial, v) => partial + v, 0)
// sum = 6
const o = a.reduce((obj, k) => { obj[k] = k; return obj }, {})
// o = {1: 1, 2: 2, 3: 3}
递归 (Recursion)
Recursion is a key topic in functional programming. when a function calls itself, it’s called a recursive function.
递归是函数式编程中的关键主题。 当一个函数调用自身时 ,它称为递归函数 。
The classic example of recursion is the Fibonacci sequence (N = (N-1 + N-2)) calculation, here in its 2^N totally inefficient (but nice to read) solution:
递归的经典示例是斐波那契数列(N =(N-1 + N-2))计算,此处使用其2 ^ N完全无效(但易于阅读)的解决方案:
var f = (n) => n <= 1 ? 1 : f(n-1) + f(n-2)
组成 (Composition)
Composition is another key topic of Functional Programming, a good reason to put it into the “key topics” list.
组合是函数式编程的另一个关键主题,有充分的理由将其纳入“关键主题”列表。
Composition is how we generate a higher order function, by combining simpler functions.
合成是我们通过组合更简单的函数来生成高阶函数的方式 。
用普通JS编写 (Composing in plain JS)
A very common way to compose functions in plain JavaScript is to chain them:
在普通JavaScript中组成函数的一种非常常见的方法是将它们链接起来:
obj.doSomething()
.doSomethingElse()
or, also very widely used, by passing a function execution into a function:
或者,也非常广泛地使用,通过将函数执行传递给函数:
obj.doSomething(doThis())
在lodash
的帮助下进行lodash
(Composing with the help of lodash
)
More generally, composing is the act of putting together a list of many functions to perform a more complicated operation.
更一般地,组合是将许多功能的列表放在一起以执行更复杂的操作的动作。
lodash/fp
comes with an implementation of compose
: we execute a list of functions, starting with an argument, each function inherits the argument from the preceding function return value. Notice how we don’t need to store intermediate values anywhere.
lodash/fp
带有compose
的实现:我们执行一个函数列表,从一个参数开始, 每个函数都从前面的函数返回值继承该参数 。 注意,我们不需要在任何地方存储中间值。
import { compose } from 'lodash/fp'
const slugify = compose(
encodeURIComponent,
join('-'),
map(toLowerCase),
split(' ')
)
slufigy('Hello World') // hello-world
翻译自: https://flaviocopes.com/javascript-functional-programming/