了解 F# 尾递归
最近在学习F#。 我尝试以不同的方式解决问题。 像这样:
(*
[0;1;2;3;4;5;6;7;8] -> [(0,1,2);(3,4,5);(6,7,8)]
*)
//head-recursive
let rec toTriplet_v1 list=
match list with
| a::b::c::t -> (a,b,c)::(toTriplet_v1 t)
| _ -> []
//tail-recursive
let toTriplet_v2 list=
let rec loop lst acc=
match lst with
| a::b::c::t -> loop t ((a,b,c)::acc)
| _ -> acc
loop list []
//tail-recursive(???)
let toTriplet_v3 list=
let rec loop lst accfun=
match lst with
| a::b::c::t -> loop t (fun ls -> accfun ((a,b,c)::ls))
| _ -> accfun []
loop list (fun x -> x)
let funs = [toTriplet_v1; toTriplet_v2; toTriplet_v3];
funs |> List.map (fun x -> x [0..8]) |> List.iteri (fun i x -> printfn "V%d : %A" (i+1) x)
我想V2和V3的结果应该是一样的。 但是,我得到以下结果:
V1 : [(0, 1, 2); (3, 4, 5); (6, 7, 8)]
V2 : [(6, 7, 8); (3, 4, 5); (0, 1, 2)]
V3 : [(0, 1, 2); (3, 4, 5); (6, 7, 8)]
为什么 V2 和 V3 的结果不同?
Recently, I'm learning F#.
I try to solve problem in different ways.
Like this:
(*
[0;1;2;3;4;5;6;7;8] -> [(0,1,2);(3,4,5);(6,7,8)]
*)
//head-recursive
let rec toTriplet_v1 list=
match list with
| a::b::c::t -> (a,b,c)::(toTriplet_v1 t)
| _ -> []
//tail-recursive
let toTriplet_v2 list=
let rec loop lst acc=
match lst with
| a::b::c::t -> loop t ((a,b,c)::acc)
| _ -> acc
loop list []
//tail-recursive(???)
let toTriplet_v3 list=
let rec loop lst accfun=
match lst with
| a::b::c::t -> loop t (fun ls -> accfun ((a,b,c)::ls))
| _ -> accfun []
loop list (fun x -> x)
let funs = [toTriplet_v1; toTriplet_v2; toTriplet_v3];
funs |> List.map (fun x -> x [0..8]) |> List.iteri (fun i x -> printfn "V%d : %A" (i+1) x)
I thought the results of V2 and V3 should be the same.
But, I get the result below:
V1 : [(0, 1, 2); (3, 4, 5); (6, 7, 8)]
V2 : [(6, 7, 8); (3, 4, 5); (0, 1, 2)]
V3 : [(0, 1, 2); (3, 4, 5); (6, 7, 8)]
Why the results of V2 and V3 are different?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
V2 使用标准累积变量来完成尾递归:
V3 使用延续 ,或者用简单的英语来说,一个累积函数:
您可以看到累积变量和累积函数之间的区别:
使用累积变量在最后一次调用时停止,因为累积变量存储答案。然而,累积函数在最后一次调用之后仍然执行一些回溯工作。应该注意的是,使用累加函数确实是尾递归,因为递归调用
loop t (fun ls -> accfun ((a,b,c)::ls))
实际上是最后语句。顺便说一句,您提供的代码是展示尾递归函数概念的一个很好的例子。理解这些示例代码的一种方法是处理小案例,就像我在上面两个插图中所做的那样。经过一些小案例的练习,你会对这个概念有更深入的理解。
V2 uses standard accumulating variable to get tail recursion done:
V3 uses continuation, or in plain English, an accumulating function:
You can see the difference between accumulating variables and accumulating functions:
Using accumulating variables stops at the last call as the accumulating variable stores the answer. However, the accumulating function still does some backtrack work after the last call. It should be noticed that using accumulating functions is indeed tail recursive as the recursive call
loop t (fun ls -> accfun ((a,b,c)::ls))
is actually the last statement of this function.Btw, the code you provided is a good example to show the concept tail recursive functions. One way to understand these sample code is to work on small cases as I did in the above two illustrations. After working on some small cases, you will understand the concept deeper.