繁体   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