以下是自定义length
功能的经典首次尝试:
length1 [] = 0
length1 (x:xs) = 1 + length1 xs
这是一个尾递归版本:
length2 = length2' 0 where
length2' n [] = n
length2' n (x:xs) = length2' (n+1) xs
但是,(n+1)
不会被严格评估,但是instad Haskell会创建一个thunk,对吗?
这是否是防止thunk创建的正确方法,从而强制严格评估(n+1)
?
length3 = length3' 0 where
length3' n [] = n
length3' n (x:xs) = length3' (n+1) $! xs
我如何使用seq
代替$!
达到相同的效果?
答案 0 :(得分:4)
我不认为这是对的 - $!
在第二个参数中是严格的,所以你只是强制列表而不是累加器。
你可以使用这样的seq获得正确的严格性:
let n' = n + 1 in n' `seq` length3' n' xs
我认为更易读的版本会使用爆炸模式(GHC扩展名):
length3' !n (x:xs) = ...
答案 1 :(得分:4)
现在写它的通常方法是:
length3 :: [a] -> Int
length3 = go 0
where
go :: Int -> [a] -> Int
go n [] = n
go n (x:xs) = n `seq` go (n+1) xs
即,作为累加器中严格列表的折叠。 GHC直接转换为Core:
Main.$wgo :: forall a_abz. GHC.Prim.Int# -> [a_abz] -> GHC.Prim.Int#
Main.$wgo =
\ (n :: GHC.Prim.Int#) (xs :: [a_abz]) ->
case xs of
[] -> n
_ : xs -> Main.$wgo a (GHC.Prim.+# n 1) xs
显示它在累加器中是未装箱的(因而是严格的)。