【问题标题】:Is there any way to turn off tail recursion optimization of Scala Compiler?有什么办法可以关闭Scala编译器的尾递归优化?
【发布时间】:2015-09-01 06:24:43
【问题描述】:

由于一些特殊的原因,我想在一个大程序中去掉所有@tailrec的效果,但又不想手动做,编译时有没有可选的参数可以关闭尾递归优化?我只想将@tailrec 留在代码中,但不想在编译时检查它并进行尾递归优化。这可能吗?

【问题讨论】:

  • 非常不清楚你在问什么。首先,您说要删除所有@tailrec 注释,然后您说要保留它们。是哪个?另外,你谈到关闭尾递归优化,这与@tailrec注解完全无关。
  • 对不起,这是我的错。我的意思是“将@tailrec 留在代码中,但消除它们的影响”
  • @tailrec 注释的唯一 效果是编译器会告诉你代码是否是尾递归的。所以,你仍然希望你的代码得到tailrec优化,你只是不想被告知不是尾递归的代码?
  • 我不希望编译器在带有@tailrec 的函数不是尾递归时发出错误。

标签: scala tail-recursion


【解决方案1】:

您可以使用编译器的-g:notailcalls 选项。

来自文档:

-g:{none,source,line,vars,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 of 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 位置13


请注意,顺便说一句,jwvh 谨慎地纠正了您对@tailrec 的解释——它实际上并没有切换尾部优化,它唯一的作用是通知编译器如果它不能插入尾调用优化,无论如何都会尝试

【讨论】:

    【解决方案2】:

    @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 
    %%>
    

    【讨论】:

    • 谢谢,但是,我的意思是我们可以在编译时忽略@tailrec 吗? scala 编译器是否为我们提供了一个选项?
    • 哦,你的意思是你想告诉编译器,“请忽略 @tailrec 注释。我不在乎是否有不是真正的尾递归。”嗯,这是个好问题。
    • 我认为 OP 想要完全停止编译器进行尾递归优化(大概是为了获得有意义的堆栈跟踪)。无论如何,这就是问题的标题所说的
    • 无论如何都支持这一点,因为 OP 显然对 @tailrec 的实际作用感到困惑。
    猜你喜欢
    • 1970-01-01
    • 2015-06-16
    • 2020-06-13
    • 2021-11-23
    • 1970-01-01
    • 1970-01-01
    • 2010-09-07
    • 1970-01-01
    相关资源
    最近更新 更多