You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Haskell累加器工作原理解析及代码实现疑问咨询

理解Haskell中累加器的工作原理

你困惑的核心点非常关键——Haskell是纯函数式语言,没有可变变量,那所谓的“累加器”怎么能“累积”值呢?其实答案是:它并没有被修改,而是每次递归都会创建一个全新的状态,传递给下一次调用。我们拿你的代码和输入4的例子一步步拆解,就能看明白。

先看你的代码逻辑

你的accu函数用了一个辅助递归函数go,它的参数(s, ls)就是我们说的“累加器状态”:s存当前的和,ls存当前的列表片段。

accu :: Int -> (Int, [Int])
accu 0 = (0, [0])
accu nr = go (0, []) nr where
    go (s,ls) 0 = (s, 0:ls)
    go (s,ls) nr = go (s+nr, nr:ls) (nr-1)

一步步走输入4的执行流程

我们把每一步的调用和状态变化列出来:

  • 调用accu 4,它会启动辅助函数:go (0, []) 4
  • 进入go (0, []) 4:第二个参数不是0,所以生成新状态(0+4, 4:[]),也就是(4, [4]),然后调用go (4, [4]) 3
  • 进入go (4, [4]) 3:生成新状态(4+3, 3:[4]) → (7, [3,4]),调用go (7, [3,4]) 2
  • 进入go (7, [3,4]) 2:生成新状态(7+2, 2:[3,4]) → (9, [2,3,4]),调用go (9, [2,3,4]) 1
  • 进入go (9, [2,3,4]) 1:生成新状态(9+1, 1:[2,3,4]) → (10, [1,2,3,4]),调用go (10, [1,2,3,4]) 0
  • 进入go (10, [1,2,3,4]) 0:触发终止条件,返回(10, 0:[1,2,3,4]),也就是最终结果(10, [0,1,2,3,4])

为什么看起来像“累加”?

你觉得累加器在“变化”,其实是因为我们每次都把更新后的新状态传递给下一次递归:

  • 原来的(s, ls)自始至终都是不可变的,比如第一步的(0, [])永远是(0, []),我们只是不再使用它了
  • 每一步的“累加”,其实是基于当前状态计算出一个全新的状态,然后把这个新状态丢给下一轮递归
  • 这就像你每次递纸条给同学,不是在原来的纸条上改,而是写一张新的、更新过的纸条递过去,最后拿到的那张就是最终结果

解释你看不懂的go第一个分支

go (s,ls) 0 = (s, 0:ls)这一行的作用是收尾:

  • 当递归到第二个参数为0时,s已经是1+2+3+4的和(也就是10),ls是[1,2,3,4]
  • 我们需要把0加到列表开头,凑出题目要求的[0..4],所以用0:ls生成完整列表
  • 最后把累积好的和s和完整列表打包成元组返回

总结一下

Haskell里的累加器模式,本质是用递归传递不可变的状态快照。没有任何变量被修改,只是每一步都基于当前状态生成新的状态,直到触发终止条件,返回最终的状态。这就是纯函数式语言实现“累积”效果的方式。

内容的提问来源于stack exchange,提问作者Bercovici Adrian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:46:23