严格评估整数累加器

时间:2013-07-08 12:21:01

标签: haskell lazy-evaluation strictness

以下是自定义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代替$!达到相同的效果?

2 个答案:

答案 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

显示它在累加器中是未装箱的(因而是严格的)。