F#尾递归和列表继续

时间:2015-05-28 12:45:19

标签: f# tail-recursion continuation

在准备考试时,我正在努力完成F#中的一些作业。

作业说:

  

考虑以下F#声明:

let rec f i = 
    function
    | [] -> [i]
    | x::xs -> i+x :: f (i+1) xs
     

f的类型为int -> int list -> int list。表达式f 10 [0;1;2;3]返回值[10;12;14;16;14]

     

函数f不是尾递归。使用累积参数声明fA的尾递归变体f

     

     

声明fC的基于延续的尾递归变体f

到目前为止,我尝试过类似的事情:

let fA i = 
    let rec loop acc = function
        | [] -> acc
        | x::xs -> loop (i+x::acc) xs
    loop i []

但我不明白为什么不与这些人合作。我知道我对这个有了更深刻的理解,所以现在我尝试了所有的大脑。

2 个答案:

答案 0 :(得分:1)

累加器

嗯,你几乎就在那里 - 首先你可能还记得原来的f有2个参数 - 所以你可能应该添加另一个:

let fA i xs = ...

然后原始更改i - 因此你也应该(将它添加到循环中):

let fA i xs = 
    let rec loop i acc = function

然后你几乎就在那里 - 你只需要用正确的参数调用loop,也许你有一个命令 -problem ...继续尝试:D

啊是的 - 正如@Sehnsucht所说 - 在某处您需要i+1在那里...记得为什么你应该把i带到你的loop ....

下一个提示

好的,您的acc似乎有些问题 - 这里还有一行 - 您可以看到几乎没有任何变化:

let fA i xs = 
    let rec loop i acc = function
        | ???
        | x::xs -> loop (???) (???::acc) xs
    ???

显然你必须在???个地方插入(不同的)东西:D

如果您仍然遇到麻烦,可以获得这样的编译版本:

let fA i xs = 
    let rec loop i acc = function
        | [] -> acc
        | x::xs -> loop i (x::acc) xs
    loop i [] xs

当然这不会正常工作,但会启动你的东西

继续

你可能已经猜到了 - 从基于累加器到基于延续的方式并不是不同的(实际上这可能更容易 - 取决于你对向后 - 思考的使用方式) :

再次开始:

let fC i xs =
    let rec loop i cont = function

如果遇到问题,也许你应该让编译器帮助你一点 - 为此添加cont这样的类型:

let fC i xs =
    let rec loop i (cont : int list -> int list) = function

现在请记住,随着时间的推移你必须创建新的延续 - 像(fun res -> ...something... |> cont)那样作为新的延续传递。将res视为使用列表的其余部分(您的xs)执行我的操作的结果,那么它应该很容易。

对于第一次延续,你很可能根本不想做任何事......但这几乎总是一样的,所以你可能马上就知道了。

一些意味着点你的老师投入

  • [] -> [i]可能很讨厌... 你现在错过了;) - 一旦你有了编译的东西(应该是你的第一个关注点)你会很快想出来我想
  • i+xi+1 ...不要忘记;)
PS:我不想太多地破坏你的作业 - 我以后会把它变成一个完整的答案 - 但是对于单一的评论而言,这是非常/不可读的IMO

答案 1 :(得分:1)

我刚刚遇到这个问题,并认为这对我来说也很好。我分享了解决方案,希望能帮到某人。请把它当作一个简单的建议,而不是最好的解决方案。我只是一个没有计算机科学教育的F#仰慕者。

请注意我使用

let f list =
  match list with
  | ...

而不是原始的,更简洁的

let f =
  function
  | ...

因为前一种语法使f的参数可见。

工作解决方案在这里:

// Original version
let rec f i list = 
  match list with
  | [] -> [i]
  | x::xs -> i+x :: f (i+1) xs

// Tail recursive version with accumulator
let fA i list =
  let rec loop i acc list =
    match list with
    | x::xs ->
      let newI = i + 1
      let newAcc = x + i :: acc
      let newList = xs
      loop newI newAcc newList
    | [] -> List.rev (i::acc)
  loop i [] list

// Continuation based version
let fC i list =
  let rec loop i (cont:int list -> int list) list =
    match list with 
    | x::xs ->
      let newI = i + 1
      let newCont = fun res -> cont (x + i :: res)
      let newList = xs
      loop newI newCont newList
    | [] -> cont (i::list) 
  loop i id list

// All these expressions evaluate to [10; 12; 14; 16; 14]
let res1 = f 10 [0..3]
let res2 = fA 10 [0..3]
let res3 = fC 10 [0..3]

如果可以改进其中一种解决方案,我将不胜感激。