В результате экспериментов я обнаружил, что при формализме вызова по имени метод становится ... нехвостовой рекурсивной !Я сделал этот пример кода для сравнения факториальных хвостовых рекурсивно, а факториальных не хвостовых рекурсивных:
package example
import scala.annotation.tailrec
object Factorial extends App {
val ITERS = 100000
def factorialTailRec(n: Int) : Int = {
@tailrec
def factorialTailRec(n: Int, f: => Int): Int = {
if (n == 0) f else factorialTailRec(n - 1, n * f)
}
factorialTailRec(n, 1)
}
for(i <-1 to ITERS) println("factorialTailRec(" + i + ") = " + factorialTailRec(i))
def factorial(n:Int) : Int = {
if(n == 0) 1 else n * factorial(n-1)
}
for(i <-1 to ITERS) println("factorial(" + i + ") = " + factorial(i))
}
Обратите внимание, что внутренняя функция tailRec
вызывает второй аргумент по имени., для которого аннотация @tailRec
по-прежнему НЕ выдает ошибку времени компиляции!
Я поигрался с разными значениями для переменной ITERS
и для значения100 000, я получаю ... StackOverflowError
!
(результат нулевого результата из-за переполнения Int
.)
ИтакЯ пошел дальше и изменил сигнатуру factorialTailRec/2
на:
def factorialTailRec(n: Int, f: Int): Int
, то есть вызов по значению для аргумента f
.На этот раз часть main
, которая запускает factorialTailRec
, завершается абсолютно нормально, тогда как, конечно, factorial/1
вылетает с точно таким же целым числом.
Очень, очень интересно.В этой ситуации кажется, что вызов по имени поддерживает кадры стека из-за необходимости вычисления самих продуктов вплоть до цепочки вызовов.