在 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
。
如果你不走运,你的第一个无限列表将使用无限量的内存。因此,请使用您的第二个无限列表(或者,如果您更喜欢匿名无限列表,请使用repeat
Prelude)。
一个示范。执行此操作时,可能会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
第一个求和在恒定空间中运行。第二个总结会占用内存,所以我在它导致我的笔记本电脑交换之前中断了它。
firstElem
在这个简单的例子中,我们可以通过在计算之前计算来避免空间泄漏theSum
,但在现实世界的应用程序中,这可能是不可能的,或者至少难以追踪。最好通过使用来避免它repeat
。
(关于优化的说明:如果我们不将-O
标志传递给ghc
thensum
将在两个求和过程中发生空间泄漏。重写它并不难,sum = foldl' (+) 0
因此即使没有 . 也不会发生空间泄漏-O
。我不知道是什么考虑导致当前而是实施。)
要回答您的问题,两者都是展开,但let
和repeat
变体更好,因为enumFrom
变体经过实际枚举,因此涉及无用的算术。