2009-07-29 2 views
18

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

По сути то, что я хотел передать следующую функцию в следующем лямбда-выражения: (я понимаю, что это глупо приложение, он просто добавляет, но я исследовать то, что я могу делать с лямбда-выражений в 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] 

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

+0

лямбда-выражения довольно разорванных питон. я не достаточно хорошо знаком, чтобы дать полный ответ, но если он вас убедит, я понимаю, что Гвидо ненавидит выражения лямбда: P –

+0

см. здесь, точный дубликат: http://stackoverflow.com/questions/481692/can-a -lambda-function-call-yourself-recursively-in-python –

+1

В них нет ничего разбитого. Они просто бесполезны, поскольку вместо этого вы можете использовать функцию, которую легче читать и понимать. –

ответ

18

Возможно, вам нужен комбинатор Y?

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

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

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

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

>>> 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 
+10

или конденсатор потока –

+0

Как это остановить? –

+0

Просто нужно быть ленивым. :-) – starblue

7

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

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

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

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

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

+9

Имея или не имея имя, абсолютно не имеет никакого отношения к тому, что ли это лямбда. Лямбда - это логическое * выражение * (которое, как оказалось, имеет тот же интерфейс, что и функция). Совершенно просто назвать им имя. –

+1

В Python выражение лямбда реализовано как анонимные функции. Следовательно, все, что я изложил выше, является правильным. Какие лямбды в теоретической математике не имеют отношения к этому вопросу. Конечно, «обычным» дать им имя. Дело в том, что в Python вы могли бы просто использовать обычную функцию, а также избавили себя от этой проблемы. –

+0

Ах, да, так что я сделал, это вариант «какой-то глупой ошибки». Я даже не остановился, чтобы подумать, что список будет полностью оценен первым. Спасибо за ладонь :) –

3
add = lambda a, b: b if a <= 0 else 1 + add(a - 1, b) 
7

Может быть, вы должны попробовать Z combinator, где этот пример из:

>>> 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 
+5

Я просто немного подбросил в рот. –

+3

Разве это было похоже на лямбда? –

+3

Либо вам нужно использовать Haskell вместо Python, либо вам нужно отказаться от использования лямбда-выражений. Ты опасен. : P –

0

Вы хотите Y комбинатор, или какой-либо другой fixed point combinator.

Вот пример реализации как выражение 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 поддерживает только передачу одного аргумента. Вы можете получить больше аргументов по currying - но я оставлю это как упражнение для читателя. :-)

+0

Или просто используйте комбинатор Z, над которым поддерживается любое количество позиционных аргументов. При желании вы также можете поддерживать аргументы ключевых слов таким же образом. – wberry

0

немного поздно ... но я только что нашел этот драгоценный камень @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) 
Смежные вопросы