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!
The reason the loop never terminates when you change
factton * fact(n-1)is thatn * fact(n-1)has to evaluate first (as the third argument toif). Evaluating it leads to another call tofact, 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 ofif, and its result would be checked viatest.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.