yo! what's up!
本篇文章会简单地介绍基本的 Functional Programming 概念,这些概念不仅重要,更是贯穿了之後的主题。
给定相同的输入,一定会得到相同的输出。并且不会产生任何 Side Effect.
那我们就来探讨一下这段纯函式(pure function)的定义,
举例来说, Math.random
Math.random() // 0.06243529219776711
Math.random() // 0.9436551613058293
Math.random() // 0.29082104009990295
Math.random
有什麽问题呢?可以看到给定同样的输入,却是不同的输出,这就是不纯的函式(impure 函式)。
满足纯函式的要点之一,就是必须是在相同输入下,无论重复呼叫多少次,输出的结果永远是一样的,举 square square为例,只要给定特定的值,就会回传该值的平方。
const square = num => Math.pow(num, 2);
square(2) // 4
square(2) // 4
square(2) // 4
数学中的函式就像是两个集合的对应关系,且集合内的数皆可以在另外一个集合找到对应的唯一值。而纯函式就是数学中的函式,必须是一对一或是多对一的关系。
Side Effect 就是当呼叫函式时也改变外部物件的状态,我们就会称这个函式有Side Effect.
而什麽是改变外部状态,举以下几个例子
let state = 0;
const counter = () => {
state += 1;
return state;
}
counter(); // 1
counter(); // 2
counter(); // 3
counter(); // 4
上面范例的counter 就是一个有side effect 的函式,因为我们因为呼叫了 counter 时也改变外部的状态。当一个有程序四处散落着 side effect 的函式时,那程序变得非常不可控。
又或是在函式内进行console.log
const doWhat = (x) => {
console.log(`hahaha, ${x}`)
return x;
}
doWhat()
这个范例则是我们在呼叫 doWhat
时,同时也在 console
写入了一些资讯,这也是一个有 Side Effect 的函式。
而会导致Side Effect 的操作,笔者列了几项,
Wait..., 读者们可能心想,在现实开发中,不使用上述这些会产生 Side Effect 的功能,还能进行开发吗? 对,基本上是开发不了任何东西的。所以FP 该不会等於 no code!
不不不
FP 不是不能有Side Effect,而是将这些Side Effect用一些方式包覆起来,将 Effectful program 跟 Pure function 有个明确的界线进行划分。在之後章节我们也会提到FP是如何解决。
为什麽纯函数这个概念这麽重要? 因为它直接确保 referential transparency 的概念!
首先我们先来看看 referential transparency 的定义
An expression is called referentially transparent if it can be replaced with its corresponding value (and vice-versa) without changing the program's behavior. - wiki
举例来说,
const x = square(2);
上述提到的范例square ,因为它是有着 referential transparency 的特性,所以我们可以将直接用4 取代
const x = 4;
此概念虽然简单,笔者们看了这个范例或许心想 So What?
但 referential transparency 确保了
就如同刚刚上述提到的特性,当程序是由众多纯函数组成时,不用担心我们影响外部的任何状态,也可以确保程序执行过程中,不会产生非预期地结果。
纯函式也确保了我们在测试时,不用去建造一个真实的环境去测试整个 program, 只需要给定 input 并预期其结果是正确的就好。
由於纯函式的关系,确保了每个输入对应唯一的输出值,这就代表我们可以进行快取,举最知名的 fibonacci number cache 过後的 fib函式与原本的 fib 的差别就是可以减少重复计算。
const memo = f => {
const cache = {};
const memoized = n => {
if(!cache[n]) {
cache[n] = f(n);
}
return cache[n]
}
return memoized;
}
const fib = n => {
if(n === 0) return 0;
if(n === 1) return 1;
return fib(n-2) + fib(n-1)
}
const memoFib = memo(fib)
memoFib(10) // 55
memoFib(10) // cache 後的 55
尽管这些概念都是基本的概念,但其概念却是贯穿着函数式编程,明天将介绍 Currying vs. Partial Application.
>>: Day 02 - 环境安装(中) Docker & MySQL
预计三十天内学会制作一个2D游戏,如果还有时间就继续练习一个3D游戏。 开始先做一个2D的卷轴射击游...
今天的 TextField 和明天的 FormControl 都是在介绍跟表单有关的介面和元件,而...
路上捡到猫 | 要取什麽名字? | 很急>< 在线等! 🐄点此填写今日份随堂测验 ...
今天的盘又是一个开高走低的情况 最近的盘真的是有够难做,作股票最讨厌的就是遇到这种情况。建议想入场的...
今天来探讨裸机Hyperviser在近几年朝大众化的原因 摆脱固有平台 受够了各大云端运算服务的绑手...