Kotlin中的扩展功能和尾部呼叫优化(TCO)

时间:2019-03-02 04:18:20

标签: kotlin tail-recursion

我具有使用TCO的以下功能:

tailrec fun superDigit(n: String): Int {
    val sum = n.fold(0) { sum, char -> sum + char.toString().toInt() }
    return if (sum < 10) sum else superDigit(sum.toString())
}

如果我实现了与扩展功能相同的功能,如下所示:

fun String.superDigit(): Int {
    val sum = fold(0) { sum, char -> sum + char.toString().toInt() }
    return if (sum < 10) sum else sum.toString().superDigit()
}

扩展功能的尾部调用也进行了优化吗?

IMO调用扩展函数仍然是一个以this作为参数的常规函数​​调用,因此它仍然是递归调用,并且由于tailrec无法在扩展函数中使用,所以我认为它没有被优化编译器。这个假设正确与否吗?

1 个答案:

答案 0 :(得分:2)

  

因为tailrec不能用于扩展功能

确定吗?

我刚刚通过查看IntelliJ IDEA中的Kotlin字节码对其进行了测试。首先,扩展函数上带有tailrec的代码可以成功编译。更进一步:比较下面的两段Kotlin代码和字节码,一个带有tailrec,另一个没有。

科特琳:

fun Double.tailrecTestExtension(): Double
        = (this - 1.0).tailrecTestExtension()

字节码:

  // access flags 0x19
  public final static tailrecTestExtension(D)D
    // annotable parameter count: 1 (visible)
    // annotable parameter count: 1 (invisible)
   L0
    LINENUMBER 13 L0
    DLOAD 0
    DCONST_1
    DSUB
    INVOKESTATIC com/example/TestKt.tailrecTestExtension (D)D
    DRETURN
   L1
    LOCALVARIABLE $receiver D L0 L1 0
    MAXSTACK = 4
    MAXLOCALS = 2

科特琳:

tailrec fun Double.tailrecTestExtension(): Double
        = (this - 1.0).tailrecTestExtension()

字节码:

  // access flags 0x19
  public final static tailrecTestExtension(D)D
    // annotable parameter count: 1 (visible)
    // annotable parameter count: 1 (invisible)
   L0
    LINENUMBER 13 L0
    DLOAD 0
    DCONST_1
    DSUB
    DSTORE 0
    GOTO L0
   L1
    LOCALVARIABLE $receiver D L0 L1 0
    MAXSTACK = 4
    MAXLOCALS = 2

请注意,在第一个示例中,有一个INVOKESTATIC调用(对应于常规递归),在第二个版本(其对应于一个循环-中,该调用已由常规跳转(GOTO)取代了tailrec引入的预期行为。

注意:我不是Kotlin字节码方面的专家,我的理解是基于有关汇编语言的一些基本知识。在这里,我假设这些知识可以转移到Kotlin字节码中。