在许多软件开发环境中,使用 'reduce' 函数(一种将集合坍缩为单个值的多功能工具)往往会在代码审查中引发负面反馈。虽然开发者经常在不经太多审查的情况下使用 'map' 和 'filter',但 'reduce' 经常被标记为“难以阅读”或不如其对应函数优雅。
“避免使用 Reduce”的社会现象
Evan Hahn 报告称,虽然他不断使用 'map' 和 'filter' 且从未收到同行的抱怨,但他的 'reduce' 用法经常导致收到“代码难以阅读”的反馈。他认为这可能是因为在 JavaScript、Python 和 Swift 等语言中,'reduce' 不够熟悉、难以阅读或不够优雅。
Hahn 指出,这一趋势可能是一种社会现象而非技术上的失败,尽管他承认在 Clojure 等函数式编程环境中,这种反馈要少得多。他观察到,即使他更倾向于使用 'reduce',他也经常退回到其他方法,以满足代码审查过程中的社会预期。
认知负荷与推理模式
Hacker News 上的评论者认为,之所以更倾向于 'map' 和 'filter' 是因为它们允许局部推理,即单个元素的输出独立于其他元素。相比之下,'reduce'(或 'fold')迫使开发者全局地思考累加器在遍历整个集合过程中状态的演变。
一位评论者指出,'reduce' 因为要求开发者为相关类型“变出”一个零值或初始状态,从而增加了心理负担。其他人指出,在命令式语言中,'reduce' 通常比标准的 'for' 循环提供很少的收益,但引入了更复杂的语法,使其成为“语法更糟糕的循环”。
不一致性与实现权衡
'reduce' 面临的一个重大障碍是编程语言之间缺乏一致性。评论者指出,不同语言在回调函数中累加器是第一个还是第二个参数,或者函数是否接收初始值等方面各不相同。这种不一致使得 'reduce' 成为一个“易误操作点”(footgun),如果未提供初始值,空集合可能会导致崩溃。
讨论还强调了性能风险,例如在 Python 的 'reduce' 函数中进行字符串拼接会导致 O(n^2) 的复杂度。一位评论者分享了一个历史轶事:在 Python 3 中,'reduce' 被降级为库函数,因为它曾导致一起生产事故,涉及到一个由于二次时间复杂度导致线程池耗尽的换行算法。
最小权力原则
“最小权力原则”被引用为青睐更高层级抽象的原因。因为 'reduce' 非常强大且通用,它在技术上几乎可以实现任何集合转换,但它经常被误用来重新实现那些已经拥有专用、更易读函数的模式。
几位评论者的共识是,如果像 'map' 或 'filter' 这样的更高层级操作不够灵活,通常最好将操作分解为多个更简单的步骤,或者使用传统的循环,而不是求助于 'reduce' 来一次性完成所有事情。
关于对 'reduce' 的“反感”的证据在很大程度上仍是轶事性的,因为没有提供定量数据来衡量这些抱怨在专业代码审查中的频率。此外,“正确”的选择往往取决于具体的语言以及开发者对函数式编程概念的熟悉程度。
虽然一些开发者认为 'reduce' 是一种“锤子/钉子”式的方法,通过将实现从调用点移开来简化复杂逻辑,但其他人坚持认为,由于还原过程缺乏透明度,使其成为比 'map' 或 'filter' 更不安全的抽象。
images
