Правильный ответ заключается в том, что эта функция не завершается для всех целых чисел (в частности, она не заканчивается на -1). Ваш друг прав, утверждая, что это псевдокод, и псевдокод не завершается при переполнении стека. Псевдокод формально не определен, но идея в том, что он делает то, что говорит на жестяной банке. Если код не говорит «завершиться с ошибкой переполнения стека», то ошибки переполнения стека нет.
Даже если бы это был настоящий язык программирования, правильный ответ все равно был бы «не заканчивается», если только использование стека не является частью определения языка. Большинство языков не определяют поведение программ, которые могут переполнять стек, потому что трудно точно знать, сколько стека будет использовать программа.
Если выполнение кода на реальном интерпретаторе или компиляторе вызывает переполнение стека, во многих языках это несоответствие между формальной семантикой языка и реализацией. Обычно считается, что реализации языка будут делать только то, что может быть сделано на конкретном компьютере с ограниченной памятью. Если программа умирает с переполнением стека, вы должны купить больший компьютер, перекомпилировать систему, если необходимо, для поддержки всей этой памяти, и попытаться снова. Если программа не завершается, возможно, вам придется продолжать делать это вечно.
Даже тот факт, что программа будет или не будет переполнять стек, недостаточно определен, поскольку некоторые оптимизации, такие как оптимизация хвостовых вызовов и запоминание, могут допускать бесконечную цепочку вызовов функций в пространстве стека с постоянной привязкой. В некоторых языковых спецификациях даже требуется, чтобы реализации выполняли оптимизацию хвостового вызова, когда это возможно (это часто встречается в функциональных языках программирования). Для этой функции f(-1)
расширяется до f(f(-2))
; внешний вызов f
- это хвостовой вызов, поэтому он ничего не помещает в стек, таким образом, он f(-2)
переходит только в стек, и он возвращается -1
, поэтому стек возвращается к тому же состоянию, в котором он находился в начале. Таким образом, с оптимизацией вызовов хвоста f(-1)
в постоянной памяти.