owned this note
owned this note
Published
Linked with GitHub
# 函數式程式設計
假設我們要將下面一組數字進行排序:
```typescript=
var input = [3, 2, 4, 1]
```
很快地,我們可能就想出以下的程式碼來達成需求:
```typescript=
function sortInput() {
// 去掉奇數
for (let i = 0; i < input.length; ++i) {
for (let j = 0; j < input.length - i - 1; ++j) {
if (input[j] > input[j + 1]) {
[input[j], input[j + 1]] = [input[j + 1], input[j]]
}
}
}
}
```
```typescript=
var input = [3, 2, 4, 1]
sortInput()
console.log(input) // [1, 2, 3, 4]
```
很快地,你可能會發現這個方法沒辦法重複使用,因為你沒辦法排序其他數列:
```typescript=
function sort(input) {
// 去掉奇數
for (let i = 0; i < input.length; ++i) {
for (let j = 0; j < input.length - i - 1; ++j) {
if (input[j] > input[j + 1]) {
[input[j], input[j + 1]] = [input[j + 1], input[j]]
}
}
}
return input
}
```
```typescript=
var input = [3, 2, 4, 1]
console.log(sort(input)) // [1, 2, 3, 4]
console.log(sort([5, 8, 7, 6])) // [5, 6, 7, 8]
```
假設,此時有一個排序,它是要降冪排序:
```typescript=
function sortBy(comparator, input) {
// 去掉奇數
for (let i = 0; i < input.length; ++i) {
for (let j = 0; j < input.length - i - 1; ++j) {
if (input[j] > input[j + 1]) {
[input[j], input[j + 1]] = [input[j + 1], input[j]]
}
}
}
return input
}
```
此時,得到一個意外的答案,奇數的陣列竟然是空的:
```typescript=
var input = [1, 2, 3, 4]
console.log(getEvenNumbers2(input)) // [2, 4]
console.log(getOddNumbers(input)) // []
```
原因是 `Array.prototype.splice`這類的函式帶有「副作用」,它會更改原始陣列,讓我們優化一下:
```typescript=
function getEvenNumbers3(input) {
// 先進行淺拷貝
input = input.slice()
for (let i = input.length - 1; i >= 0; --i)
if (input[i] % 2 === 1)
input.splice(i, 1)
return input
}
function getOddNumbers2(input) {
// 先進行淺拷貝
input = input.slice()
for (let i = input.length - 1; i >= 0; --i)
if (input[i] % 2 === 0)
input.splice(i, 1)
return input
}
```
接著測試看看:
```typescript=
var input = [1, 2, 3, 4]
console.log(getEvenNumbers3(input)) // [2, 4]
console.log(getOddNumbers2(input)) // [1, 3]
```
一切正常,但你可能會問 `getEvenNumbers3` 好像與 `getOddNumbers2` 看起來差不多?
```typescript=
function filter(predicate, input) {
// 先進行淺拷貝
input = input.slice()
for (let i = input.length - 1; i >= 0; --i)
if (predicate(input[i]) === false)
input.splice(i, 1)
return input
}
```
抽取共有的邏輯之後,我們可以得到一個 `filter` 「高階函式」,接受一個斷言函式,用以判斷是否要保留各個元素:
```typescript=
var input = [1, 2, 3, 4]
const isOdd = n => n % 2 === 1
const isEven = n => n % 2 === 0
console.log(filter(isOdd, input)) // [1, 3]
console.log(filter(isEven, input)) // [2, 4]
```
我們可以透過日常將這些函式抽取出來,來提升開發速度,也可以直接使用社群維護的函式庫,例如「Ramda」:
```typescript=
import * as R from 'ramda'
var input = [1, 2, 3, 4]
const isOdd = n => n % 2 === 1
const isEven = n => n % 2 === 0
console.log(R.filter(isOdd, input)) // [1, 3]
console.log(R.filter(isEven, input)) // [2, 4]
```
# 結論
- 提升開發速度 🚀
- 代碼可讀性高
- 降低專案體積 📦
- 避免邊夜問題
- 容易除錯追蹤
- 提升可測試性
- ...族繁不及備載