tail rec kotlin list

时间:2017-11-13 01:17:39

标签: functional-programming kotlin tail-recursion tail-call-optimization

我正试图做一些可能导致Kotlin中的StackOverflow的操作。

知道这一点,我记得Kotlin支持tailrec函数,所以我试着这样做:

private tailrec fun Turn.debuffPhase(): List<Turn> {
    val turns = listOf(this)
    if (facts.debuff == 0 || knight.damage == 0) {
        return turns
    }

    // Recursively find all possible thresholds of debuffing
    return turns + debuff(debuffsForNextThreshold()).debuffPhase()
}

令我惊讶的是,IDEA并未将其识别为tailrec,我试图将其解除为扩展功能并使其成为正常功能:

private tailrec fun debuffPhase(turn: Turn): List<Turn> {
    val turns = listOf(turn)
    if (turn.facts.debuff == 0 || turn.knight.damage == 0) {
        return turns
    }

    // Recursively find all possible thresholds of debuffing
    val newTurn = turn.debuff(turn.debuffsForNextThreshold())
    return turns + debuffPhase(newTurn)
}

即便如此,它也不被接受。重要的是,最后一个函数调用是否属于同一个函数?我知道+List plus函数的符号,但是它应该有所作为吗?我在互联网上看到的用于尾部调用其他语言的所有示例都允许这些actions

我也试图用Int做到这一点,这似乎比添加到列表更常用,但结果相同:

private tailrec fun discoverBuffsNeeded(dragon: RPGChar): Int {
    val buffedDragon = dragon.buff(buff)
    if (dragon.turnsToKill(initKnight) < 1 + buffedDragon.turnsToKill(initKnight)) {
        return 0
    }

    return 1 + discoverBuffsNeeded(buffedDragon)
}

所有这些实现是否都不允许尾调用?我想到了其他一些方法来解决这个问题(比如将列表作为参数传递给MutableList),但是在可能的情况下我会尽量避免在函数内部发送集合,这似乎应该是有可能。

PS:关于问题计划,我实施了problem的解决方案。

2 个答案:

答案 0 :(得分:4)

您的所有示例都不是尾递归的。

尾调用是子例程中的最后一个调用。递归调用是对子程序的调用。尾递归调用是对子程序的尾调用。

在所有示例中,尾部调用是+,而不是子例程。所以,所有这些都是递归的(因为它们自称),并且所有这些都有尾调用(因为每个子例程总是有#34;最后一次调用&#34;),但它们都不是尾递归的(因为递归呼叫不是最后一次通话。

中缀符号有时会模糊尾部调用的内容,当您以前缀形式或方法调用编写每个操作时更容易看到:

return plus(turns, debuff(debuffsForNextThreshold()).debuffPhase())
// or
return turns.plus(debuff(debuffsForNextThreshold()).debuffPhase())

现在变得更容易看到对debuffPhase的调用不在尾部位置,而是调用plus(即+)处于尾部位置。如果Kotlin有一般的尾调用,那么 plus的调用确实会被消除,但是AFAIK,Kotlin只有尾递归(比如Scala),所以它不会。

答案 1 :(得分:0)

在不给出谜题的答案的情况下,这是一个非尾递归函数。

fun fac(n: Int): Int =
    if (n <= 1) 1 else n * fac(n - 1)

这不是尾递归,因为递归调用不在尾部位置,正如Jörg的答案所指出的那样。

可以使用CPS

将其转换为尾递归函数
tailrec fun fac2(n: Int, k: Int = 1): Int =
    if (n <= 1) k else fac2(n - 1, n * k)

虽然更好的界面可能会隐藏私人帮助函数的延续。

fun fac3(n: Int): Int {
    tailrec fun fac_continue(n: Int, k: Int): Int =
        if (n <= 1) k else fac_continue(n - 1, n * k)
    return fac_continue(n, 1)
}