Home > Article > Web Front-end > Introduction and summary of functional programming (with code)
Recently, I have been reading the introductory classics of functional programming. I have summarized it myself. If you are interested or need it, you can take a look.
1. What is functional programming
Functional programming is mainly based on mathematical functions and its ideas, so let’s first review the functions in mathematics
y = f(x)
That is, the function f(x) takes x as the parameter and y as the result. x and y can be any numbers, which contains several key points:
1. The function must always receive a parameter
2. The function must always return a value
3. The function should run according to the received parameter (such as x), not the external environment
4. For a given x, will only output the only y
Let us understand from an example
// 一个计税函数 var percentValue = 5; var calculateTax = (value) => {return value/100 * (100 + percentValue)};
Use functions in mathematics to analyze this function, first look at the third item, the function should be based on the received parameters rather than the external environment. The function calculateTax here relies on the external percentValue. Therefore, this function cannot be called a function in the mathematical sense. So how can we transform it into a function in the mathematical sense? Very simple
var calculateTax = (value,percentValue) => {return value/100 * (100 percentValue)};
Now this function can be called a real function .
Now let’s define functional programming in simple technical terms: Functional programming is a paradigm in which a function completes its own logic relying solely on input and output. This ensures that the function returns the same result when called multiple times. The function does not change any external environment variables.
2. Reference transparency
According to the definition of the function, we can conclude that all functions will return the same value for the same input and output. This property of a function is called referential transparency
As an example
var identity = (i) => { return i };
We define a simple function that does not rely on global variables inside the function, it will simply return the input. Now suppose it is applied between other function calls like
sum(4,5) + identity(1);
According to the definition of referential transparency, we can convert it to
sum(4,5) + 1;
This process is called replacement model, so you The result of the function can be directly replaced (mainly because the function's logic does not depend on other global variables).
Since the function returns the same value for a given input result, we can actually cache it. For example, we have a function "factorial" to calculate factorial. It takes an argument to calculate its factorial, for example the factorial of 5 is 120. When the user enters the factorial of 5 for the second time, due to reference transparency (the same result is returned for the same input), we know that the result is 120, but the machine does not know it. We need to let the machine cache the result so that future calls can directly return the result. Instead of having to calculate it again. This shows the importance of referential transparency and cacheable code in functional programming.
3. Functional, declarative and abstract
Functional programming advocates declarative programming and writing abstract code
What is declarative programming
Suppose we want to print out all the elements in an array, we can use the following method
var array = [1,2,3]; for(let i = 0; i < array.length; i++){ console.log(array[i]) }
In this code, we tell the code exactly how to do it. Such as: getting the length of the array, looping the array, and getting each element by index. This is imperative programming. Imperative programming advocates telling the compiler what to do.
Let’s look at another way
var arr = [1,2,3]; arr.forEach((ele) => { console.log(ele) })
In the above code, we have removed getting the array length, looping the array, using index to get the array elements, etc. We only care about what to do (i.e. printing array elements), getting the array length, looping, etc. are all done by the machine for us. We only need to care about what to do, not how to do it. This is declarative programming
Functional programming Programming advocates creating functions in an abstract manner that can be reused elsewhere in the code.
4. Pure function
What is a pure function? A pure functional function returns the same output for a given input. For example, the double above
var double = (value) => value * 2;
is a pure function because it always returns the same output for the same input. Pure functions follow referential transparency, so we can simply replace double(5) with 10. So what’s the cool thing about pure functions? Let’s take a look
1.4.1 Pure functions produce testable code
Impure functions have side effects. Let’s take the previous tax calculation function as an example to illustrate this function
var percentValue = 5; var calculateTax = (value) => {return value/100 * (100 + percentValue)};
It is not a pure function, mainly because it depends on the external environment to calculate its logic. When the external environment changes, it will affect the result. Therefore, the main characteristic of a pure function is that it does not depend on any external variables and should not change any external variables. If you change external variables, it may cause changes in the behavior of other functions, that is, produce side effects, which can make the behavior of the system unpredictable.
1.4.2 Reasonable code
We should infer the function of the function through its name, such as double function
var double = (value) => value * 2;
我们可以通过函数名轻易的推出这个函数会把给定的数值加倍,因此根据引用透明性,我们可以直接把 double(5) 替换成 10。还有一个例子,Math.max(3,4,5,6) 结果是什么?虽然我们只看到了函数的名字,但是我们很容易看出结果,我们看到实现了吗?并没有,为什么,就是因为 Math.max 是纯函数啊!!!
5、 并发代码
纯函数允许我们并发的执行代码,因为纯函数不会改变它的环境,这意味着我们根本不需要担心同步问题。当然,js 是单线程的,但是如果项目中使用了 webworker 来并发执行任务,该怎么办?或者有一段 Node 环境中的服务端代码需要并发的执行函数,又该怎么办呢?
// 非纯函数代码 let global = 'something' let function1 = (input) => { // 处理 input // 改变 global global = "somethingElse" } let function2 = () => { if(global === "something"){ // 业务逻辑 } }
如果我们需要并发的执行 function1 和 function2,假设 function1 在 function2 之前执行,就会改变 function2 的执行结果,所以并发执行这些代码就会造成不良的影响,现在把这些函数改为纯函数。
let function1 = (input,global) => { // 处理 input // 改变 global global = "somethingElse" } let function2 = (global) => { if(global === "something"){ // 业务逻辑 } }
此处我们把 global 作为两个函数的参数,让它们变成纯函数,这样并发执行的时候就不会有问题了。
6、可缓存
既然纯函数对于给定的输入总能返回相同的输出,那么我们就能缓存函数的输出,例如
var doubleCache = (value) => { const cache = {}; return function(value){ if(!cache[value]){ cache[value] = value * 2 console.log('first time') } return cache[value]; } } var double = doubleCache(); double(2) // first time,4 double(2) // 4 // 或者直接使用立即执行函数 var double = ((value) => { const cache = {}; return function(value){ if(!cache[value]){ cache[value] = value * 2 console.log('first time') } return cache[value]; } })() double(2) // first time,4 double(2) // 4
这个函数中,假设我们第一次输入 5,cache 中并没有,于是执行代码,由于闭包的存在,cache[5] = 10,第二次我们调用的时候,cache[5] 存在,所以直接 return 10,看到了吗?这就是纯函数的魅力!!!别忘记这是因为纯函数的引用透明性。
7、 管道与组合
纯函数应该被设计为一次只做一件事,并且根据函数名就知道它所做的事情。
比如 linux 系统下有很多日常任务的命令,如 cat 用于打印文件内容,grep 用于搜索文件,wc 用于计算行数,这些命令一次只解决一个问题,但是我们可以用管道或组合来完成复杂的任务。假设我们需要在一个文件中找到一个特定的名称并统计它的出现次数,在命令行要输入如下指令
cat jsBook | grep -i “composing” | wc
上面的命令通过组合多个函数解决了我们的问题。组合不是 linux 命令独有的,它们是函数式编程范式的核心。
我们把它们称为函数式组合。来看一个 compose 函数的例子
var add1 = (value) =>{ return value+1 }; var double = (value) => {return value*2 }; var compose = (a,b) => { return (c) => { return a(b(c)); } } var doubleAndAdd1 = compose(add1,double); doubleAndAdd1(5) // 打印 5 * 2 + 1 = 11
compose 函数返回一个函数,将 b 的结果作为 a 的参数,这里就是将 double 的结果作为 add1 的参数,来实现了函数的组合。
8、 纯函数是数学函数
还记得我们之前的缓存函数吗,假设我们多次调用 double 对象,那么 cache 中就会变成这样
{ 1: 2, 2: 4, 3: 6, 4: 8, 5: 10 }
假设我们设置 double 的输入范围限制为 1 - 5,而且我们已经为这个范围建立的 cache 对象,因此只要参照 cache 就能根据指定输入返回指定输出。也就是一一对应的关系。
那么数学函数的定义是什么呢?
在数学中,函数是一种输入集合和可允许的输出集合之间的关系,具有如下属性:每个输入都精确地关联一个输出。函数的输入称为参数,输出称为值。对于一个给定的函数,所有被允许的输入集合称为该函数的定义域,而被允许的输出集合称为值域。
上面的定义和纯函数完全一致,例如在 double 中,你能找到定义域和值域吗?当然可以!通过这个例子,可以很容易看到数学函数的思想被借鉴到函数式范式的世界
9、 我们要做什么?
我们将通过学习,构建出一个 ES6-Functional 的函数式库,通过构建的过程,我们将理解如何使用 JavaScript 函数,以及如何在日常工作中应用函数式编程。
10、小结
这一节我们只是简单的介绍了函数式编程的概念,以及什么是纯函数,其中最重要的就是引用透明性。然后研究了几个短小的例子,通过例子来加深对函数式编程的理解。接下来我们将一步一步深入了解函数式编程。
The above is the detailed content of Introduction and summary of functional programming (with code). For more information, please follow other related articles on the PHP Chinese website!