возможны ли рекурсивные лямбда-выражения? - PullRequest
18 голосов
/ 30 июля 2009

Я пытаюсь написать лямбда-выражение, которое вызывает само себя, но я не могу найти какой-либо синтаксис для этого, или даже если это возможно.

По сути, я хотел передать следующую функцию в следующее лямбда-выражение: (Я понимаю, что это глупое приложение, оно просто добавляет, но я изучаю, что я могу сделать с лямбда-выражениями в python)

def add(a, b):
   if a <= 0:
      return b
   else:
      return 1 + add(a - 1, b)

add = lambda a, b: [1 + add(a-1, b), b][a <= 0]

, но вызов лямбда-формы add приводит к ошибке времени выполнения, поскольку достигнута максимальная глубина рекурсии. Возможно ли вообще сделать это на python? Или я просто совершаю какую-то глупую ошибку? О, я использую python3.0, но я не думаю, что это должно иметь значение?

Ответы [ 6 ]

19 голосов
/ 30 июля 2009

Может быть, вам нужен комбинатор Y?

Редактировать - сделать это комбинатором Z (я не понял, что Y комбинаторы больше для вызова по имени)

Использование определения комбинатора Z из Википедия

>>> Z = lambda f: (lambda x: f(lambda *args: x(x)(*args)))(lambda x: f(lambda *args: x(x)(*args)))

Используя это, вы можете определить add как полностью анонимную функцию (т.е. без ссылки на ее имя в своем определении)

>>> add = Z(lambda f: lambda a, b: b if a <= 0 else 1 + f(a - 1, b))
>>> add(1, 1)
2
>>> add(1, 5)
6
8 голосов
/ 30 июля 2009

Возможно, вам следует попробовать комбинатор Z , откуда взят этот пример:

>>> Z = lambda f: (lambda x: f(lambda *args: x(x)(*args)))(lambda x: f(lambda *args: x(x)(*args)))
>>> fact = lambda f: lambda x: 1 if x == 0 else x * f(x-1)
>>> Z(fact)(5)
120
6 голосов
/ 30 июля 2009

Прежде всего, рекурсивные лямбда-выражения совершенно не нужны. Как вы сами указываете, чтобы лямбда-выражение вызывало само себя, оно должно иметь имя. Но лямбда-выражения - это не что иное, как анонимные функции. Поэтому, если вы дадите лямбда-выражению имя, это уже не лямбда-выражение, а функция.

Следовательно, использование лямбда-выражения бесполезно и только смущает людей. Поэтому создайте его с помощью def.

Но да, как вы сами обнаружили, лямбда-выражения могут быть рекурсивными. Ваш собственный пример. На самом деле это настолько фантастически рекурсивно, что вы превышаете максимальную глубину рекурсии. Так что это нормально рекурсивно. Ваша проблема в том, что вы всегда вызываете add в выражении, поэтому рекурсия никогда не останавливается. Не делай этого. Ваше выражение может быть выражено так вместо этого:

add = lambda a, b: a > 0 and (1 + add(a-1, b)) or b

Который решает эту проблему. Тем не менее, ваше первое определение - правильный способ сделать это.

3 голосов
/ 30 июля 2009
add = lambda a, b: b if a <= 0 else 1 + add(a - 1, b)
0 голосов
/ 30 января 2014

немного поздно ... но я только что нашел этот драгоценный камень @ http://metapython.blogspot.com/2010/11/recursive-lambda-functions.html

def myself (*args, **kw):
    caller_frame = currentframe(1)
    code = caller_frame.f_code
    return  FunctionType(code, caller_frame.f_globals)(*args,**kw)

print "5! = "
print (lambda x:1 if n <= 1 else myself(n-1)*n)(5)
0 голосов
/ 30 июля 2009

Требуется комбинатор Y или какой-либо другой комбинатор с фиксированной точкой .

Вот пример реализации в виде лямбда-выражения Python:

Y = lambda g: (lambda f: g(lambda arg: f(f)(arg))) (lambda f: g(lambda arg: f(f)(arg)))

Используйте это так:

factorial = Y(lambda f: (lambda num: num and num * f(num - 1) or 1))

То есть вы передаете в Y () функцию с одним аргументом (или лямбду), которая получает в качестве аргумента рекурсивную версию себя. Поэтому функции не нужно знать свое собственное имя, поскольку вместо этого она получает ссылку на себя.

Обратите внимание, что это усложняет вашу функцию add (), поскольку комбинатор Y поддерживает передачу только одного аргумента. Вы можете получить больше аргументов, карри - но я оставлю это в качестве упражнения для читателя. : -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...