9

I have just started learning python. I came across lambda functions. On one of the problems, the author asked to write a one liner lambda function for factorial of a number.

This is the solution that was given:

num = 5
print (lambda b: (lambda a, b: a(a, b))(lambda a, b: b*a(a, b-1) if b > 0 else 1,b))(num)

I cannot understand the weird syntax. What does a(a,b) mean?

Can someone explain?

Thanks

Artsiom Rudzenka
  • 27,895
  • 4
  • 34
  • 52
drcocoa
  • 1,155
  • 1
  • 14
  • 23

11 Answers11

10

The factorial itself is almost as you'd expect it. You infer that the a is... the factorial function. b is the actual parameter.

<factorial> = lambda a, b: b*a(a, b-1) if b > 0 else 1

This bit is the application of the factorial:

<factorial-application> = (lambda a, b: a(a, b))(<factorial>, b)

a is the factorial function itself. It takes itself as its first argument, and the evaluation point as the second. This can be generalized to recursive_lambda as long as you don't mind a(a, b - 1) instead of a(b - 1):

recursive_lambda = (lambda func: lambda *args: func(func, *args))
print(recursive_lambda(lambda self, x: x * self(self, x - 1) if x > 0 else 1)(6))
# Or, using the function verbatim:
print(recursive_lambda(lambda a, b: b*a(a, b-1) if b > 0 else 1)(6))

So we have the outer part:

(lambda b: <factorial-application>)(num)

As you see all the caller has to pass is the evaluation point.


If you actually wanted to have a recursive lambda, you could just name the lambda:

fact = lambda x: 1 if x == 0 else x * fact(x-1)

If not, you can use a simple helper function. You'll notice that ret is a lambda that can refer to itself, unlike in the previous code where no lambda could refer to itself.

def recursive_lambda(func):
    def ret(*args):
        return func(ret, *args)
    return ret

print(recursive_lambda(lambda factorial, x: x * factorial(x - 1) if x > 1 else 1)(6))  # 720

Both ways you don't have to resort to ridiculous means of passing the lambda to itself.

Community
  • 1
  • 1
Waleed Khan
  • 11,426
  • 6
  • 39
  • 70
7

It is this simple:

n=input()

print reduce(lambda x,y:x*y,range(1,n+1))
Juan Serrats
  • 1,358
  • 5
  • 24
  • 30
Ujjwal Aryal
  • 111
  • 1
  • 5
6

Let's peel this one liner open like an onion.

print (lambda b: (Y))(num)

We are making an anonymous function (the keyword lambda means we're about to type a series of parameter names, then a colon, then a function that uses those parameters) and then pass it num to satisfy its one parameter.

   (lambda a, b: a(a, b))(X,b)

Inside of the lambda, we define another lambda. Call this lambda Y. This one takes two parameters, a and b. a is called with a and b, so a is a callable that takes itself and one other parameter

            (lambda a, b: b*a(a, b-1) if b > 0 else 1
            ,
            b)

These are the parameters to Y. The first one is a lambda function, call it X. We can see that X is the factorial function, and that the second parameter will become its number.

That is, if we go up and look at Y, we can see that we will call:

X(X, b)

which will do

b*X(X, b-1) if b > 0 else 1

and call itself, forming the recursive part of factorial.

And looking all the way back outside, we can see that b is num that we passed into the outermost lambda.

num*X(X, b-1) if num > 0 else 1

This is kind of confusing because it was written as a confusing one liner :)

Patashu
  • 21,443
  • 3
  • 45
  • 53
  • It was an online competition on one liners. And I was preparing for that. It was confusing but after spending a great amount of time, I think I got it down. Thanks – drcocoa Jun 24 '13 at 23:20
2

There are two hard parts about this function.
1. lambda a, b: b*a(a, b-1) if b > 0 else 1.
2. the "b" that's folowing 1.

For 1, it's nothing more than:

def f(a, b):
    if b > 0:
        b * a(a, b - 1)
    else:
        1

For 2, this b

(lambda b: (lambda a, b: a(a, b))(lambda a, b: b*a(a, b-1) if b > 0 else 1,b))(num)
                                                                      (this one)

is actually this b:

(lambda b: (lambda a, b: a(a, b))(lambda a, b: b*a(a, b-1) if b > 0 else 1,b))(num)
   (this one)

The reason is that it's not inside the definition of the second and third lambda, so it refers to the first b.

After we apply num and strip off the outer function:

(lambda a, b: a(a, b))  (lambda a, b: b*a(a, b-1) if b > 0 else 1, num) 

It's just applying a function to a tuple, (lambda a, b: b*a(a, b-1) if b > 0 else 1, num)
Let's call this tuple as (f, num) (f's def is above) Applying lambda a, b: a(a, b) on it, we get

f(f, num).

Suppose your num is 5.
By definiton of f, it first evaluates to

5 * f(f, 4)  

Then to:

5 * (4 * f(f, 3)) 

All the way down to

5 * (4 * (3 * (2 * (1 * f(f, 0)))))

f(f, 0) goes to 1.

5 * (4 * (3 * (2 * (1 * 1))))

Here we go, the factorial of 5.

octref
  • 6,521
  • 7
  • 28
  • 44
2

We can use the below lambda expression

     fact = lambda n:1 if n==0 else n*fact(n-1)
     print(fact(5)
     >>> 120
Abhishake Gupta
  • 2,939
  • 1
  • 25
  • 34
0

the generalized def of recursive lambda is as below

recursive_lambda = (lambda func: lambda *args: func(func, *args))
0

Very nicely explained! Only one minor fix: if b > 1 NOT if b > 0

The result is the same but logically more correct as it is executing one unnecessary additional loop (even though multiplying by 1)

Wikipedia => n!, is the product of all positive integers less than or equal to n

0

if you want to do a factorial using lambda expression then you need to put if else condition like:

if x==1: 
    return 1 
else 
    return x*function_name(x-1)

for example fact is my lambda expression:

fact( lambda x:1 if x==1/else x*fact(x-1) )

this is recursive if else condition

sunwarr10r
  • 4,420
  • 8
  • 54
  • 109
0

Easy Method to find Factorial using Lambda Function

fac = lambda x : x * fac(x-1) if x > 0 else 1

print(fac(5))

output: 120

User Input to find Factorial

def fac():

    fac_num = int(input("Enter Number to find Factorial "))

    factorial = lambda f : f * factorial(f-1) if f > 0 else 1

    print(factorial(fac_num))

fac()

Enter Number to find Factorial : 5

120

0

The code in question:

lambda b : (lambda a, b : a(a, b)) (lambda a, b : b * a(a, b-1) if b > 0 else 1,  b)

To make it clearer, let me replace variable names a with f, and b with n.

lambda n : (lambda f, n : f(f, n)) (lambda f, n : n * f(f, n-1) if n > 0 else 1,  n)

Recursive Factorial is a function that will call itself, or be applied to itself, something like f(f). Let us set Factorial(n) to be f(f,n) and compute as follows:

def func(f, n):  # takes a function and a number, return a number.
        if n > 0 :
            return n * f(f, n-1)
        else :
            return 1

Translating the above def func into lambda notation:

func is     lambda f, n : n * f(f, n-1) if n > 0 else 1 

func is then to be applied to itself along with integer n.

Back to beginning statement, Factorial(n) is f(f,n), or f(func,n) when func is applied into f.

def Factorial(n):  
    # return f (func, n)  # next, substitute f and func with their corresponding lambdas.
    return (lambda f, n : f(f, n))  (lambda f, n : n * f(f, n-1) if n > 0 else 1, n)

and using lambda instead of def Factorial, the whole thing becomes:

lambda n : (lambda f, n : f(f, n)) (lambda f, n : n * f(f, n-1) if n > 0 else 1, n)
Setiono
  • 1
  • 1
-1
while True: 
    #It is this simple:
    from functools import reduce
    n=input('>>')
    n=int(n)
    if n==0:
        print('factorial: ',1)
    elif n<0:
        print('invalid input')
    else:
        print('factorial: ',(reduce(lambda x,y:x*y,list(range(1,n+1)))))
deadshot
  • 8,881
  • 4
  • 20
  • 39
  • The question was about understanding a complex expression, not about coming up with a new one. Also, you are using an imported function `reduce`, which was,implicitly, not allowed. – Amitai Irron May 13 '20 at 13:30
  • Another thing - an `import` statement inside a loop - definitely a bad idea. – Amitai Irron May 13 '20 at 13:31