我的rec函数是尾递归的吗?
这个函数是尾递归的吗?
let rec rec_algo1 step J =
if step = dSs then J
else
let a = Array.init (Array2D.length1 M) (fun i -> minby1J i M J)
let argmin = a|> Array.minBy snd |> fst
rec_algo1 (step+1) (argmin::J)
一般来说,有没有办法正式检查它?
谢谢。
Is this function tail-recursive ?
let rec rec_algo1 step J =
if step = dSs then J
else
let a = Array.init (Array2D.length1 M) (fun i -> minby1J i M J)
let argmin = a|> Array.minBy snd |> fst
rec_algo1 (step+1) (argmin::J)
In general, is there a way to formally check it ?
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
该函数是尾递归的;我用肉眼就能看出来。
一般来说,这并不总是容易判断。也许最可靠/务实的事情就是在大输入上检查它(并确保您在“发布”模式下进行编译,因为“调试”模式会关闭尾部调用以实现更好的调试)。
This function is tail-recursive; I can tell by eyeballing it.
In general it is not always easy to tell. Perhaps the most reliable/pragmatic thing is just to check it on a large input (and make sure you are compiling in 'Release' mode, as 'Debug' mode turns off tail calls for better debugging).
是的,您可以正式证明函数是尾递归的。每个表达式归约都有一个尾位置,如果所有递归都位于尾位置,则该函数是尾递归的。函数有可能在一个地方是尾递归的,但在另一个地方却不是。
在表达式
let pat = exprA in exprB
中,只有exprB
位于尾部位置。也就是说,虽然您可以去评估exprA
,但您仍然必须回来评估exprB
并牢记exprA
。对于该语言中的每个表达式,都有一个归约规则告诉您尾部位置在哪里。在ExprA中; ExprB 又是ExprB
。在if ExprA then ExprB else ExprC
中,它既是ExprB
又是ExprC
等等。编译器当然知道这一点。然而,F# 中提供了许多可用的表达式,以及编译器在运行过程中执行的许多内部优化,例如在模式匹配编译期间、
seq{}
或async{}
等计算表达式> 可以使知道哪些表达式位于尾部位置变得不明显。实际上,通过一些实践,小函数很容易通过查看嵌套表达式并检查不在函数调用尾部位置的槽来确定尾部调用。 (请记住,尾部调用可能是另一个函数!)
Yes, you can formally prove that a function is tail-recursive. Every expression reduction has a tail-position, and if all recursions are in tail-positions then the function is tail-recursive. It's possible for a function to be tail-recursive in one place, but not in another.
In the expression
let pat = exprA in exprB
onlyexprB
is in tail-position. That is, while you can go evaluateexprA
, you still have to come back to evaluateexprB
withexprA
in mind. For every expression in the language, there's a reduction rule that tells you where the tail position is. InExprA; ExprB
it'sExprB
again. Inif ExprA then ExprB else ExprC
it's bothExprB
andExprC
and so on.The compiler of course knows this as it goes. However the many expressions available in F# and the many internal optimizations carried out by the compiler as it goes, e.g. during pattern match compiling, computation expressions like
seq{}
orasync{}
can make knowing which expressions are in tail-position non-obvious.Practically speaking, with some practice it's easy for small functions to determine a tail call by just looking at your nested expressions and checking the slots which are NOT in tail positions for function calls. (Remember that a tail call may be to another function!)
你问我们如何正式检查这一点,以便我尝试一下。我们首先必须定义函数尾递归的含义。 该形式的递归函数定义
如果
e
内对f
的所有调用都是尾调用,则 是尾递归的 - 即。发生在尾部上下文中。 尾部上下文C
被归纳地定义为带有空洞[]
的术语:其中
e
是 F# 表达式,x
是一个变量,p
是一个模式。我们应该将其扩展为相互递归函数定义,但我将其作为练习。现在让我们将其应用到您的示例中。函数体中对
rec_algo1
的唯一调用是在此上下文中:并且由于这是尾上下文,因此该函数是尾递归的。这就是函数式程序员观察它的方式 - 扫描定义的主体以查找递归调用,然后验证每个调用是否出现在尾部上下文中。尾部调用的一个更直观的定义是,除了返回调用结果之外,没有对调用结果执行任何其他操作。
You asked how we can formally check this so I'll have a stab. We first have to define what it means for a function to be tail-recursive. A recursive function definition of the form
is tail recursive if all calls of
f
insidee
are tail calls - ie. occur in a tail context. A tail contextC
is defined inductively as a term with a hole[]
:where
e
is an F# expression,x
is a variable andp
is a pattern. We ought to expand this to mutually recursive function definitions but I'll leave that as an exercise.Lets now apply this to your example. The only call to
rec_algo1
in the body of the function is in this context:And since this is a tail context, the function is tail-recursive. This is how functional programmers eyeball it - scan the body of the definition for recursive calls and then verify that each occurs in a tail context. A more intuitive definition of a tail call is when nothing else is done with the result of the call apart from returning it.