定義Haskell中無限列表:在同一個int的Haskell中定義無限列表,哪種方式?
[1,1..] => [1,1,1,..]
或者,循環方式:
lst=1:lst
是第一個定義一樣的第二?如果不是,哪一個是首選方式?
定義Haskell中無限列表:在同一個int的Haskell中定義無限列表,哪種方式?
[1,1..] => [1,1,1,..]
或者,循環方式:
lst=1:lst
是第一個定義一樣的第二?如果不是,哪一個是首選方式?
你可能想要repeat
其中的定義相當於你的第二個實現。
您的第一個示例中的[1,1..]
表示法是enumFrom*
前奏函數的語法糖。使用任何你喜歡的。
repeat
/1:lst
更好,他們不需要任何額外的計算,但[1,1..]
作用:
[1,1..] = enumFromThen 1 1 = en 1
where en n = n : en (n + nΔ)
nΔ = 1-1 = 0
所以它總是需要執行額外的1+0
。
要回答你的問題,兩者都展開,但let
和repeat
變體更好,因爲enumFrom
變體經歷實際的枚舉,所以涉及無用的算術。
如果您不走運,您的第一個無限列表將使用無限量的內存。因此,使用你的第二個無限列表(或者,如果你更喜歡匿名無限列表,請使用Prelude中的repeat
)。
示範。這樣做可能會讓watch free -m
在另一個窗口中運行。
$ cat so.hs
import Control.Exception (evaluate)
import System.IO (hFlush, stdout)
with :: String -> [Int] -> IO()
with s xs
= do putStrLn $ "Summing part of a " ++ s
theSum <- evaluate $ sum (take 100000000 xs)
firstElem <- evaluate $ head xs
putStrLn $ "sum $ take 100000000 [" ++ show firstElem ++ "...] is " ++ show theSum
main :: IO()
main
= do with "call to repeat" (repeat 1)
putStr "Press return to continue..."
hFlush stdout
getLine
with "list comprehension" [1,1..]
$ ghc -O --make so.hs
[1 of 1] Compiling Main (so.hs, so.o)
Linking so ...
$ ./so
Summing part of a call to repeat
sum $ take 100000000 [1...] is 100000000
Press return to continue...
Summing part of a list comprehension
^C
第一次總和運行在恆定的空間。第二個總和消耗了內存,所以在它導致我的筆記本電腦交換之前中斷它。
在這種簡單的情況下,我們可以在計算theSum
之前通過計算firstElem
來避免空間泄漏,但在現實世界的應用中,這可能是不可能的,或者至少難以追蹤。最好避免使用repeat
。
(關於優化的一個注意:如果我們不求和都在-O
標誌傳遞給ghc
然後sum
意志內存泄露它不會是很難改寫sum = foldl' (+) 0
,使其沒有空間泄漏,即使沒有-O
我不知道導致目前實現的是什麼因素)
你能詳細說明爲什麼第一個可能會使用無限的內存嗎? –
我假設,因爲每個'1'可能實際上被分配並佔用幾個字節,而在第二種情況下,無論如何,只有一個'1'和一個指針。 – MatrixFrog
@MatrixFrog正確。 – dave4420