有没有办法关闭Scala编译器的尾递归优化?
Is there any way to turn off tail recursion optimization of Scala Compiler?
由于某些特殊原因,我想在一个大程序中删除所有@tailrec的效果,但不想手动这样做,编译时是否有任何可选参数可以关闭尾递归优化?我只想在代码中保留@tailrec,但不想检查它并在编译时进行尾递归优化。这可能吗?
@tailrec
注解没有开启 on/off 尾递归优化。如果带注释的例程不是正确尾递归,它只会告诉编译器出错。
如果例程是尾递归的,那么无论是否注释,优化都在那里。
来自docs:
A method annotation which verifies that the method will be compiled
with tail call optimization.
If it is present, the compiler will issue an error if the method
cannot be optimized into a loop.
关键词是"verifies"。
更新
@mikołak 是正确的。快速检查显示 -g:notailcalls
标志关闭注释验证。
%%> scalac tailrec.scala
tailrec.scala:5: error: could not optimize @tailrec annotated method rf: it contains a recursive call not in tail position
def rf(x: Int): Int = if (x < 1) x else rf(x-2)+1
^
one error found
%%> scalac -g:notailcalls tailrec.scala
%%>
您可以使用编译器的 -g:notailcalls
选项。
来自文档:
-g:{none,source,line,vars,notailcalls}
"none" generates no debugging info,
"source" generates only the source file attribute,
"line" generates source and line number information,
"vars" generates source, line number and local variable information,
"notailcalls" generates all of the above and will not perform tail call optimization.
请注意,如文档所述,这还将启用调试信息生成。
一个例子:
import scala.annotation.tailrec
class A {
@tailrec
final def x(i: Int): Int = if(i == 0) {i;} else {x(i-1)}
}
编译时 javap -p -v A.class
of x
"normally":
public final int x(int);
descriptor: (I)I
flags: ACC_PUBLIC, ACC_FINAL
Code:
stack=2, locals=2, args_size=2
0: iload_1
1: iconst_0
2: if_icmpne 7
5: iload_1
6: ireturn
7: iload_1
8: iconst_1
9: isub
10: istore_1
11: goto 0
相同,当用-g:notailcalls
编译时:
public final int x(int);
descriptor: (I)I
flags: ACC_PUBLIC, ACC_FINAL
Code:
stack=3, locals=2, args_size=2
0: iload_1
1: iconst_0
2: if_icmpne 9
5: iload_1
6: goto 16
9: aload_0
10: iload_1
11: iconst_1
12: isub
13: invokevirtual #12 // Method x:(I)I
16: ireturn
重要的一点是位置 13
上的 invokevirtual
。
请注意,顺便说一下,jwvh 在 @tailrec
的插话中谨慎地纠正了您 - 它实际上并没有切换尾部优化,它唯一做的就是通知编译器失败如果它无法插入尾调用优化,它无论如何都会尝试。
由于某些特殊原因,我想在一个大程序中删除所有@tailrec的效果,但不想手动这样做,编译时是否有任何可选参数可以关闭尾递归优化?我只想在代码中保留@tailrec,但不想检查它并在编译时进行尾递归优化。这可能吗?
@tailrec
注解没有开启 on/off 尾递归优化。如果带注释的例程不是正确尾递归,它只会告诉编译器出错。
如果例程是尾递归的,那么无论是否注释,优化都在那里。
来自docs:
A method annotation which verifies that the method will be compiled with tail call optimization.
If it is present, the compiler will issue an error if the method cannot be optimized into a loop.
关键词是"verifies"。
更新
@mikołak 是正确的。快速检查显示 -g:notailcalls
标志关闭注释验证。
%%> scalac tailrec.scala
tailrec.scala:5: error: could not optimize @tailrec annotated method rf: it contains a recursive call not in tail position
def rf(x: Int): Int = if (x < 1) x else rf(x-2)+1
^
one error found
%%> scalac -g:notailcalls tailrec.scala
%%>
您可以使用编译器的 -g:notailcalls
选项。
来自文档:
-g:{none,source,line,vars,notailcalls}
"none" generates no debugging info,
"source" generates only the source file attribute,
"line" generates source and line number information,
"vars" generates source, line number and local variable information,
"notailcalls" generates all of the above and will not perform tail call optimization.
请注意,如文档所述,这还将启用调试信息生成。
一个例子:
import scala.annotation.tailrec
class A {
@tailrec
final def x(i: Int): Int = if(i == 0) {i;} else {x(i-1)}
}
编译时 javap -p -v A.class
of x
"normally":
public final int x(int);
descriptor: (I)I
flags: ACC_PUBLIC, ACC_FINAL
Code:
stack=2, locals=2, args_size=2
0: iload_1
1: iconst_0
2: if_icmpne 7
5: iload_1
6: ireturn
7: iload_1
8: iconst_1
9: isub
10: istore_1
11: goto 0
相同,当用-g:notailcalls
编译时:
public final int x(int);
descriptor: (I)I
flags: ACC_PUBLIC, ACC_FINAL
Code:
stack=3, locals=2, args_size=2
0: iload_1
1: iconst_0
2: if_icmpne 9
5: iload_1
6: goto 16
9: aload_0
10: iload_1
11: iconst_1
12: isub
13: invokevirtual #12 // Method x:(I)I
16: ireturn
重要的一点是位置 13
上的 invokevirtual
。
请注意,顺便说一下,jwvh 在 @tailrec
的插话中谨慎地纠正了您 - 它实际上并没有切换尾部优化,它唯一做的就是通知编译器失败如果它无法插入尾调用优化,它无论如何都会尝试。