cps在“又一个haskell教程”中

时间:2013-07-13 10:23:56

标签: haskell

我正在通过阅读“又一个Haskell教程”这本书来学习haskell,而且我遇到了一个问题,当涉及到contiuation传递风格。这本书给出了一个cps折叠:

cfold’ f z [] = z
cfold’ f z (x:xs) = f x z (\y -> cfold’ f y xs)

并给出测试结果:

CPS> cfold (+) 0 [1,2,3,4]
10
CPS> cfold (:) [] [1,2,3]
[1,2,3]

但是,当我尝试测试时,我发现存在问题,ghci给出了:

*Main> cfold (+) 0 []

<interactive>:8:7:
    Occurs check: cannot construct the infinite type:
      t10 = (t10 -> t10) -> t10
    Expected type: t10 -> t10 -> (t10 -> t10) -> t10
      Actual type: t10 -> t10 -> t10
    In the first argument of `cfold', namely `(+)'
    In the expression: cfold (+) 0 []
    In an equation for `it': it = cfold (+) 0 []

这对我来说很有意义,因此我将cps的定义更改为类似的内容 这样:

cfold f z [] = z
cfold f z (x:xs) = (\y -> cfold f y xs) (f x z)

它运作良好:

*Main> cfold (+) 0 [1,2,3]
6

所以我的问题来了,是书中的错误还是我想念的东西?

2 个答案:

答案 0 :(得分:9)

我认为书中没有错误。本书中的函数cfold'在contiuation传递样式中采用函数,因此传递给cfold'的函数必须采用3个参数:累加器,当前列表元素和继续,要传递给它结果。

(+)只接受两个参数,因为它不是cps样式,所以你不能使用cfold'函数。要使用cfold'对列表的元素求和,您可以写:

> cfold' (\e acc cont -> cont (acc + e)) 0 [3,2,4]
9

请注意,lambda采用三个参数:元素,累加器和延续。根据当前计算的结果,延续是一个表示“下一步该做什么”的函数。因此,通过每次调用continuation,您将处理下一个元素。

那么,当我不打电话给延续时,您认为会发生什么?我可以停止处理列表,实现像takeWhile这样的东西吗?

> cfold' (\acc e cont -> if e <= 3 then cont (e : acc) else acc) [] [1,2..]
[3,2,1]

在此示例中,当元素大于3时,我们不调用continuation,因此我们停止处理列表。否则,我们将元素添加到累加器,这就是列表反向出现的原因。如示例所示,这适用于无限列表。

并且,您还可以编写一个函数,将任何 2参数函数(非cps样式)转换为cps样式:

toCps2 :: (a -> b -> c) -> (a -> b -> (c -> d) -> d)
toCps2 f = \a b c -> c (f a b)

此函数只传递将非cps函数应用于continuation的结果。您可以使用此函数来实现您的cfold,它采用非cps样式的函数来折叠列表:

cfold :: (a -> b -> c) -> b -> [a] -> c
cfold f = cfold' (toCps2 f)

使用此功能,您现在可以使用(+)折叠列表:

> cfold (+) 0 [3,2,4]
9

答案 1 :(得分:2)

又一个Haskell教程也是 cfold 的定义

cfold f z l = cfold' (\x t g -> f x (g t)) z l

此功能 cfold 正在使用 cfold'

在WinHugs环境中,它运行正常。