Haskell中foldr与foldl应用div函数的计算逻辑问询
解惑Haskell的foldr/foldl:为什么div折叠只返回一个值?
嘿,我完全懂你这种困惑!当初我刚学Haskell的fold系列函数时,也对着类似的问题挠头好久——明明加法折叠的替换逻辑能理解,怎么一换div就完全不是预期的结果?咱们一步步掰扯清楚:
首先纠正核心误解:fold是「归约」,不是「映射」
你之所以以为会得到“每个元素除以7的列表”,是不小心把fold和map的作用搞混了:
map是映射操作:给列表里的每个元素都套上同一个函数,输出一个和原列表长度相同的新列表,比如map (\x -> xdiv7) [1,2,3]才会返回[0,0,0],这是你预期的“每个元素除以7”的效果。- 而
foldr/foldl是归约操作:它们的目标是把整个列表的所有元素,通过你传入的二元函数,一步步“折叠”合并成单一值——从始至终,它们都不会生成新列表,只会输出一个结果。
拿你的div折叠例子,一步步看GHCi的计算过程
假设你在GHCi里输入的是类似foldr (div) 7 [1,2,3]或者foldl (div) 7 [1,2,3],咱们分别拆解:
情况1:foldr div 7 [1,2,3]
foldr的核心逻辑是右结合,从列表的末尾开始往回计算,公式是:foldr f z [x1,x2,x3] = f x1 (f x2 (f x3 z))
代入你的例子,展开后是:
div 1 (div 2 (div 3 7))
现在一步步计算:
- 最内层:
div 3 7——Haskell的div是整数除法,正数相除会截断向零,3除以7商0,所以这一步结果是0。 - 中间层:
div 2 0——这里直接触发除以零错误,GHCi会抛出*** Exception: divide by zero。
情况2:foldl div 7 [1,2,3]
foldl是左结合,从列表的开头往前计算,公式是:foldl f z [x1,x2,x3] = f (f (f z x1) x2) x3
代入例子展开后是:
div (div (div 7 1) 2) 3
计算过程:
- 最内层:
div 7 1 = 7 - 中间层:
div 7 2 = 3(整数除法,7除以2商3) - 最外层:
div 3 3 = 1
最终GHCi会输出1——这就是你看到的“只得到一个元素”的原因,它本来就只会返回单一值。
再回顾你理解的加法折叠,验证归约逻辑
你之前懂的foldr (+) 0 [1,2,3],展开是1 + (2 + (3 + 0)),最终得到6——这也是一个单一值,只是加法的场景下,你可能默认接受“求和得到一个数”,但换了div就忘了fold的本质是归约。
总结一下
如果你的目标是给列表每个元素做转换生成新列表,用map;如果是要把列表所有元素合并成一个值(求和、求积、找最大值、拼接字符串等),才用foldr/foldl。搞清楚这两类函数的定位,就不会再出现这种预期偏差啦~
内容的提问来源于stack exchange,提问作者Pape Sow Traore
相关产品推荐
相关产品推荐

