views:

633

answers:

5

I'm trying to write a lambda-expression that calls itself, but i can't seem to find any syntax for that, or even if it's possible.

Essentially what I wanted to transfer the following function into the following lambda expression: (I realize it's a silly application, it just adds, but I'm exploring what I can do with lambda-expressions in 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]

but calling the lambda form of add results in a runtime error because the maximum recursion depth is reached. Is it even possible to do this in python? Or am I just making some stupid mistake? Oh, I'm using python3.0, but I don't think that should matter?

+5  A: 

Maybe you need a Y combinator?

Edit - make that a Z combinator (I hadn't realized that Y combinators are more for call-by-name)

Using the definition of the Z combinator from Wikipedia

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

Using this, you can then define add as a completely anonymous function (ie. no reference to its name in its definition)

>>> 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
Ben Lings
or a flux capacitor
Joel Coehoorn
How would it stop?
Jeremy Powell
It just needs to be lazy. :-)
starblue
lazy would be **def add(a,b): return a+b** :P
Jeremy Powell
+4  A: 

First of all recursive lambda expressions are completely unnecessary. As you yourself point out, for the lambda expression to call itself, it needs to have a name. But lambda expressions is nothing else than anonymous functions. So if you give the lambda expression a name, it's no longer a lambda expression, but a function.

Hence, using a lambda expression is useless, and will only confuse people. So create it with a def instead.

But yes, as you yourself discovered, lambda expressions can be recursive. Your own example is. It's in fact so fantastically recursive that you exceed the maximum recursion depth. So it's recursive alright. Your problem is that you always call add in the expression, so the recursion never stops. Don't do that. Your expression can be expressed like this instead:

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

Which takes care of that problem. However, your first def is the correct way of doing it.

Lennart Regebro
Having or not having a name has absolutely nothing to do with whether something is a lambda. A lambda is a logical *expression* (which happens to have the same interface as a function). It's perfectly ordinary to give them a name.
Glenn Maynard
In Python, lambda expression are implemented as anonymous functions. Hence, everything I stated above is correct. What lambdas are in theoretical mathematics is not relevant for this question. Of course it's "ordinary" to give them a name. The point is that in Python you could then just have used a normal function, ans saved yourself the trouble.
Lennart Regebro
Ah right, so what I did was the "some stupid mistake" option. I didn't even pause to think that the list would get fully evaluated first. Thanks for the face palm :)
Eric Lifka
I have no interest in theoretical math. You said that a lambda is an anonymous function, and giving a lambda a name makes it not a lambda. You're incorrect. Being anonymous or having a name has exactly nothing to do with whether a Python lambda is a lambda.
Glenn Maynard
No, I said "lambda expressions are anonymous functions", and in the context of Python (which this is) this is exactly what they are. So if you give it a name, it is, for all intents and purposes, just a normal function. There is no practical or theroretical difference.
Lennart Regebro
"if you give the lambda expression a name, it's no longer a lambda expression". It's right there, and it's wrong. I'm finished discussing this.
Glenn Maynard
If a lambda is an anonymous function (and it is), what is then a non-anonymous lambda? Right, a function. Again, there is no practical difference between a function and a named lambda. This is a fact.
Lennart Regebro
+1  A: 
add = lambda a, b: b if a <= 0 else 1 + add(a - 1, b)
Glenn Maynard
+2  A: 

Perhaps you should try the Z combinator, where this example is from:

>>> 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
Barry Kelly
I just threw up a little in my mouth.
Glenn Maynard
Did it taste like lambda?
Evan Fosmark
Either you need to use Haskell instead of Python, or you need to quit using lambda expressions. You're dangerous. :P
Jeremy Powell
A: 

You want the Y combinator, or some other fixed point combinator.

Here's an example implementation as a Python lambda expression:

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

Use it like so:

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

That is, you pass into Y() a single-argument function (or lambda), which receives as its argument a recursive version of itself. So the function doesn't need to know its own name, since it gets a reference to itself instead.

Note that this does get tricky for your add() function because the Y combinator only supports passing a single argument. You can get more arguments by currying -- but I'll leave that as an exercise for the reader. :-)

Daniel Pryden