# 函数式编程
# pipe
pipe
的作用是按顺序从左到右串联起一系列 一元函数,并最终返回一个函数
// 使用展开符来获取数组格式的 pipe 参数
function pipe(...funcs) {
function callback(input, func) {
return func(input)
}
return function (param) {
return funcs.reduce(callback, param)
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
使用
function add4(num) {
return num + 4
}
function multiply3(num) {
return num * 3
}
function divide2(num) {
return num / 2
}
const compute = pipe(add4, multiply3, divide2)
// 输出 21
console.log(compute(10))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Compose
Compose
的作用是按顺序从右到左串联起一系列 一元函数,并最终返回一个函数
// 使用展开符来获取数组格式的 pipe 参数
function compose(...funcs) {
function callback(input, func) {
return func(input)
}
return function (param) {
return funcs.reduceRight(callback, param)
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
使用
function add4(num) {
return num + 4
}
function multiply3(num) {
return num * 3
}
function divide2(num) {
return num / 2
}
const compute = compose(add4, multiply3, divide2)
// 输出 19
console.log(compute(10))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Function(函子)
Functor
指的是一个实现了 map
方法的数据结构。
# 1. Identity Functor
const Identity = (x) => ({
map: (f) => Identity(f(x)),
valueOf: () => x,
inspect: () => `Identity {${x}}`,
})
1
2
3
4
5
2
3
4
5
# 2. Maybe Functor
定义
const isEmpty = (x) => x === undefined || x === null
const Maybe = (x) => ({
map: (f) => (isEmpty(x) ? Maybe(null) : Maybe(f(x))),
valueOf: () => x,
inspect: () => `Maybe {${x}}`,
})
1
2
3
4
5
6
7
2
3
4
5
6
7
使用
function add4(x) {
return x + 4
}
function add8(x) {
x + 8 // 没有 return,函数返回 undefined
}
function toString(x) {
return x.toString()
}
function addX(x) {
return x + 'X'
}
function add10(x) {
return x + '10'
}
const res = Maybe(10).map(add4).map(add8).map(toString).map(addX).inspect()
// 输出 Maybe {null}
console.log(res)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Monad(单子)
Monad
是一个同时实现了 map
方法和 flatMap
方法的盒子。
map
预期 f(x)
会输出一个具体的值。这个值会作为下一个“基础行为”的回调入参传递下去。
而 flatMap
预期 f(x)
会输出一个 Functor
,它会像剥洋葱一样,把 Functor
里包裹的值给“剥”出来。确保最终传递给下一个“基础行为”的回调入参,仍然是一个具体的值。
定义
const Monad = (x) => ({
map: (f) => Monad(f(x)),
// flatMap 直接返回 f(x) 的执行结果
flatMap: (f) => f(x),
valueOf: () => x,
inspect: () => `Monad {${x}}`,
})
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
使用
const monad = Monad(1)
const nestedMonad = Monad(monad)
// 试试会发生什么?
console.log(nestedMonad.flatMap((x) => x))
1
2
3
4
5
2
3
4
5
# Semigroup(半群)
Semigroup
是闭合于结合性二元运算之下的集合 S 构成的代数结构。核心是实现了 concat
函数
加法半群
// 定义一个类型为 Add 的 Semigroup 盒子
const Add = (value) => ({
value,
// concat 接收一个类型为 Add 的 Semigroup 盒子作为入参
concat: (box) => Add(value + box.value),
})
// 输出一个 value=6 的 Add 盒子
Add(1).concat(Add(2)).concat(Add(3))
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# Monoid(幺半群)
Monoid
是一种介于Semigroup
和group
之间的代数结构,它是一个拥有了identity element
的半群。
Monoid = Semigroup + identity element
identity element
在数学上叫做“单位元”。 单位元的特点在于,它和任何运算数相结合时,都不会改变那个运算数。
在函数式编程中,单位元也是一个函数,我们一般把它记为“empty() 函数”
也就是说,Monoid = Semigroup + empty() 函数
// 定义一个类型为 Add 的 Semigroup 盒子
const Add = (value) => ({
value,
// concat 接收一个类型为 Add 的 Semigroup 盒子作为入参
concat: (box) => Add(value + box.value),
})
// 这个 empty() 函数就是加法运算的单位元
Add.empty = () => Add(0)
// 输出一个 value=3 的 Add 盒子
Add.empty().concat(Add(1)).concat(Add(2))
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
在实践中,Monoid
常常被放在 reduce
的 callback
中参与计算。如下:
// 定义一个类型为 Add 的 Monoid 盒子
const Add = (value) => ({
value,
// concat 接收一个类型为 Add 的 Monoid 盒子作为入参
concat: (box) => Add(value + box.value),
})
Add.empty = () => Add(0)
// 把 Add 盒子放进 reduce 的 callback 里去
const res = [1, 2, 3, 4].reduce((monoid, num) => monoid.concat(Add(num)), Add.empty())
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# foldMap
foldMap 的作用是实现 n 元的 Monoid 盒子运算
// 这里我以 map+reduce 的写法为例,抽象 foldMap() 函数
const foldMap = (Monoid, arr) => arr.map(Monoid).reduce((prevMonoid, currentMonoid) => prevMonoid.concat(currentMonoid), Monoid.empty())
// 定义 Multi 盒子
const Multi = (value) => ({
value,
concat: (box) => Multi(value * box.value),
})
Multi.empty = () => Multi(1)
// 使用 foldMap 实现 Multi 盒子求积功能
const res = foldMap(Multi, [1, 2, 3, 4])
// 输出 24, 求积成功
console.log(res.value)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15