【问题标题】:dynamic programming and continuation passing style动态规划和延续传递风格
【发布时间】:2012-12-12 08:59:37
【问题描述】:

对于像斐波那契这样的简单问题,编写 CPS相对简单

let  fibonacciCPS n =
  let rec fibonacci_cont a cont =
    if a <= 2 then cont 1
    else
      fibonacci_cont (a - 2) (fun x ->
        fibonacci_cont (a - 1) (fun y ->
          cont(x + y)))

  fibonacci_cont n (fun x -> x)

但是,在here(或书intro to algo)的截杆示例中,闭包的数量并不总是等于2,并且不能硬编码。

我想必须将中间变量更改为序列。

(我喜欢将延续视为一份合同,说“当你有价值时,把它传给我,然后我会在治疗后把它传给我的老板”或类似的东西,它推迟了实际执行)

对于棒材切割,我们有

//rod cutting
let p = [|1;5;8;9;10;17;17;20;24;30|]

let rec r n = seq { yield p.[n-1]; for i in 1..(n-1) -> (p.[i-1] + r (n-i)) } |> Seq.max 
[1 .. 10] |> List.map (fun i -> i, r i) 

在这种情况下,我需要附加新创建的延续

let cont' = fun (results: _ array) -> cont(seq { yield p.[n-1]; for i in 1..(n-1) -> (p.[i-1] + ks.[n-i]) } |> Seq.max)

返回子问题的“笛卡尔积”延续。 有没有人看过 CPS 版本的切杆/对此有任何提示?

【问题讨论】:

  • 模式匹配不是更直接吗?
  • 最后,我需要在每一步都取一个序列的最大值,所以我看不出它是如何降低噪音的
  • 我认为我需要通过“撰写”操作来“收集”子延续

标签: algorithm recursion f# dynamic-programming continuations


【解决方案1】:

我假设你想显式地 CPS 一切,这意味着像列表理解这样的一些好东西将会丢失(也许使用异步块会有所帮助,我不太了解 F#)——所以从一个简单的递归函数开始:

let rec cutrod (prices: int[]) = function
    | 0 -> 0
    | n -> [1 .. min n (prices.Length - 1)] |>
           List.map (fun i -> prices.[i] + cutrod prices (n - i)) |>
           List.max

很明显,我们需要使用的列表函数的 CPS 版本(map、max,如果您也想 CPS [1..(blah)] 表达式,可能还有一个列表构建函数)。 map 非常有趣,因为它是一个高阶函数,所以它的第一个参数需要修改为采用 CPS ed 函数。这是一个 CPS List.map 的实现:

let rec map_k f list k = 
  match list with
    | [] -> k []
    | x :: xs -> f x (fun y -> map_k f xs (fun ys -> k (y :: ys)))

请注意,map_k 像任何其他 CPS 函数一样调用其参数 f,并将 map_k 中的递归放入延续中。使用 map_k、max_k、gen_k(从 1 到某个值构建一个列表),cut-rod 函数可以进行 CPS 编辑:

let rec cutrod_k (prices: int[]) n k = 
  match n with
    | 0 -> k 0
    | n -> gen_k (min n (prices.Length - 1)) (fun indices ->
               map_k (fun i k -> cutrod_k prices (n - i) (fun ret -> k (prices.[i] + ret))) 
                     indices 
                     (fun totals -> max_k totals k))

【讨论】:

  • 非常有启发性的方法
  • 扩展你的系统方法,向前风格和CP风格之间存在一一对应是有道理的,因此两者之间存在机械转换。你知道任何说明/探索这一点的好论文/链接吗?
  • 特别是在 fsharp 中,可能有人可以使用 CPS 表达式构建器来记账,因为 CPS 似乎是由 monads 概括的(moggi cf mirrors.csl.sri.com/www.brics.dk/%257Ehosc/local/…
  • 也就是说,我的目标是解开递归,以便在递归循环中组合地添加功能,(用 ycombinator 将其重新绑定)在这种情况下,表达式构建器不适合.. 除非我也可以组合地制作它们..
  • 我没有任何具体的链接,但是Schemers经常研究这种东西,所以也许你可以在这里找到一些好东西:http://library.readscheme.org/page6.html。但基本上,CPS 转换有点像编译为低级代码,您可以显式地指定操作顺序,并且显式管理“堆栈”:将 k 包装在新函数中 = push,应用 k = pop/return,传递 k 不变 = 尾调用。
猜你喜欢
  • 2011-06-30
  • 2012-04-19
  • 1970-01-01
  • 2010-12-21
  • 1970-01-01
  • 1970-01-01
  • 2011-12-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多