执行时这是一个尾调?

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)

也许有一种简单的方法来测试我不知道的行为,但这可能是另一个问题.

Tom*_*cek 6

如果您不使用流水线操作符,我认为这更容易看到.实际上,两个流水线操作符被定义为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),这可以更有效地编译成循环.(但是跨多个函数或运算符的调用不能轻易使用循环.)