Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# 如何知道函数在F中是否是尾部递归的#_F#_Recursion_Tail Recursion_Tail Call Optimization - Fatal编程技术网

F# 如何知道函数在F中是否是尾部递归的#

F# 如何知道函数在F中是否是尾部递归的#,f#,recursion,tail-recursion,tail-call-optimization,F#,Recursion,Tail Recursion,Tail Call Optimization,我编写了以下函数: let str2lst str = let rec f s acc = match s with | "" -> acc | _ -> f (s.Substring 1) (s.[0]::acc) f str [] 我怎么知道F#编译器是否将其转换为循环?有没有一种不用反射器就能找到答案的方法(我没有反射器的经验,也不知道C#) 编辑:另外,是否可以在不使用内部函数的情况下编写尾部递归函数,或者循环是

我编写了以下函数:

let str2lst str =
    let rec f s acc =
      match s with
        | "" -> acc
        | _  -> f (s.Substring 1) (s.[0]::acc)
    f str []
我怎么知道F#编译器是否将其转换为循环?有没有一种不用反射器就能找到答案的方法(我没有反射器的经验,也不知道C#)

编辑:另外,是否可以在不使用内部函数的情况下编写尾部递归函数,或者循环是否需要驻留在其中


此外,F#std lib中是否有一个函数可以多次运行给定的函数,每次都将最后一次输出作为输入?假设我有一个字符串,我想在字符串上运行一个函数,然后在结果字符串上再次运行它,依此类推……

不幸的是,没有简单的方法

阅读源代码、使用类型并通过检查确定某件事是否是尾部调用并不难(这是“最后一件事”,而不是在“尝试”块中),但人们会猜测自己并犯错误。没有简单的自动化方法(除了检查生成的代码)

当然,您可以在一大块测试数据上尝试您的函数,看看它是否崩溃

F#编译器将为所有尾部调用生成.tail IL指令(除非使用了关闭它们的编译器标志-用于在需要保留堆栈帧进行调试时),但直接尾部递归函数将优化为循环的情况除外。(编辑:我认为现在F#编译器也无法发出.tail,因为它可以证明调用站点中没有递归循环;这是一种优化,因为在许多平台上.tail操作码稍微慢一些。)

“tailcall”是一个保留的关键字,其思想是F#的未来版本可能允许您编写

tailcall func args
如果不是尾部调用,则会收到警告/错误

只有不是自然尾部递归的函数(因此需要额外的累加器参数)才会“强迫”您使用“内部函数”习惯用法

以下是您所问问题的代码示例:

let rec nTimes n f x =
    if n = 0 then
        x
    else
        nTimes (n-1) f (f x)

let r = nTimes 3 (fun s -> s ^ " is a rose") "A rose"
printfn "%s" r

我喜欢Paul Graham在On Lisp中阐述的经验法则:如果还有工作要做,例如操作递归调用输出,那么调用就不是尾部递归。

“tailcall”很有趣,因为它让您指定了调用的位置,而不是对整个函数更有用的“tailrec”声明。你能有一个“部分尾部递归”函数吗?@StephenSwensen你绝对能。控制流分支可能导致尾部调用和非尾部调用之间的替代,并且您可能只希望在编译时检查实际上是尾部调用的调用
让rec f x=如果x>0,那么f(1+x)否则0+f(1+x)
说明了这个概念,尽管它显然不会终止。另请参见