从循环到递归:如何避免可变状态

副标题 / 摘要 循环常依赖可变变量,而递归可以用参数传递状态。本文展示转换思路与适用场景。 目标读者 学习函数式编程的开发者 想减少可变状态的人 关注代码可推理性的工程师 背景 / 动机 可变状态会降低可推理性并增加错误。 递归可以把状态显式化,从而更安全。 核心概念 递归:函数调用自身 累加器:用参数传递中间状态 不可变性:避免状态被修改 实践指南 / 步骤 找出循环中的状态变量 把状态变量变成参数 定义终止条件 返回最终结果 可运行示例 # 循环版本 def sum_loop(nums): s = 0 for x in nums: s += x return s # 递归版本 def sum_rec(nums, acc=0): if not nums: return acc return sum_rec(nums[1:], acc + nums[0]) if __name__ == "__main__": print(sum_loop([1, 2, 3])) print(sum_rec([1, 2, 3])) 解释与原理 循环依赖可变变量 s,递归用参数 acc 传递状态。 这样状态是显式的,减少副作用。 常见问题与注意事项 递归一定更好吗? 不一定,深度过大会栈溢出。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

不变性为何让代码更安全:Immutability 的价值与边界

副标题 / 摘要 不变性并不是“不能改”,而是“用新值替代旧值”。本文解释它如何降低错误、提升可测试性,并给出工程落地方式。 目标读者 经常处理共享状态与并发的工程师 需要提升可测试性与可维护性的开发者 对函数式思想有兴趣的团队负责人 背景 / 动机 大量线上问题来自“意外修改”:某个函数悄悄改了共享对象,导致后续逻辑出现不可预期的结果。 不变性让“修改”变成显式的“新值生成”,错误更容易被发现与隔离。 核心概念 不可变性:对象创建后不再改变 共享状态风险:多个模块引用同一对象,任何修改都会外溢 副作用:函数内部改变了外部可观察状态 实践指南 / 步骤 在核心逻辑中优先使用不可变数据结构 把“修改”改写为“返回新值” 把副作用集中在边界层(IO、缓存、DB) 使用类型或工具强制不可变(如 frozen) 可运行示例 from dataclasses import dataclass, replace @dataclass(frozen=True) class Order: id: int price: int def apply_discount(order: Order, rate: float) -> Order: new_price = int(order.price * (1 - rate)) return replace(order, price=new_price) if __name__ == "__main__": o1 = Order(id=1, price=100) o2 = apply_discount(o1, 0.2) print(o1, o2) 解释与原理 不变性让“状态变化”变成显式的数据流。 当对象不能被修改时,共享引用不会造成隐式副作用,测试也更容易覆盖到边界条件。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

可变值 vs 不可变值:优缺点、成本与工程选择

副标题 / 摘要 可变值带来性能与直觉操作,不可变值带来安全与可预测性。本文从工程角度给出取舍指南。 目标读者 需要做语言/架构选型的开发者 经常处理共享状态与并发的工程师 想降低 bug 成本的团队负责人 背景 / 动机 “用可变还是不可变”常被当成风格问题,但本质是成本问题: 可变值降低了短期编码成本,却提高了长期维护成本;不可变值相反。 核心概念 可变值:对象可被修改,引用指向同一状态 不可变值:对象创建后不可变,修改通过创建新值 别名问题:多个引用指向同一对象导致隐式副作用 实践指南 / 步骤 核心业务规则优先用不可变 性能敏感且局部范围内用可变 共享数据优先不可变 用类型或约定明确边界 在接口处转换:可变在边界层,不可变在核心层 可运行示例 下面展示共享可变带来的副作用: nums = [1, 2, 3] ref = nums ref.append(4) print(nums) # [1, 2, 3, 4] 不可变方式: nums = (1, 2, 3) ref = nums ref = ref + (4,) print(nums) # (1, 2, 3) print(ref) # (1, 2, 3, 4) 解释与原理 可变值让“状态变化”隐式发生,易产生别名问题; 不可变值把变化变成显式的新值,便于推理与测试。 常见问题与注意事项 不可变一定更慢吗? 不一定。结构共享与持久化数据结构可以降低成本。 可变值是不是更直观? 对局部数据更直观,但对共享状态更危险。 如何混用? 常见做法是“核心域不可变,边界层可变”。 最佳实践与建议 在并发场景优先不可变 在性能关键、局部封闭场景用可变 给团队建立明确的可变/不可变规范 小结 / 结论 可变值适合局部与性能场景,不可变值适合共享与核心逻辑。 真正的工程实践不是二选一,而是分层与约束。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]

为什么函数式编程重要:适用场景与落地路径

副标题 / 摘要 函数式编程不是宗教,而是一套降低复杂度的方法。本文解释它为什么重要、何时适用,以及如何在现有项目中渐进引入。 目标读者 想提高代码可测试性与可维护性的工程师 需要处理并发、流式数据的开发者 对 FP 有兴趣但不知道如何落地的人 背景 / 动机 复杂系统的主要成本不是写代码,而是理解、调试和演进。 函数式编程强调纯函数、不可变性与组合,能显著减少隐藏状态与副作用带来的不确定性。 核心概念 纯函数:同样输入必然同样输出,没有副作用 不可变性:数据一旦创建就不再修改 高阶函数:函数作为参数或返回值 组合:用小函数拼成复杂逻辑 实践指南 / 步骤 先把“核心逻辑”写成纯函数,把 IO 放在边界层。 优先使用不可变数据,避免共享可变状态。 用 map/filter/reduce 表达数据流。 把副作用集中管理(日志、网络、数据库)。 用测试保证纯函数行为稳定。 可运行示例 from typing import List def normalize_prices(prices: List[int]) -> List[int]: return [p for p in prices if p > 0] def discount(prices: List[int], rate: float) -> List[int]: return [int(p * (1 - rate)) for p in prices] def total(prices: List[int]) -> int: return sum(prices) if __name__ == "__main__": raw = [100, -1, 200, 150] clean = normalize_prices(raw) discounted = discount(clean, 0.1) print(total(discounted)) 解释与原理 纯函数让“状态变化”显式化,减少隐藏副作用。 不可变性降低并发与缓存场景下的错误概率。 组合让复杂逻辑变成“可替换的积木”。 ...

2026年1月24日 · 1 分钟 · map[name:Jeanphilo]