Существуют различные ответы на переполнение стека, которые объясняют условия, при которых хвостовая рекурсия возможна в Scala. Я понимаю ограничения и то, как и где я могу воспользоваться хвостовой рекурсией. Часть, которую я не понимаю, является причиной ограничения частных или окончательных методов.Scala и хвостовая рекурсия
Я не исследовал, как компилятор Scala фактически конвертирует рекурсивную функцию в нерекурсивную на уровне байт-кода, но предположим, что она делает что-то вроде следующего. У меня есть класс Foo
с рекурсивной функцией mod
:
class Foo {
def mod(value: Int, denom: Int): Int = {
if(denom <= 0 || value <= 0) 0
else if(0 <= value && value < denom) value
else mod(value - denom, denom)
}
}
Это основной функции по модулю, который я представляю компилятор Scala транслирует в какой-то псевдо-Java-Scala, как:
class Foo {
def mod(value: Int, denom: Int): Int = {
if(denom <= 0 || value <= 0) return 0
while(value > denom) value -= denom
return value
}
}
(I могу поверить, что я перепутались, что перевод, но я не думаю, что детали очень важны ..)
Так что теперь предположим, что я подкласс Foo
:
class Bar extends Foo {
def mod(value:Int, denom: Int): Int = 1
}
Что это значит, что это не работает? Когда на JVM вызывается Foo/Bar
и mod
, почему возникает проблема с разрешением функции mod
, которая должна использоваться. Почему это отличается от ситуации, когда базовая функция является нерекурсивной?
Несколько возможных причин я вижу для этого является случай, являются:
по какой-либо причине реализация компилятора Scala не справиться с этим (достаточно, если это так справедливо, если так. , есть планы изменить это?)
в
Foo
функцияmod
является потеряется вmod-non-recursive
во время компиляции такFoo
фактически не имеет методыmod
переопределить.
... Или вложенная функция. –
@ Ken Bloom: ams знает, что методы должны быть окончательными или частными, или оптимизация не произойдет. Он спрашивает * почему. Почему сказывается, что scalac не оптимизирует переопределяемые методы? Не похоже, что в этой реализации мода рекурсивных вызовов в этой реализации будет какая-то двусмысленность. – divegeek
Неправда. Если функция [i] может быть переопределена [/ i], код, созданный для вызова, должен быть полиморфным. Подумайте, что в этом случае вы не знаете, что первый вызов пришел из подкласса, а не из «вне» класса, в котором этот метод определен. –