views:

131

answers:

1

I was looking at an article on Peter Norvig's website, where he's trying to answer the following question (this is not my question, btw) "Can I do the equivalent of (test ? result : alternative) in Python?"

here's one of the options listed by him,

def if_(test, result, alternative=None):
    "If test is true, 'do' result, else alternative. 'Do' means call if callable."
    if test:
        if callable(result): result = result()
        return result
    else:
        if callable(alternative): alternative = alternative()
        return alternative

And here's a usage example.

>>> fact = lambda n: if_(n <= 1, 1, lambda: n * fact(n-1))
>>> fact(6)
720

I understand how this works (I think), but I was just playing with the code, and decided to see what happens when I change the third argument in the definition of 'fact' above to n * fact(n-1), that is, change it to a non-callable expression. On running it, the interpreter goes into a never ending loop. I have a pretty good idea of why that is happening, that is, the if_ function is returning back the same expression that it is receiving. But what is the type of that expression? What exactly is going on here? I am not looking for a detailed explanation , but just for some pointers to python's evaluation model which might help my understanding.

Thanks!

+4  A: 

The reason the loop never terminates when you change fact to n * fact(n-1) is that n * fact(n-1) has to evaluate first (as the third argument to if). Evaluating it leads to another call to fact, ad infinitum (since there is no longer any base case to stop it).

Previously, you were passing a function object (lambda), which would not be evaluated until the body of if, and its result would be checked via test.

This is known (I believe) as eager evaluation, where function arguments are evaluated before they are passed to the function. In a lazy-evaluation scheme, the arguments would not be evaluated until they were used in the function body.

danben
If n<=1 would be evaluated correctly, the recursion would stop at n = 1, returning 1.
Krab
`n <= 1` will be evaluated, but its value is never checked until inside the function body. When he uses a recursive expression instead of a lambda as the third parameter, the function body is never reached.
danben
I wasn't arguing that it computes n!, I just misunderstood your answer. Now, I read it once more and I agree with it.
Krab
thanks! that makes sense.
fsm
@fsm - you're welcome. If you found this answer to be helpful, I'd appreciate if you marked it as accepted.
danben