既然你已经标记此哈斯克尔,我会在这方面的回答:在Haskell,做CPS变换相当于在Cont
单子,其转换价值x
成高阶函数工作是采用一个参数并将其应用于x
。
所以,首先,这里是1 + 2规则哈斯克尔:(1 + 2)
这里,它是在延续单子:
contAdd x y = do x' <- x
y' <- y
return $ x' + y'
...不是非常翔实。为了看看发生了什么,让我们拆开monad。首先,除去do
符号:
contAdd x y = x >>= (\x' -> y >>= (\y' -> return $ x' + y'))
的return
函数升降机一个值到单子,并且在这种情况下被实施为\x k -> k x
,或使用中缀运算符截面\x -> ($ x)
。
contAdd x y = x >>= (\x' -> y >>= (\y' -> ($ x' + y')))
的(>>=)
操作(读“绑定”)链一起计算在单子,并且在这种情况下被实现为\m f k -> m (\x -> f x k)
。更改绑定功能,以前缀的形式,并在拉姆达取代,再加上一些重命名为清楚:
contAdd x y = (\m1 f1 k1 -> m1 (\a1 -> f1 a1 k1)) x (\x' -> (\m2 f2 k2 -> m2 (\a2 -> f2 a2 k2)) y (\y' -> ($ x' + y')))
减少一些功能的应用:
contAdd x y = (\k1 -> x (\a1 -> (\x' -> (\k2 -> y (\a2 -> (\y' -> ($ x' + y')) a2 k2))) a1 k1))
contAdd x y = (\k1 -> x (\a1 -> y (\a2 -> ($ a1 + a2) k1)))
,有点最后的清理和重命名:
contAdd x y = \k -> x (\x' -> y (\y' -> k $ x' + y'))
换句话说:函数的参数已经从数字变成了函数,该函数需要一个数字并返回整个表达式的最终结果,像你所期望的那样。
编辑:一位评论者指出,contAdd
本身仍然采用咖喱风格的两个参数。这是明智的,因为它不直接使用延续,但不是必需的。如果不这样做,你需要先打散参数的函数关系:
contAdd x = x >>= (\x' -> return (\y -> y >>= (\y' -> return $ x' + y')))
,然后用它是这样的:
foo = do f <- contAdd (return 1)
r <- f (return 2)
return r
注意,这是从早期版本确实没有不同;它只是将每个部分应用程序的结果打包为一个延续,而不仅仅是最终结果。由于函数是第一类值,因此保持数字的CPS表达式与保持函数的CPS表达式之间没有显着差异。
请记住,我在这里以非常冗长的方式写出所有步骤,以明确所有步骤,其中某些东西是连续传递样式。
附录:您可能会注意到,最终表达式看起来与monadic表达式的脱糖版非常相似。这并不是巧合,因为单调表达式的内在嵌套性使得他们根据先前的值改变计算结构与延续传递风格密切相关;在这两种情况下,你都有某种意义上的因果关系的概念。
也许我是唯一一个不知道CPS在这方面意味着什么的人。 :-) 或者可能不是。如果不是:http://en.wikipedia.org/wiki/Continuation-passing_style – 2010-12-22 16:56:12
请注意,CPS有两种形式:它是范例(例如Cont)或实现细节(例如Codensity)。前者当然会将每个函数转换为函数函数,即它增加了另一个参数。在后者中,它就是如何在引擎盖下实现的。一些语言在所有地方使用CPS,同时提供非CPS接口,Scheme是典型示例。但是,大多数Scheme实现继续前进,并将该实现细节公开为一种名为call/cc的语言功能。 – ertes 2013-06-02 07:42:18