OCaml编译器会处理布尔运算符以使递归尾部递归吗?
让我们看一看下面的函数OCaml编译器会处理布尔运算符以使递归尾部递归吗?,ocaml,Ocaml,让我们看一看下面的函数is_prime: let is_prime n = let rec div_check i = i * i > n || (n mod i <> 0 && div_check (i+1)) in n >= 2 && div_check 2 设为素数= 让rec div|u check i=i*i>n||(n mod i 0&&div|u check(i+1)) 在里面 n>=2&&div\u检查2 因
is_prime
:
let is_prime n =
let rec div_check i = i * i > n || (n mod i <> 0 && div_check (i+1))
in
n >= 2 && div_check 2
设为素数=
让rec div|u check i=i*i>n||(n mod i 0&&div|u check(i+1))
在里面
n>=2&&div\u检查2
因此,如果nmodi0
为false,则它将停止
但是如果nmodi0
为真,那么它将递归地继续
我的问题是,如果它继续,OCaml是否优化了编译器,因此只直接返回div\u check(i+1)
,即尾部递归?或者它是否仍会保留true&&
部分并等待div\u check
返回
ps该函数取自答案是肯定的。我简化了您的示例,以使编译器输出更易于理解:
let rec is_prime n =
let rec div_check i =
i * i > n || (i + i < n && div_check (i+1)) in
div_check n
让rec为素数=
让记录div_检查i=
i*i>n | |(i+i
给定此输入,编译器将发出以下程序集(我添加了一些注释):
camlIs\u prime\u div\u check\u 1010:
.L102:
movq 16(%rbx),%rdi
movq%rax,%rsi
sarq$1,%rsi
movq%rax,%rdx
decq%rdx
imulq%rsi,%rdx;我,我
增量%rdx
cmpq%rdi,%rdx;如果i*i=n,则返回
addq$2,%rax;否则i:=i+1
jmp.L102;第一分区支票
.L100:
movq$1,%rax
ret
但要小心,这只适用于香草OCaml的短路操作员。
即使是无害的更改,例如,let(&&&=(&&&)
也会这样做,jmp.L102
,也会被call camlIs\u prime\u div\u check.
所取代
此外,这仅在呼叫位于短路操作员右侧时有效。左表达式为非尾部递归表达式。我已经对它进行了测试,实际上,将div\u check
放在&&
操作符的左侧将发出非尾部递归代码
如果您怀疑调用是否为tail,您可以始终向编译器添加
-annot
标志,并查看相应的*.annot
文件,以获得调用(tail)
注释。merlin中对此提供了一些工具支持,但我还没有弄清楚如何正确使用它。请记住,最终评判仍然是装配输出 i+i>n是否可以替换n mod i 0?只是为了表明短路调用是尾部递归的,函数的语义当然会改变。如果我加回nmodi0
,编译器将发出一些键击,这些键击也将检查除法是否为零,否则,调用仍然是尾部递归的。
camlIs_prime__div_check_1010:
.L102:
movq 16(%rbx), %rdi
movq %rax, %rsi
sarq $1, %rsi
movq %rax, %rdx
decq %rdx
imulq %rsi, %rdx ; i * i
incq %rdx
cmpq %rdi, %rdx ; if i * i <= n then return
jle .L101
movq $3, %rax
ret
.L101:
movq 16(%rbx), %rdi
leaq -1(%rax, %rax), %rsi ; i + i
cmpq %rdi, %rsi ; i + i ? n
jge .L100 ; if i + i >= n then return
addq $2, %rax ; else i := i + 1
jmp .L102 ; div_check i
.L100:
movq $1, %rax
ret