Kotlin 中的扩展函数和尾调用优化 (TCO)
Extension function and tail call optimization (TCO) in Kotlin
我有以下使用 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 不能用于扩展函数,我的假设是它没有被优化由编译器。这个假设是否正确?
since tailrec can't be used in extension functions
你确定吗?
我刚刚通过查看 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 字节码。
我有以下使用 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 不能用于扩展函数,我的假设是它没有被优化由编译器。这个假设是否正确?
since tailrec can't be used in extension functions
你确定吗?
我刚刚通过查看 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 字节码。