3-29.【函数式编程】请解释为什么数组可以作为 Monad,并实现一个示例函数组合多个数组操作。

4 阅读1分钟

1️⃣ 为什么数组可以作为 Monad

在函数式编程中,Monad 的核心特征是:

  1. 容器类型,封装值
  2. bind / flatMap:接受一个返回同类型容器的函数,并将嵌套容器“平铺”
  3. 遵循 Monad 规律:左单位元、右单位元、结合律

数组作为容器

  • 数组 [A] 是一个容器,可以包含 0 或多个值
  • 函数 (A) -> [B] 可以返回多个值
  • flatMap 可以将 [ [B] ] 平铺成 [B]

结合律示例

let a = [1, 2]
let f: (Int) -> [Int] = { [$0, $0 * 2] }
let g: (Int) -> [Int] = { [$0 - 1, $0 + 1] }

let left = a.flatMap(f).flatMap(g)
let right = a.flatMap { x in f(x).flatMap(g) }
print(left == right) // true

✅ 数组满足 Monad 特性


2️⃣ 示例:数组操作组合函数

假设我们有一些操作函数:

// 每个操作返回一个数组(可能有多个结果)
func addOne(_ x: Int) -> [Int] {
    [x + 1]
}

func multiplyTwo(_ x: Int) -> [Int] {
    [x * 2]
}

func duplicates(_ x: Int) -> [Int] {
    [x, x] // 生成重复值
}

3️⃣ 泛型链式组合函数

func chainArrays(_ input: [Int], operations: [(Int) -> [Int]]) -> [Int] {
    return operations.reduce(input) { result, op in
        result.flatMap(op) // flatMap 平铺嵌套数组
    }
}

4️⃣ 使用示例

let initial = [1, 2]

let operations: [(Int) -> [Int]] = [addOne, multiplyTwo, duplicates]

let finalResult = chainArrays(initial, operations: operations)
print(finalResult)

输出解释

  1. [1, 2]addOne[2, 3]
  2. [2, 3]multiplyTwo[4, 6]
  3. [4, 6]duplicates[4, 4, 6, 6]

✅ 每一步都是 Monad bind(flatMap) ,数组嵌套自动平铺


5️⃣ 总结

特性数组 Monad
容器[A]
bind / flatMaparray.flatMap { (A) -> [B] } -> [B]
单位元空数组 []
规律左单位元、右单位元、结合律
优势可组合多个操作,自动平铺结果,避免嵌套数组

💡 核心思想:数组可以返回多个值,flatMap 让这些多值平铺组合,天然体现 Monad 的“平铺容器、链式组合”语义。