← 返回 Day 95 正文

Day 95 · 图解

Day 095 — 函数式编程深入 · 图解

1. reduce 归约过程

reduce(lambda acc, x: acc + x, [1, 2, 3, 4, 5])

步骤:
  初始: acc=1
  Step1: acc=1+2=3
  Step2: acc=3+3=6
  Step3: acc=6+4=10
  Step4: acc=10+5=15
  
  [1, 2, 3, 4, 5]
   ↓
  [1+2, 3, 4, 5] → [3, 3, 4, 5]
   ↓
  [3+3, 4, 5] → [6, 4, 5]
   ↓
  [6+4, 5] → [10, 5]
   ↓
  [10+5] → [15]

2. 柯里化 vs 偏函数

普通函数:
  add(a, b, c) → result
  
柯里化:
  add(1)(2)(3) → result
  add(1)(2, 3) → result
  add(1, 2)(3) → result
  
  特点: 每次调用返回新函数,直到参数足够

偏函数 (partial):
  add_1_2 = partial(add, 1, 2)
  add_1_2(3) → result
  
  特点: 固定部分参数,生成新函数

┌─────────────────────────────────────────┐
│              柯里化                       │
│                                           │
│  add ──► add(1) ──► add(1)(2) ──► result  │
│           │              │                │
│           ▼              ▼                │
│        新函数          新函数              │
└─────────────────────────────────────────┘

┌─────────────────────────────────────────┐
│              偏函数                       │
│                                           │
│  add + (1,2) ──► add_1_2 ──► add_1_2(3)  │
│                       │           │      │
│                       ▼           ▼      │
│                    新函数       result    │
└─────────────────────────────────────────┘

3. 不可变数据结构

可变 vs 不可变:

可变 list:
  data = [1, 2, 3]
  other = data
  data.append(4)
  → data = [1, 2, 3, 4]
  → other = [1, 2, 3, 4]  (一起变了!)

不可变 tuple:
  data = (1, 2, 3)
  other = data
  data = data + (4,)  # 创建新对象
  → data = (1, 2, 3, 4)
  → other = (1, 2, 3)  (不变!)

不可变操作模式:
  ┌──────────┐      ┌──────────┐
  │ 原始数据  │      │ 原始数据  │
  │ [1,2,3]  │      │ [1,2,3]  │  ← 不变
  └────┬─────┘      └──────────┘
       │
       ▼ 操作
  ┌──────────┐
  │ 新数据    │
  │ [1,2,3,4]│  ← 新对象
  └──────────┘

4. 管道模式

数据处理管道:

输入: "  Hello   World  "
  │
  ▼ strip
"Hello   World"
  │
  ▼ lower
"hello   world"
  │
  ▼ replace(" ", "_")
"hello___world"
  │
  ▼ title
"Hello___World"
  │
  ▼ 正则清理
"Hello_World"
  │
  ▼ 输出

函数组合:
  pipe_forward(f1, f2, f3)(x) = f3(f2(f1(x)))
  compose(f1, f2, f3)(x) = f1(f2(f3(x)))

5. 惰性求值

惰性求值 vs 急切求值:

急切求值 (列表推导):
  data = [x*2 for x in range(1000000) if x%2==0][:10]
  → 计算所有 500000 个元素,只用 10 个
  → 浪费 99.998% 的计算

惰性求值 (管道):
  data = LazyRange(1000000)
          .filter(x%2==0)
          .map(x*2)
          .take(10)
  → 只计算前 20 个元素(10个偶数 × 2)
  → 按需计算,内存友好

  时间线: ──────────────────────────────►
  急切:   [计算全部500000个]→[取前10个]
  惰性:   [取1个]→[取1个]→[取1个]...→[够了,停]

6. singledispatch 类型分派

singledispatch 工作流程:

                    ┌─────────────┐
                    │  调用 serialize │
                    │  (value)     │
                    └──────┬──────┘
                           │
                           ▼
                    ┌─────────────┐
                    │ 检查 value 类型 │
                    └──────┬──────┘
                           │
           ┌───────────────┼───────────────┐
           │               │               │
     ┌─────▼─────┐  ┌─────▼─────┐  ┌─────▼─────┐
     │  int      │  │  str      │  │  list     │
     │ serialize │  │ serialize │  │ serialize │
     │ (int)     │  │ (str)     │  │ (list)    │
     └─────┬─────┘  └─────┬─────┘  └─────┬─────┘
           │               │               │
           ▼               ▼               ▼
      "int:42"      "str:hello"    "list:[1,2,3]"

类似 Java 的方法重载,但更灵活