Performance 为什么这个代码不是常量空间?

Performance 为什么这个代码不是常量空间?,performance,haskell,Performance,Haskell,我目前正在学习Haskell(作为一名程序员,这是我第一次尝试函数式语言) 我想写一个函数来扫描一个列表并返回该列表的最小和最大元素。前奏曲功能最小值和最大值的作用,但两者同时进行。我想出了以下代码: import Data.List -- Declaration of rand minMax :: [Int] -> Maybe (Int, Int) minMax [] = Nothing minMax (x:xs) = Just (foldl' f (x, x) xs)

我目前正在学习Haskell(作为一名程序员,这是我第一次尝试函数式语言)

我想写一个函数来扫描一个列表并返回该列表的最小和最大元素。前奏曲功能
最小值
最大值
的作用,但两者同时进行。我想出了以下代码:

import Data.List

-- Declaration of rand

minMax :: [Int] -> Maybe (Int, Int)
minMax []   = Nothing
minMax (x:xs) = Just (foldl' f (x, x) xs)
                where
                  f (a, b) c = (if c < a then c else a, if c > b then c else b)
编译并运行所有这些与分析,它告诉我它使用超过200 MB的内存,所以它肯定不是一个常数空间函数(我希望它是)

问题是为什么以及我应该改变什么来修复它。据我所知,
foldl'
从左到右折叠列表(与生成列表的方式相同),并且不懒惰,因此我不明白为什么内存使用率如此之高。我敢肯定是
minMax
函数不正确,因为只需使用

main = print $ take 1000000 $ rand 7666532

给我1MB的使用量,这是我理解和期望的。

注意,
foldl'
强制累加器使用弱头标准形式。由于累加器是一个元组,因此它不会强制计算元组的两个元素

如果显式强制这两个元素,则会得到一个常量空间函数:

main = print $ minMax $ take 1000000 $ rand 7666532
f (a, b) c = a `seq` b `seq` (if c < a then c else a, if c > b then c else b)
因此,代码将变成:

minMax :: [Int] -> Maybe (Pair Int Int)
minMax []   = Nothing
minMax (x:xs) = Just (foldl' f (Pair x x) xs)
                where
                  f (Pair a b) c = Pair (if c < a then c else a) (if c > b then c else b)
minMax::[Int]->可能(成对Int)
minMax[]=无
minMax(x:xs)=刚好(foldl'f(对x x)xs)
哪里
f(对AB)c=对(如果cb则c else b)

这完全避免了thunk。

foldl'
中使用的
seq
函数本质上强制将其第一个参数计算为WHNF(弱头范式)

如前所述,一旦您每次调用构造函数的应用程序,WHNF评估就会停止<因此,代码>(a,b)始终在WHNF中,即使
a
b
是重击,因为在到达
a
b
之前,您正在点击元组构造函数
(,)

因此,尽管使用了
foldl'
,该空间还是会泄漏:

foldl' (\ (a, b) x -> (a + x, b + x)) (0, 1) [1..1000000]
但这并不是:

foldl' (\ (a, b) x -> a `seq` b `seq` (a + x, b + x)) (0, 1) [1..10000000]
有时使用
-XBangPatterns
扩展来编写以下代码也很方便:

foldl' (\ (!a, !b) x -> (a + x, b + x)) (0, 1) [1..10000000]

请编辑您的问题并添加完整的代码,包括
rand
的定义。在这里,使用严格的左折叠不会强制计算对的元素,因为累加器已经处于弱头标准形式,元组是惰性的。请使用
seq
添加代码,或者定义严格的对数据类型:
数据对a b=pair!A.b
并用它代替普通的
(a,b)
。我想提到哪些摘要有效地结合了这样的折叠,事实上给出的一个例子是:
L.fold((,)L.minimum L.maximum)[1..10000000]
必须:确保您正在使用优化swait进行编译。。。但你仍然在那里储存着thunks。你不是吗?@Jubobs啊,是的。累加器作为
(如果..,如果..)
传递。但是,它是一个恒定的空间(每次调用
f
时,thunk都会被计算,因此它们的大小是恒定的。一旦您确保函数足够严格,GHC几乎肯定会避免创建thunk,只要您在编译时启用了优化(
-O
-O2
).很好的链接,让我对引擎盖下发生的事情有了更好的了解。现在我很难决定接受哪个答案…我希望我能同时做到这两个。
foldl' (\ (!a, !b) x -> (a + x, b + x)) (0, 1) [1..10000000]