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

Haskell中do块内单元素列表的收集拼接及执行逻辑疑问

关于Haskell Do块解糖与列表拼接的疑问解答

先贴出你提到的代码:

triples = do
   [1..] >>= \z ->
      [1..z] >>= \x ->
         [x..z] >>= \y ->
            guard (x^2 + y^2 == z^2) >>= \_ ->
               return (x, y, z)

你的理解纠正与细节补充

你的步骤理解基本正确,补充两个关键细节:

  • guard在列表Monad中的行为:断言为真时返回[()](包含空元组的单元素列表),为假时返回[](空列表)
  • return a在列表Monad中等价于[a],就是把值包装成单元素列表

执行顺序:完全对应命令式嵌套循环

这段代码的执行逻辑和命令式语言的嵌套循环完全一致:

  1. 外层遍历:逐个取出无限列表[1..]的元素作为z
  2. 中层遍历:针对当前z,逐个取出[1..z]的元素作为x
  3. 内层遍历:针对当前x,逐个取出[x..z]的元素作为y
  4. 条件过滤:如果x² + y² == z²成立,就保留(x,y,z);不成立则直接跳过该组合

单元素列表的拼接:靠>>=的列表定义

要搞懂拼接逻辑,必须明确列表Monad中>>=的底层实现:

(>>=) :: [a] -> (a -> [b]) -> [b]
xs >>= f = concat (map f xs)

简单说,>>=会先把列表里的每个元素传给函数f,得到一堆子列表,再把这些子列表扁平化拼接成一个大列表。

放到你的代码里逐层拆解:

  • 最内层:当guard返回[()]时,\_ -> return (x,y,z)会把()映射成[(x,y,z)];如果guard返回[],映射后还是[]
  • 中层拼接:[x..z] >>= \y -> ...会把每个y对应的结果列表(要么是单元素三元组列表,要么是空列表)拼接,得到当前x下所有符合条件的三元组
  • 上层拼接:[1..z] >>= \x -> ...会把每个x对应的结果列表拼接,得到当前z下所有符合条件的三元组
  • 最外层拼接:[1..] >>= \z -> ...会把每个z对应的结果列表拼接,最终得到所有勾股三元组的无限列表

关于你测试代码的疑问

你测试的代码省略了最后一步的函数,完整写法应该是:

[1..3] >>= \z ->
   [1..z] >>= \x ->
      [x..z] >>= \y -> return (x,y,z)

这时候>>=会把所有(x,y,z)的组合拼接成一个列表,结果是:

[(1,1,1),(1,1,2),(1,2,2),(1,1,3),(1,2,3),(1,3,3),(2,2,3),(2,3,3),(3,3,3)]

而加上guard后,相当于给每个组合加了过滤:只有满足条件的才会生成单元素列表,不满足的生成空列表,最终拼接后就只剩下符合条件的三元组了。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 20:30:51