所以,看看有什麼優化實際上呢,讓我們look at the core:
% ghc -O2 -ddump-simpl Temp.hs
[1 of 1] Compiling Temp (Temp.hs, Temp.o)
==================== Tidy Core ====================
Result size of Tidy Core = {terms: 29, types: 26, coercions: 0}
Temp.count
:: forall a_arN.
(a_arN -> GHC.Types.Bool) -> [a_arN] -> GHC.Types.Int
[GblId,
Arity=2,
Caf=NoCafRefs,
Str=DmdType <L,C(U)><S,1*U>,
Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=2, Value=True,
ConLike=True, WorkFree=True, Expandable=True,
Guidance=IF_ARGS [60 0] 191 0}]
Temp.count =
\ (@ a_aMA)
(p_arV :: a_aMA -> GHC.Types.Bool)
(eta_B1 :: [a_aMA]) ->
letrec {
go_aNr [Occ=LoopBreaker]
:: [a_aMA] -> GHC.Prim.Int# -> GHC.Types.Int
[LclId, Arity=1, Str=DmdType <S,1*U>]
go_aNr =
\ (ds_aNs :: [a_aMA]) ->
case ds_aNs of _ [Occ=Dead] {
[] -> GHC.Types.I#;
: y_aNx ys_aNy ->
case p_arV y_aNx of _ [Occ=Dead] {
GHC.Types.False -> go_aNr ys_aNy;
GHC.Types.True ->
let {
g_a10B [Dmd=<L,C(U)>] :: GHC.Prim.Int# -> GHC.Types.Int
[LclId, Str=DmdType]
g_a10B = go_aNr ys_aNy } in
\ (x_a10C :: GHC.Prim.Int#) -> g_a10B (GHC.Prim.+# x_a10C 1)
}
}; } in
go_aNr eta_B1 0
清潔它一點:
Temp.count :: forall aType. (aType -> Bool) -> [aType] -> Int
Temp.count = \(@ aType) (p :: aType -> Bool) (as :: [aType]) ->
letrec {
go :: [aType] -> GHC.Prim.Int# -> Int
go = \(xs :: [aType]) ->
case xs of _ {
[] -> I#; -- constructor to make a GHC.Prim.Int# into an Int
: y ys ->
case p y of _ {
False -> go ys;
True ->
let {
go' :: GHC.Prim.Int# -> Int
go' = go ys
} in \(x :: GHC.Prim.Int#) -> go' (GHC.Prim.+# x 1)
}
};
} in go as 0
由於我們在拆箱類型GHC.Prim.Int#
,all the additions are strict操作,所以我們只有一個循環的數據。
當您打開GHC中的優化時,兩次遍歷可能會融合爲一次。但是,是的,原則上你是對的,儘管第二次遍歷只是在較短的,被過濾的列表上,所以通常無關緊要。 – leftaroundabout
第二個版本不是尾遞歸,可能會導致比第一個更多的內存使用。您可以使用(嚴格)累加器將其保存在常量內存中。或者你可以使用'foldl'(如果p a然後succ c else c)0,則可以使用\ c a - >。 – chi
您可能會喜歡[懶惰評估指南](https://hackhands.com/guide-lazy-evaluation-haskell/):D – Carsten