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

Haskell中含求和的递归条件定义实现方法求助

嘿,我完全懂你想要的那种贴近数学写法的递归求和函数——刚入坑Haskell的时候,我也在fold/scan的用法上绕了好几个弯,咱们一步步把这个问题掰明白~

先从最贴近数学定义的递归写法开始

数学里的求和∑ₐᵇ f(i),递归定义其实很清晰:

如果起始值a大于结束值b,和为0;否则等于f(a)加上从a+1到b的求和

直接把这个逻辑翻译成Haskell就行,完全不需要fold/scan,新手也能一眼看懂:

sumFromTo :: (Num a, Ord a) => (a -> a) -> a -> a -> a
sumFromTo f a b
  | a > b     = 0  -- 边界条件:空求和为0
  | otherwise = f a + sumFromTo f (a + 1) b

试试调用:sumFromTo (\x -> x^2) 1 3 会返回14(1²+2²+3²),完美贴合数学逻辑。

用fold实现:更函数式的折叠思路

如果你想用fold来做,核心是先把「从a到b的所有i」生成一个序列,再把f(i)的结果累加起来。Haskell里用[a..b]就能生成这个序列,然后结合foldr或者foldl:

写法1:手动写fold的逻辑

sumFromToFold :: (Num a, Enum a, Ord a) => (a -> a) -> a -> a -> a
sumFromToFold f a b = foldr (\x acc -> f x + acc) 0 [a..b]

这里foldr的第一个参数是「把当前元素f(x)加到累加器acc上」,初始值设为0——刚好对应a>b时序列为空,fold直接返回0的边界情况。

写法2:更简洁的组合式写法

其实可以把map f和foldr (+) 0组合起来,更符合函数式的风格:

sumFromToFold' :: (Num a, Enum a, Ord a) => (a -> a) -> a -> a -> a
sumFromToFold' f a b = foldr (+) 0 $ map f [a..b]

map f [a..b]先把每个i转成f(i),再用foldr把所有值加起来,逻辑和上面完全一致,只是更紧凑。

用scan实现:看清楚每一步的累加过程

scan和fold类似,但它会返回每一步的累加结果,而不是只返回最终总和。如果你想观察求和的中间过程,比如从a开始每加一项的结果,就很适合用scan:

sumSteps :: (Num a, Enum a, Ord a) => (a -> a) -> a -> a -> [a]
sumSteps f a b = scanl (+) 0 $ map f [a..b]

比如调用sumSteps (\x -> x) 1 3,会得到[0,1,3,6]——第一个0是初始值,后面依次是1、1+2、1+2+3的结果。如果只想要最终总和,取列表的最后一个元素就行(last $ sumSteps f a b),但如果只需要总和的话,fold会更高效。

新手容易踩的小坑

  • 如果你用的是浮点数(比如Double),[a..b]的步长是1,可能不符合你的需求,这时候可以用enumFromThenTo a (a+step) b来指定步长,但整数求和的话[a..b]完全够用。
  • 别忘记类型约束:Num保证可以做加法,Ord用来判断a和b的大小,Enum用来生成序列([a..b]需要这个约束)。

内容的提问来源于stack exchange,提问作者Raphael J.F. Berger

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:01:54