动态编程和延续传递风格

时间:2012-12-26 21:55:58

标签: algorithm recursion f# dynamic-programming continuations

对于像斐波那契这样的简单问题,编写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版的切杆/对此有任何提示?

1 个答案:

答案 0 :(得分:2)

我假设你想要明确地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中的递归放入continuation中。使用map_k,max_k,gen_k(将列表从1构建到某个值),切杆功能可以通过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))