当累加器满足一定条件时,如何从haskell中的折叠函数中跳出?
在将当累加器满足一定条件时,如何从haskell中的折叠函数中跳出?,haskell,functional-programming,fold,Haskell,Functional Programming,Fold,在将someFunction应用于列表的每个元素之后,我计算列表的总和,如下所示: sum (map someFunction myList) someFunction非常占用资源,因此为了优化它,如果超过某个阈值,我想停止计算总和 似乎我需要使用折叠,但如果累加器达到阈值,我不知道如何打破它。我的猜测是以某种方式组合fold和takeWhile,但我不确定如何组合。您可以尝试创建自己的sum函数,或者称之为boundedSum 一个整数上限 求和的[整数] 与上限进行比较的“汇总到此点”值
someFunction
应用于列表的每个元素之后,我计算列表的总和,如下所示:
sum (map someFunction myList)
someFunction
非常占用资源,因此为了优化它,如果超过某个阈值,我想停止计算总和
似乎我需要使用折叠,但如果累加器达到阈值,我不知道如何打破它。我的猜测是以某种方式组合fold
和takeWhile
,但我不确定如何组合。您可以尝试创建自己的sum
函数,或者称之为boundedSum
整数
上限[整数]
boundedSum :: Integer -> [Integer] -> Integer -> Integer
boundedSum upperBound (x : xs) prevSum =
let currentSum = prevSum + x
in
if currentSum > upperBound
then upperBound
else boundedSum upperBound xs currentSum
boundedSum upperBound [] prevSum =
prevSum
我认为这样,在当前元素超过上限之前,如果求和,就不会“吃掉”更多的列表
编辑:建议使用比我更好的技术的答案,而且问题本身看起来与你的问题非常相似。其中一个选项是使用函数,该函数返回
foldl
的中间计算列表
foldl (\b a -> b + if b > someThreshold then 0 else a) 0 (map someFunction myList)
因此,scanl1(+)(map someFunction myList)
将返回计算的中间和。由于Haskell
是一种惰性语言,因此在需要它之前,它不会计算myList
的所有值。例如:
take 5 $ scanl1 (+) (map someFunction myList)
head $ dropWhile (< 1000) $ scanl1 (+) [1..1000000000]
将计算someFunction
5次并返回这5个结果的列表
之后,当某个条件为True
时,您可以使用或并停止计算。例如:
take 5 $ scanl1 (+) (map someFunction myList)
head $ dropWhile (< 1000) $ scanl1 (+) [1..1000000000]
head$dropWhile(<1000)$scanl1(+)[1..100000000]
当数字总和达到1000并返回
1035
时,将停止计算。这是一种可能的解决方案:
last . takeWhile (<=100) . scanl (+) 0 . map (^2) $ [1..]
last。takeWhile(使用有界加法运算符,而不是使用foldl
的(+)
foldl (\b a -> b + if b > someThreshold then 0 else a) 0 (map someFunction myList)
因为Haskell是非严格的,所以只有对someFunction
的调用才是计算if-then-else
所必需的,它们本身也会被计算。fold
仍然遍历整个列表
> foldl (\b a -> b + if b > 10 then 0 else a) 0 (map (trace "foo") [1..20])
foo
foo
foo
foo
foo
15
sum[1..5]>10
,您可以看到trace“foo”
只执行5次,而不是20次
但是,您不应该使用Data.Foldable中的严格版本foldl'
。
另一种技术是使用foldM
与或一起捕获提前终止效果。左
表示提前终止
import Control.Monad(foldM)
sumSome :: (Num n,Ord n) => n -> [n] -> Either n n
sumSome thresh = foldM f 0
where
f a n
| a >= thresh = Left a
| otherwise = Right (a+n)
要忽略退出条件,只需使用任一id
组合即可
sumSome' :: (Num n,Ord n) => n -> [n] -> n
sumSome' n = either id id . sumSome n
这将满足您的要求,而无需构建中间列表,因为scanl'
会(而且scanl
甚至会在其上导致thunks累积):
参见相关。类似的内容,使用直到::(a->Bool)->(a->a)->a->a
,从前奏曲开始
sumUntil :: Real a => a -> [a] -> a
sumUntil threshold u = result
where
(_, result) = until stopCondition next (u, 0)
next :: Real a => ([a], a) -> ([a], a)
next ((x:xs), y) = (xs, x + y)
stopCondition :: Real a => ([a], a) -> Bool
stopCondition (ls, x) = null ls || x > threshold
然后申请
sumUntil 10 (map someFunction myList)
几乎可以,但是使用scanl而不是fold。如果总和超过阈值,或者如果资源密集型函数的结果超过阈值?可能需要考虑的情况是,可能永远不会达到阈值,在这种情况下,head
将出错。也许我会使用find
而不是head代码>和dropWhile
:查找(>=1000)(scanl1(+)[1..)
这很酷,看起来像巫术,但应用于无限列表时它确实会挂起。我认为跟踪显示,a
只是不是强制的,尽管整个列表都被遍历了。啊,对了。我不太明白为什么这似乎起作用。我一直认为这种懒散的把戏是一种典型的“突围”方式从褶皱中。