【问题标题】:How to convert CPS-style gcd computation to use the Continuation Monad如何将 CPS 风格的 gcd 计算转换为使用 Continuation Monad
【发布时间】:2015-12-16 13:19:38
【问题描述】:

让我们考虑以下 Continuation monad 的实现,用于 CPS 样式的计算产生和整数:

module Cont : sig
  type 'a t = ('a -> int) -> int
  val return : 'a -> 'a t
  val bind : 'a t -> ('a -> 'b t) -> 'b t
  val callCC: (('a -> 'b t) -> 'a t) -> 'a t
end = struct
  type 'a t = ('a -> int) -> int

  let return x =
    fun cont -> cont x

  let bind m f =
    fun cont -> m (fun x -> (f x) cont)

  let callCC k =
    fun cont -> k (fun x -> (fun _ -> cont x)) cont
end

我们如何重写 gcd 计算的 CPS 风格的实现(参见 How to memoize recursive functions?),尤其是利用 Cont monad 的记忆?

定义后

let gcd_cont k (a,b) =
  let (q, r) = (a / b, a mod b) in
  if r = 0 then Cont.return b else k (b,r)

我尝试使用类型求解器来提示我记忆函数应该具有的类型:

# let gcd memo ((a,b):int * int) =
  Cont.callCC (memo gcd_cont (a,b)) (fun x -> x)
;;
    val gcd :
  (((int * int -> int Cont.t) -> int * int -> int Cont.t) ->
   int * int -> (int -> 'a Cont.t) -> int Cont.t) ->
  int * int -> int = <fun>

但是我无法将这个提示转化为实际的实现。有人能做到这一点吗?在记忆函数中使用“callCC”背后的逻辑是,如果在缓存中找到一个值,那么这是一个提前退出条件。

【问题讨论】:

    标签: ocaml monads continuations


    【解决方案1】:

    我觉得问题在于,在他对How to memoize recursive functions? 的回答中,Michael 称 CPS 风格不是 CPS 风格。在 CPS 样式中,每当想要返回一个值时,都会使用额外的延续参数 k - 然后将该值应用于 k

    这不是我们真正想要的,也不是实现的:

    let gcd_cont k (a,b) =
      let (q, r) = (a / b, a mod b) in
      if r = 0 then b else k (b,r)
    

    这里k不是用来返回的(b是直接返回的),它是用来代替递归调用的。这展开了递归:在gcd_cont 中,可以将k 视为gcd_cont 本身,就像使用let rec 一样。稍后,gcd_cont 可以使用定点组合器转换为真正的递归函数,基本上“将其提供给自身”:

    let rec fix f x = f (fix f) x
    let gcd = fix gcd_cont
    

    (这相当于Michael定义的call函数)

    与直接使用let rec 定义gcd 的区别在于,具有展开递归的版本允许人们“检测”递归调用,因为递归本身是由定点组合器执行的。这就是我们想要的记忆:如果结果不在缓存中,我们只想执行递归。因此 memo 组合子的定义。

    如果函数使用let rec 定义,则递归在定义函数的同时关闭,因此无法检测递归调用站点以插入记忆。

    作为旁注,这两个答案基本上实现了相同的事情:唯一的区别是它们在定点组合器中实现递归的方式:迈克尔的定点组合器使用let rec,杰克逊的一个使用参考,即“兰丁的结” — 如果您的语言中有引用,则另一种实现递归的方法。

    所以,总而言之,我想说在 continuation monad 中实现它是不可能的/没有真正意义,因为这件事一开始就不是 CPS。

    【讨论】:

    • 我不喜欢命名 CPS 什么不是。函数的“可测量”版本的特定形式是否有任何规范名称?
    猜你喜欢
    • 2011-09-29
    • 2016-01-24
    • 2020-03-26
    • 1970-01-01
    • 2019-12-13
    • 1970-01-01
    • 2019-10-03
    • 2020-07-31
    • 2019-11-27
    相关资源
    最近更新 更多