簡體   English   中英

Haskell Length 函數實現

[英]Haskell Length function implementation

我正在學習 Haskell 編程,並試圖了解列表的工作原理,因此我嘗試編寫兩個可能的length函數:

myLength :: [a] -> Integer
myLength  = foldr (\x -> (+) 1) 0

myLength1 :: [a] -> Integer
myLength1 []     = 0
myLength1 (x:xs) = (+1) (myLength1 xs)

哪一個更好?

從我的角度來看, myLength1更容易理解,並且在列表上操作看起來很自然。

另一方面, myLength更短,並且不使用遞歸; 這是否意味着myLengthmyLength1運行得更快?

請記住foldr這種“偽實現”:

foldr :: function -> initializer -> [a] -> b
foldr _ i [] = i
foldr f i (x:xs) = x `f` (foldr f i xs)

現在我們有了你的代碼

myLength :: [a] -> Integer
myLength  = foldr (\x -> (+) 1) 0

myLength1 :: [a] -> Integer
myLength1 []     = 0
myLength1 (x:xs) = (+1) (myLength1 xs)

由於foldr本身也是遞歸的,因此 myLength1 和 myLength 幾乎相同,但在第一種情況下,遞歸調用由 foldr 完成,而不是由您自己明確完成。 他們應該在同一時間跑。

兩個函數都做同樣的事情:foldr 使用遞歸,最終會以類似於直接遞歸函數的方式執行。 可能有人會說 foldr 版本更簡潔(一旦你習慣了它們,高階函數通常比直接遞歸更具可讀性)。

但是這兩個函數非常糟糕:它們最終都會構建一個很大的 thunk(一個未評估的值) 1 + (1 + (1 + ... + 0)..))這將占用大量內存( O (n) space ) 並且會減慢評估速度。 為避免這種情況,您應該從列表的開頭開始添加 1,如下所示:

betterLength xs = go 0 xs
  where
    go n [] = n
    go n (_:xs) = n `seq` go (n+1) xs

seq確保在遞歸調用 go 函數之前評估 n ,因此沒有+1累積。 使用BangPatterns擴展,你可以這樣寫:

betterLength xs = go 0 xs
  where
    go n [] = n
    go !n (_:xs) = go (n+1) xs

也可以使用 fold 執行此版本:

betterLength = foldl' (\n _ -> n + 1) 0 

其中foldl'L EFT就是嚴格(”)。

使用foldr ,它可以實現為:

length' xs = foldr (\_ n -> 1 + n) 0 xs

解釋:

lambda 函數(\\_ x -> n + 1)每次有一個元素時都會將累加器加一。 例如:

lenght' [1..4]

將應用為: 1 + ( 1 + ( 1 + ( 1 + 0)))

回想一下foldr是這樣定義的:

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f v [] = v
foldr f v (x:xs) = f x (foldr f v xs)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM