4

在 Haskell 中定义无限列表:

[1,1..] => [1,1,1,..]

或者,循环方式:

lst=1:lst

第一个定义与第二个相同吗?如果不是,哪一种是首选方式?

4

4 回答 4

6

您可能想要repeat定义与您的第二个实现等效的位置。

[1,1..]一个示例中的符号是enumFrom*前奏函数的语法糖。使用任何你喜欢的。

于 2012-07-02T18:42:50.647 回答
6

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

于 2012-07-02T19:06:15.230 回答
4

如果你不走运,你的第一个无限列表将使用无限量的内存。因此,请使用您的第二个无限列表(或者,如果您更喜欢匿名无限列表,请使用repeatPrelude)。


一个示范。执行此操作时,可能会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标志传递给ghcthensum将在两个求和过程中发生空间泄漏。重写它并不难,sum = foldl' (+) 0因此即使没有 . 也不会发生空间泄漏-O。我不知道是什么考虑导致当前而是实施。)

于 2012-07-02T19:59:15.767 回答
3

要回答您的问题,两者都是展开,但letrepeat变体更好,因为enumFrom变体经过实际枚举,因此涉及无用的算术。

于 2012-07-02T19:07:48.717 回答