Ite*_*ter 2 f# tail-recursion tail-call
一旦编译并运行,这将表现为尾调用吗?
let rec f accu = function
| [] -> accu
| h::t -> (h + accu) |> f <| t
Run Code Online (Sandbox Code Playgroud)
也许有一种简单的方法来测试我不知道的行为,但这可能是另一个问题.
如果您不使用流水线操作符,我认为这更容易看到.实际上,两个流水线操作符被定义为inline
,所以编译器会将代码简化为以下(我认为这个版本也更易读,更容易理解,所以我会写这个):
let rec f accu = function
| [] -> accu
| h::t -> f (h + accu) t
Run Code Online (Sandbox Code Playgroud)
尾调用是在另一个过程中作为其最终动作发生的子例程调用; 它可能会产生一个返回值,然后由调用过程立即返回.
所以是的,f
最后一行的呼叫是尾调用.
如果你想分析原始表达式(h + accu) |> f <| t
(不知道流水线操作符是内联的),那么这实际上就变成了((h + accu) |> f) <| t
.这意味着表达式<|
使用两个参数调用运算符并返回结果 - 因此调用<|
是尾调用.
该<|
运营商的定义是这样的:
let (<|) f a = f a
Run Code Online (Sandbox Code Playgroud)
现在,对f
流水线操作符内部的调用也是尾调用(对于其他流水线操作符也是如此).
总之,如果编译器没有进行内联,你将有一个三个尾调用的序列(使用.NET .tail
指令编译,以确保.NET执行尾调用).但是,由于编译器执行内联,它会看到你有一个递归的尾调用(f
调用f
),这可以更有效地编译成循环.(但是跨多个函数或运算符的调用不能轻易使用循环.)