由于某些特殊原因,我想在大程序中删除所有@tailrec的效果,但是不想手动执行,编译时是否有任何可选参数可以关闭尾递归优化?我想在代码中留下@tailrec,但是不想检查它并在编译时进行尾递归优化。这可能吗?
答案 0 :(得分:6)
您可以使用编译器的-g:notailcalls
选项。
来自文档:
-g:{无,源,线,乏,notailcalls}
“none”不会生成调试信息,
“source”仅生成源文件属性
“line”生成源和行号信息,
“vars”生成源,行号和局部变量信息,
“notailcalls”生成以上所有内容,不会执行尾调用优化。
请注意,正如文档所指定的那样,这也将启用调试信息生成。
一个例子:
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
的{{1}}:
x
与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
上的invokevirtual
。
请注意,顺便说一句,jwvh谨慎地纠正了13
的插入 - 它实际上并没有切换尾部优化,它唯一能做的就是如果它不能通知编译器失败插入尾调用优化,无论如何都会尝试。
答案 1 :(得分:3)
@tailrec
注释不会打开/关闭尾递归优化。如果带注释的例程不正确尾递归,它只会告诉编译器输出错误。
如果例程是尾递归的,则无论是否注释,都会进行优化。
来自docs:
一种方法注释,用于验证是否将编译该方法 尾调用优化。
如果存在,编译器将在方法时发出错误 无法优化成循环。
操作词是“验证”。
<强>更新强>
@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
%%>