Вы можете использовать что-то вроде лямбда-исчисление , чтобы избежать присваивания и самоссылки, заменив оба на доступ к аргументу анонимной функции. Например:
fact = (lambda f: f(f))(lambda f: (lambda n: n*f(f)(n-1) if n else 1))
Протестировано в Ideone .
Некоторые подробности ниже для получения дополнительной информации.
Я знаю, что лямбда-исчисление известно как мощный (полный по Тьюрингу), но минималистичный c «язык программирования». Он использует только идентификаторы для переменных, которые могут быть связаны (в значительной степени с аргументами функции) или не связаны (в основном, когда речь идет о частях выражения). Так что это было хорошей отправной точкой.
Канонический путь к express рекурсии в лямбда-исчислении - использование комбинатора с фиксированной точкой . Хотя этот комбинатор может быть наивно выражен в синтаксисе Python, тщательная оценка приводит к бесконечной рекурсии.
Код https://rosettacode.org/wiki/Y_combinator#Python, упомянутый в комментариях, позволяет избежать этой бесконечной рекурсии, задерживая один из рекурсивные вызовы, пока функция не будет вызвана. Но я бы предпочел оставить подробное объяснение этого подхода в отдельном ответе.
Какова основная идея выражения рекурсии в лямбда-исчислении? Передача функции в качестве аргумента самой себе. Итак, я начал с этого:
lambda f: f(f) # λ f.f f
Мне нужно передать этой функции другую функцию, которая принимает функцию в качестве значения. Как lambda f: …
. И результатом этого вызова должна быть функция, которая должна принимать n
в качестве аргумента для вычисления факториала. Мое первое приближение состояло в том, чтобы думать о f
как о выражении для рекурсивного вызова, поэтому сначала я произнес:
(lambda f: f(f))(lambda f: (lambda n: n*f(n-1) if n else 1))
Но потом я понял, что это неправильно: f
сам по себе не является рекурсивным вызовом , поскольку f
- это функция, которая принимает аргумент f
. Таким образом, f(f)
является рекурсивным вызовом, приводящим к решению, которое я напечатал в начале.