2013-07-08 52 views
1

这里的评价是一个自定义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)不会evaluted严格来说,Haskell会创建一个thunk,对吗?

这是防止创建thunk的正确方法,从而迫使严格评估(n+1)

length3 = length3' 0 where 
    length3' n [] = n 
    length3' n (x:xs) = length3' (n+1) $! xs 

我将如何实现与seq代替$!同样的效果?

+3

如果你打开优化,你不必做任何事情,因为严格分析可以确定'n'中的定义是严格的。 – augustss

+0

@augustss哦,我不知道。谢谢! – fredoverflow

+0

这就是说,严格信息发生的具体情况取决于类型。如果你使用'Int',它肯定会起作用。 – augustss

回答

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产生直接翻译到核心:

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 

显示,这是在累加器装箱(并因此严格)。

4

我不认为这是非常正确的 - $!是严格的第二个参数,所以你只是强制列表而不是累加器。

可以使用序列像这样得到正确的严格:

let n' = n + 1 in n' `seq` length3' n' xs 

我觉得更可读的版本将使用爆炸模式(一个GHC扩展名):

length3' !n (x:xs) = ...