Поддерживает ли java и оптимизирует хвостовые рекурсивные вызовы?

Скажем, я получил рекурсивную функцию, которая является хвостовой рекурсивной.

System.out.println( sum(Arrays.asList(0, 1, 2, 3, 4, 5)) );

int sum(List<Integer> integers) {
    if (integers.isEmpty())
        return 0;
    else
        return integers.get(0) + sum(integers.subList(1, integers.size()));
}

Интересно, будет ли эта функция sum расти на стеке или будет ли она изменена на цикл (так как это функция хвоста-рекурсии)?

Я только что прочитал, что Scala обнаруживает такие вызовы и оптимизирует его, но является ли это Scala - только вещь или JVM вообще?

Ответы

Ответ 1

Java поддерживает хвостовые рекурсивные вызовы, но AFAIK не оптимизирует их. Я думаю, что это компилятор Scala, который просто способен на это, а не на JVM. Просмотрите аннотацию @tailrec в Scala, чтобы узнать, что может сделать компилятор:)

Но независимо от того, оптимизирует ли Java/JVM хвостовую рекурсию, ваша функция будет сложнее оптимизировать, чем необходимо.

Посмотрите на это:

int sum(List<Integer> integers) {
    return sum(integers, 0);
}

int sum(List<Integer> integers, int sumSoFar) {
    if (integers.isEmpty())
        return sumSoFar;
    else
        return sum(
                integers.subList(1, integers.size()),
                sumSoFar + integers.get(0)
        );
}

См., я добавил перегруженный sum с так называемым рассчитанным параметром суммы. Таким образом, когда вы возвращаетесь в ветвь else, вам больше не нужен фактический кадр стека - у вас есть все, что вам нужно, в качестве аргументов функции в рекурсивном вызове.

В вашем фрагменте фрейм стека, вероятно, должен существовать до тех пор, пока рекурсивный вызов.

Ответ 2

В соответствии с этой статьей от апреля 2014 года:

Важно отметить, что это не ошибка в JVM. Это оптимизация, которая может быть реализована, чтобы помочь функциональным программистам, которые используют рекурсию, которая намного более обычна и нормальна на этих языках. Недавно я поговорил с Брайаном Гетцем в Oracle об этой оптимизации, и он сказал, что он содержит список вещей, которые нужно добавить в JVM, но это просто не высокоприоритетный элемент.