Suppose we have a function with the following structure:
def f():
# ...
# some computations
# ...
if something_is_wrong_with_previous_computations:
return None
# ...
# some other computations
# ...
if something_is_wrong_with_previous_computations2:
return some_variable
#...
return result
As I see it, using return statement in the middle of a function is not functional at all. If we were in some lispy language we would have the let (then computations could be written with let*) statement, which would help us deal with these situations with ease. Unfortunately, we don't have it here. What should we do?
Simulate let with creating lots of nested functions and calling them in place?
Use something like Maybe monad or another complex stuff like that?
Don't waste our time and write it imperatively?
Something else?
A return statement anywhere in a function is not functional.
In Python, you have no choice.
The Lisp code
(defun sgnum (x)
(cond
((< x 0) -1)
((zerop x) 0)
(t 1)))
turns into Python as
def sgnum(x):
if x < 0:
return -1
elif x == 0:
return 0
else:
return 1
In Lisp, we know we have deviated from functional coding when we use variable assignment, or the "program feature": an explicit progn construct, or an implicit equivalent, or any of is cousins like prog or prog1. A functional function in Lisp always has a body which is made up of a single expression (or possibly no expressions at all).
You can redefine what you mean by "functional coding" in Python. How about these
rules:
Every statement in a "functional function" must be a single statement; it cannot be followed by another statement. Thus, the whole body of a function is a single statement, and in it are embedded single statements.
No statement in the function may allow control to fall through it. Every statement must return. Thus return is not only considered "functional" but essential to achieving this goal.
A variable may be defined, but not redefined. Parallel, mutually exclusive control flows may assign the same variable different values, but no variable can be assigned more than once in the same control flow.
With these kinds of rules, you can get the program to have a control flow graph resembling that a program in the pure Lisp style: a control graph that is basically a tree of decisions with embedded calculations and variable binding, at the leaves of which are values to be returned.
Speaking of variable binding, we should probably have a fourth rule:
A statement may be preceded by a sequence of fresh variable assignments that contain no side effects. Such a sequence, together with the statement which follows it, counts as one statement.
Arguably also a fifth one:
No statement must be used which evaluates any contained expression or statement more than once.
Otherwise we permit loops, which are not functional. This is tricky because some looping constructs are relatively well behaved, like implicitly stepping a dummy variable over the elements of a list. The only way you can tell it's not functional is that a lexical closure captured in the loop will easily reveal there is only one variable being mutated and not a fresh variable being bound for each iteration.
According to these rules, sgnum is "functional": it contains just one if/elif/else statement, which does not allow control to fall through it: every branch returns:
This version of sgnum is not "functional" any more:
def sgnum(x):
if x < 0:
return -1
if x == 0:
return 0
return 1
It contains three statements in sequence. Whereas the following is "functional" even though it also consists of three statements:
def distance(x0, y0, x1, y1):
xd = x1 - x0
yd = y1 - y0
return math.sqrt(xd * xd + yd * yd)
These meet the rules. The first two statements bind fresh variables, meeting rule 3, so are permitted by 4 to precede a statement. The return statement meets rules 1 and 2. This is very similar to:
(defun distance (x0 y0 x1 y1)
(let ((xd (- x1 x0))
(yd (- y1 y0)))
(sqrt (+ (* xd xd) (* yd yd)))))
Lastly, note how our rules are at odds with the ancient programming advice of "have only one exit point in a function". That little tidbit you may find in some coding conventions is quite anti-functional. To achieve a single point of return in a nontrivial function requires imperative style control flows through multiple statements and/or variable assignments. From the functional point of view, it is a myopic, silly rule; but it makes sense in those contexts where it is recommended, because it can help improve very poorly structured imperative code.
Related
In a C program, inlining a function is a fairly intuitive optimization. If the inlined function's body is sufficiently small, you end up saving the jump to the function and creation of the stack frame, and you store the return value wherever the function's result would have been stored, jumping to the end of the inlined function's "body" rather than long-jumping to the return pointer.
I'm interested in doing the same thing in Python, converting two python functions into another valid python function where the first got "inlined" into the second. An ideal solution to this might look something like the following:
def g(x):
return x ** 2
def f(y):
return g(y + 3)
# ... Becomes ...
def inlined_f(y):
return (y + 3) ** 2
Clearly, in a language as dynamic as Python, this isn't trivial to do automatically. The best generic solution I have come up with is to use dict to capture the arguments passed to the function, wrap the function body in a one-iteration for loop, use break to jump to the end of the function, and replace uses of arguments with indexes into the argument dictionary. The result looks something like the following:
def inlined_f(y):
_g = dict(x=y + 3)
for ____ in [None]:
_g['return'] = _g['x'] ** 2
break
_g_return = _g.get('return', None)
del _g
return _g_return
I don't care that it's ugly, but I do care that it doesn't support returns from within loops. E.g.:
def g(x):
for i in range(x + 1):
if i == x:
return i ** 2
print("Woops, you shouldn't get here")
def inlined_f(y):
_g = dict(x=y + 3)
for ____ in [None]:
for _g['i'] in range(_g['x'] + 1):
if _g['i'] == _g['x']:
_g['return'] _g['i'] ** 2
break # <-- Doesn't exit function, just innermost loop
print("Woops, you shouldn't get here")
_g_return = _g.get('return', None)
del _g
return _g_return
What approach could I take to this problem that avoids needing to use break to "jump" out of the inlined function's body? I'd also be open to an overall better, generic approach could I take to inline one Python function into another.
For reference, I'm working at the AST (abstract syntax tree) level, so using parsed Python code; clearly, outside of literal values, I don't know what value or type anything will have while performing this transformation. The resulting inlined function must behave identically to the original functions, and must support all features typically available when calling a function. Is this even possible in Python?
EDIT: I should clarify since I used the tag "optimization", that I'm not actually interested in a performance boost. The resulting code does not need to be faster, it just must not call the inlined function while still behaving identically. You can assume that both functions' source code is available as valid Python.
The only reasonable way on source level I see, simplified:
Parse the source into some AST (or just use the built-in AST).
Copy a subtree representing the function's body.
Rename the variables in the subtree, e.g. by adding an unique prefix.
At the call site, replace all passed arguments with assignments using the function's new variable names.
Remove the call and replace it with the function body you've prepared.
Serialize the AST back to source.
What poses real problems:
Generator functions; just don't inline them.
Returns from under try/finally that need to run the finally part. Might be pretty hard to rewrite correctly; imho, best left in-unlined.
Returns from under context managers that need to run the __exit__ parts. While not impossible, it's also tricky to rewrite preserving the semantics; likely also best left un-inlined.
Mid-function returns, especially from within multiple loop constructs. You might need to replace them with an extra variable and thread it into every condition of every while statement, and likely to add a conditional break to for statements. Again, not impossible but likely best left un-inlined.
Probably the closest analog to a return would be raising an Exception, which would work to pop out of nested loops to the top of the "inlined function".
class ReturnException(Exception):
pass
g = dict(x=y + 3)
try:
for j in some_loop:
for _g['i'] in range(_g['x'] + 1):
if _g['i'] == _g['x']:
raise ReturnException(_g['i'] ** 2)
except ReturnException as e:
_g['return'] = e.message
else:
_g['return'] = None
I don't know how much overhead is associated with exceptions though or if that would be faster than simply calling the function.
This is not a duplicate of Assignment inside lambda expression in Python, i.e., I'm not asking how to trick Python into assigning in a lambda expression.
I have some λ-calculus background. Considering the following code, it
looks like Python is quite willing to perform side-effects in lambda
expressions:
#!/usr/bin/python
def applyTo42(f):
return f(42)
def double(x):
return x * 2
class ContainsVal:
def __init__(self, v):
self.v = v
def store(self, v):
self.v = v
def main():
print('== functional, no side effects')
print('-- print the double of 42')
print(applyTo42(double))
print('-- print 1000 more than 42')
print(applyTo42(lambda x: x + 1000))
print('-- print c\'s value instead of 42')
c = ContainsVal(23)
print(applyTo42(lambda x: c.v))
print('== not functional, side effects')
print('-- perform IO on 42')
applyTo42(lambda x: print(x))
print('-- set c\'s value to 42')
print(c.v)
applyTo42(lambda x: c.store(x))
print(c.v)
#print('== illegal, but why?')
#print(applyTo42(lambda x: c.v = 99))
if __name__ == '__main__':
main()
But if I uncomment the lines
print('== illegal, but why?')
print(applyTo42(lambda x: c.v = 99))
I'll get
SyntaxError: lambda cannot contain assignment
Why not? What is the deeper reason behind this?
As the code demonstrates, it cannot be about “purity” in a
functional sense.
The only explanation I can imagine is that assignemts do not
return anything, not even None. But that sounds lame and would
be easy to fix (one way: make lambda expressions return None if
body is a statement).
Not an answer:
Because it's defined that way (I want to know why it's defined that way).
Because it's in the grammar (see above).
Use def if you need statements (I did not ask for how to get
statements into a function).
“This would change syntax / the language / semantics” would be ok as an answer if you can come up with an example of such a change, and why it would be bad.
The entire reason lambda exists is that it's an expression.1 If you want something that's like lambda but is a statement, that's just def.
Python expressions cannot contain statements. This is, in fact, fundamental to the language, and Python gets a lot of mileage out of that decision. It's the reason indentation for flow control works instead of being clunky as in many other attempts (like CoffeeScript). It's the reason you can read off the state changes by skimming the first object in each line. It's even part of the reason the language is easy to parse, both for the compiler and for human readers.2
Changing Python to have some way to "escape" the statement-expression divide, except maybe in a very careful and limited way, would turn it into a completely different language, and one that no longer had many of the benefits that cause people to choose Python in the first place.
Changing Python to make most statements expressions (like, say, Ruby) would again turn it into a completely different language without Python's current benefits.
And if Python did make either of those changes, then there'd no longer be a reason for lambda in the first place;2,3 you could just use def statements inside an expression.
What about changing Python to instead make assignments expressions? Well, it should be obvious that would break "you can read off the state changes by skimming the first object in each line". Although Guido usually focuses on the fact that if spam=eggs is an error more often than a useful thing.
The fact that Python does give you ways to get around that when needed, like setattr or even explicitly calling __setitem__ on globals(), doesn't mean it's something that should have direct syntactic support. Something that's very rarely needed doesn't deserve syntactic sugar—and even more so for something that's unusual enough that it should raise eyebrows and/or red flags when it actually is done.
1. I have no idea whether that was Guido's understanding when he originally added lambda back in Python 1.0. But it's definitely the reason lambda wasn't removed in Python 3.0.
2. In fact, Guido has, multiple times, suggested that allowing an LL(1) parser that humans can run in their heads is sufficient reason for the language being statement-based, to the point that other benefits don't even need to be discussed. I wrote about this a few years ago if anyone's interested.
3. If you're wondering why so many languages do have a lambda expression despite already having def: In many languages, ranging from C++ to Ruby, function aren't first-class objects that can be passed around, so they had to invent a second thing that is first-class but works like a function. In others, from Smalltalk to Java, functions don't even exist, only methods, so again, they had to invent a second thing that's not a method but works like one. Python has neither of those problems.
4. A few languages, like C# and JavaScript, actually had perfectly working inline function definitions, but added some kind of lambda syntax as pure syntactic sugar, to make it more concise and less boilerplatey. That might actually be worth doing in Python (although every attempt at a good syntax so far has fallen flat), but it wouldn't be the current lambda syntax, which is nearly as verbose as def.
There is a syntax problem: an assignment is a statement, and the body of a lambda can only have expressions. Python's syntax is designed this way1. Check it out at https://docs.python.org/3/reference/grammar.html.
There is also a semantics problem: what does each statement return?
I don't think there is interest in changing this, as lambdas are meant for very simple and short code. Moreover, a statement would allow sequences of statements as well, and that's not desirable for lambdas.
It could be also fixed by selectively allowing certain statements in the lambda body, and specifying the semantics (e.g. an assignment returns None, or returns the assigned value; the latter makes more sense to me). But what's the benefit?
Lambdas and functions are interchangeable. If you really have a use-case for a particular statement in the body of a lambda, you can define a function that executes it, and your specific problem is solved.
Perhaps you can create a syntactic macro to allow that with MacroPy3 (I'm just guessing, as I'm a fan of the project, but still I haven't had the time to dive in it).
For example MacroPy would allow you to define a macro that transforms f[_ * _] into lambda a, b: a * b, so it should not be impossible to define the syntax for a lambda that calls a function you defined.
1 A good reason to not change it is that it would cripple the syntax, because a lambda can be in places where expressions can be. And statements should not. But that's a very subjective remark of my own.
My answer is based on chepner's comment above and doesn't draw from any other credible or official source, however I think that it will be useful.
If assignment was allowed in lambda expressions, then the error of confusing == (equality test) with = (assignment) would have more chances of escaping into the wild.
Example:
>>> # Correct use of equality test
... list(filter(lambda x: x==1, [0, 1, 0.0, 1.0, 0+0j, 1+0j]))
[1, 1.0, (1+0j)]
>>> # Suppose that assignment is used by mistake instead of equality testing
... # and the return value of an assignment expression is always None
... list(filter(lambda x: None, [0, 1, 0.0, 1.0, 0+0j, 1+0j]))
[]
>>> # Suppose that assignment is used by mistake instead of equality testing
... # and the return value of an assignment expression is the assigned value
... list(filter(lambda x: 1, [0, 1, 0.0, 1.0, 0+0j, 1+0j]))
[0, 1, 0.0, 1.0, 0j, (1+0j)]
As long as exec() (and eval()) is allowed inside lambda, you can do assignments inside lambda:
q = 3
def assign(var_str, val_str):
exec("global " + var_str + "; " +
var_str + " = " + val_str)
lambda_assign = lambda var_str, val_str: assign(var_str, val_str)
q ## gives: 3
lambda_assign("q", "100")
q ## gives: 100
## what would such expression be a win over the direct:
q = 100
## ? `lambda_assign("q", "100")` will be for sure slower than
## `q = 100` isn't it?
q_assign = lambda v: assign("q", v)
q_assign("33")
q ## 33
## but do I need lambda for q_assign?
def q_assign(v): assign("q", v)
## would do it, too, isn't it?
But since lambda expressions allow only 1 expression to be defined inside their body (at least in Python ...), what would be the point of to allow an assignment inside a lambda? Its net effect would be to assign directly (without using any lambda) q = 100, isn't it?
It would be even faster than doing it over a defined lambda, since you have at least one function lookup and execution less to execute ...
There's not really any deeper reasons, it has nothing to do with lambda or functional language designs, it's just to avoid programmers from mixing = and == operators, which is a very common mistake in other languages
IF there's more to this story, I assume like MAYBE because python bdfl GVR has expressed his unloving sides to lambda and other functional features and attempted(and conceded) to remove them from python 3 altogether https://www.artima.com/weblogs/viewpost.jsp?thread=98196
At the time of this writing the core devs were seen having a heated discussions recently on whether to include a limited name binding expression assignment, the debate is still on going so perhaps someday we may see it in lambda(unlikely)
As you said it yourself it is definitely not about side effects or purity, they just don't want lambda to be more than a single expression... ... ...
With that said, here's something about multi expressions assignments in lambda, read on if you're interested
It is not at all impossible in python, in fact it was sometimes necessary to capture variable and sidestep late bindings by (ab)using kwargs(keyword arguments)
edit:
code example
f = lambda x,a=1: (lambda c = a+2, b = a+1: (lambda e = x,d = c+1: print(a,b,c,d,e))())()
f("w")
# output 1 2 3 4 w
# expression assignment through an object's method call
if let(a=1) .a > 0 and let(b=let.a+1) .b != 1 and let(c=let.b+let.a) .c:
print(let.a, let.b, let.c)
# output 1 2 3
As it stands, Python was designed as a statement-based language. Therefore assignment and other name bindings are statements, and do not have any result.
The Python core developers are currently discussing PEP 572, which would introduce a name-binding expression.
I think all the fellows answered this already. We use mostly lambdas function when we just want to:
-create some simple functions that do the work perfectly in a specific place(most of the time hidden inside some other big functions
-The lambda function does not have a name
-Can be used with some other built-ins functions such as map, list and so forth ...
>>> Celsius = [39.2, 36.5, 37.3, 37.8]
>>> Fahrenheit = map(lambda x: (float(9)/5)*x + 32, Celsius) # mapping the list here
>>> print Fahrenheit
[102.56, 97.700000000000003, 99.140000000000001, 100.03999999999999]
Please visit this webpage , this could be useful.Keep it up !!!
https://www.python-course.eu/lambda.php
I learnt that mutability of an object is defined with respect to its state but not with its identity.
Below program changes state of a locally scoped object referred by name count within function hailstone().
def hailstone(n):
count = 1
"""Print the terms of the 'hailstone sequence' from n to 1."""
assert n > 0
print(n)
if n > 1:
if n % 2 == 0:
count += hailstone(n / 2)
else:
count += hailstone((n * 3) + 1)
return count
if (__name__ == '__main__'):
result = hailstone(10)
print(result)
After reading the below paragraph of this article, I see that the above code looks fine as regards changing the state of the locally scoped object that name count refers to (i.e. count += hailstone(n / 2)):
Ignore all that. Functional code is characterised by one thing: the absence of side effects. It doesn't rely on data outside the current function, and it doesn't change data that exists outside the current function. Every other “functional” thing can be derived from this property. Use it as a guide rope as you learn.
So, how do I understand the meaning of this statement from this answer:
In functional programming, it is not proper to ever change the value of a variable.
Does functional programming allow changing the state of the locally scoped object referred by name count in my above program?
There is no clearcut definition of functional programming. However, it typically is meant to imply the absence, or at least the minimisation, of side effects -- sometimes people speak of pure functional programming to emphasise their complete absence. But what is a side effect?
A common definition is that a side effect is anything that breaks referential transparency. Referential transparency in turn can be defined in various ways, but perhaps the most enlightening definition is that order of evaluation should be irrelevant. That is, a program is referentially transparent (or pure) if you can simplify any of its subexpressions, in any order, merely by substituting definitions, without changing the outcome of the program. In particular, you can always replace a variable with its (unique!) definition.
Clearly, an assignments to a mutable variable breaks this condition, so it has to be considered a side effect -- even if it is merely local. Likewise a print statement, by the way.
There are ways to have mutable state, or IO, without breaking referential transparency. That is the mystical concept of a monad. But I won't go into that here.
Not knowing Python, I assume count is a "local" variable, not visible from outside, and every invokation of hailstone gets its own instance of count. If this is so, then, as #jonrsharpe explained, it is immaterial what you do to count.
Observe, however, that your code is needlessly verbose. Why not simply:
if n > 1:
if n % 2 == 0:
return 1 + hailstone(n/2)
else:
return 1 + hailstone(n*3+1)
else:
return 1
It turns out that the variable is not needed at all, not to speak of the need to update it.
Your function has no side effects (aside from the print) at the function level. That's a necessary, but not sufficient, condition to be called functional. Functional programs consist of one big expression. Your example is expressions at the function level, interspersed with imperative statements in the function body. An if without an else is a statement, as is a variable reassignment.
That's not to say that an imperative function without side effects isn't better than an imperative function with side effects. There are certainly advantages to your example, even if it isn't considered purely functional.
This program mutates x:
x = 2
x = x * 3
x = x + 4
return x
In functional programming you don't use mutation. Your code should define each variable exactly once:
x1 = 2
x2 = x1 * 3
x3 = x2 + 4
return x3
It is possible that the compiler will allocate the same memory location for x1, x2 and x3, and mutate in place, as the original program does. However, this is an implementation detail and shouldn't be important.
Unlike the previous sequential program, you can think of the new one as a system of equations and read it in arbitrary order. In this case the computation still has to be done top-to-bottom due to data dependency, but this does not have to in general.
You can get rid of the "=" symbol altogether, by using functions. To get rid of "x1 = 2", parametrize the remaining lines by x1 and pass 2 to the function:
(function(x1) {
x2 = x1 * 3
x3 = x2 + 4
return x3
})(2)
and continue this process to remove "x2 = x1 * 3" and "x3 = x2 * 4".
Sometimes you assign to a variable in a loop to accumulate something (e.g. add items in an array for x in A: sum += x). In this case, instead of repeated assignment you can rewrite the program to use functions such as "sum", "reduce", "map", "filter", which are high-level abstractions of loops.
I was lying. It is possible to have local mutable state in a pure functional program, as long as the mutability does not "leak" outside of the scope. In Haskell, there is a library called "ST" for this purpose. It embeds a small imperative language with commands 'create variable', 'read variable', 'write variable', and if your program does not leak a mutable variable, you can retrieve its result in a pure function. However, it is more awkward to use (see this answer) and not used as much as you use assignment in imperative languages.
I'm trying to write a python function in a functional way. The problem is I don't know, how to transform an if conditional into a functional style. I have two variables: A and C, which I want to check for the following conditions:
def function():
if(A==0): return 0
elif(C!=0): return 0
elif(A > 4): return 0
else: someOtherFunction()
I looked at the lambda shortcircuiting, but I couldn't get it to work.
I thank you in advance for your help!
From the link you posted:
FP either discourages or outright disallows statements,
and instead works with the evaluation of expressions
So instead of if-statements, you could use a conditional expression:
def function():
return (0 if ((A == 0) or (C != 0) or (A > 4)) else
someOtherFunction())
or, (especially useful if there were many different values):
def function():
return (0 if A == 0 else
0 if C != 0 else
0 if A > 4 else
someOtherFunction())
By the way, the linked article proposes
(<cond1> and func1()) or (<cond2> and func2()) or (func3())
as a short-curcuiting equivalent to
if <cond1>: func1()
elif <cond2>: func2()
else: func3()
The problem is they are not equivalent! The boolean expression fails to return the right value when <cond1> is Truish but func1() is Falsish (e.g. False or 0 or None). (Or similarly when <cond2> is Truish but func2 is Falsish.)
(<cond1> and func1())
is written with the intention of evaluating to func1() when <cond1> is Truish, but when func1() is Falsish, (<cond1> and func1()) evaluates to False, so the entire expression is passed over and Python goes on to evaluate (<cond2> and func2()) instead of short-circuiting.
So here is a bit of interesting history. In 2005,
Raymond Hettinger found a similar hard-to-find bug in type(z)==types.ComplexType and z.real or z when z = (0+4j) because z.real is Falsish. Motivated by a desire to save us from similar bugs, the idea of using a less error-prone syntax (conditional expressions) was born.
There's nothing non-"functional style" in your current code! who said conditionals are not functional anyway? Practically all functional languages have a conditional operator of some sort, for instance the cond special form in Lisp.
I'd take issue with the code if it were using the assignment operator, or mutating state in some way (say, appending to a list) but as it is, the function in the question is already in a "functional style" - there are no state changes.
Perhaps you meant something like this?
return A != 0 and C == 0 and A <= 4 and someOtherFunction()
The above will return False if either A == 0 or C != 0 or A > 4, in all other cases it will return the value of calling someOtherFunction(). And by the way, False can be assumed to evaluate to 0 (for example, 42 + False == 42), so the semantics in the code in the question will be preserved from the caller's point of view.
Notice that you're taking the information in the link out of context. There's absolutely no need to use a lambda for this, the article is only explaining how to get around an inherent limitation of lambdas in Python, which is that you can't return statements inside (like if-elif-else) - only expressions are allowed, but you can fake them with boolean operators. In the context of a normal function by all means, use conditionals.
Although Peter Norvig is a really great guy, his website is pretty hard to search.
I remember reading about Can I do the equivalent of (test ? result : alternative) in Python? on his site a while back during some research before a functional Python talk.
I'm not going to sway you one way or the other in light of my findings, but you should still go and read the section about ternary conditional operators in a functional style.
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
Just use it as you have it.
Python does not have the syntax and library built-ins to make it easy to program all functions to directly return a single expression. That's not the most important part of functional style anyway, the most important part is making sure that your functions maintain referential integrity. Basically this means that whenever you supply them with the same input values they return the same output.
So when trying to program functionally in Python, I do not refrain from using statements entirely. I use a block of local variable assignments as an equivalent of let ... in ... from Haskell. I use an if/elif/else chain as an equivalent of a case expression from Haskell. And often there are built-in types which do not provide an adequate interface to create new "modified" versions of them rather than updating them in-place, so you instead have to implement such operations with an explicit copy operation and then using mutations on the new copy.
Python allows you to implement functional-style designs directly. You can easily structure your program as a whole bunch of functions which explicitly pass state around and don't have side effects, so you can design your high level algorithms in a very similar way that you would in a functional programming language. Nearly every programming language supports functional programming in this sense, if you're prepared to stomach the boilerplate necessary to fake first class functions. Since Python has first class functions, you don't even have to put up with boilerplate.
But that's as far as Python goes in supporting functional programming. It does't really support implementing functions as single referentially-transparent expressions. But that doesn't really matter. In a language that doesn't enforce or track purity, you get pretty much all the benefits of functional programming that you can by simply designing your program as a bunch of referentially transparent functions, and then how you implement those functions doesn't actually matter as long as the interface is kept referentially transparent.
Suppose I have an if statement with a return. From the efficiency perspective, should I use
if(A > B):
return A+1
return A-1
or
if(A > B):
return A+1
else:
return A-1
Should I prefer one or another when using a compiled language (C) or a scripted one (Python)?
Since the return statement terminates the execution of the current function, the two forms are equivalent (although the second one is arguably more readable than the first).
The efficiency of both forms is comparable, the underlying machine code has to perform a jump if the if condition is false anyway.
Note that Python supports a syntax that allows you to use only one return statement in your case:
return A+1 if A > B else A-1
From Chromium's style guide:
Don't use else after return:
# Bad
if (foo)
return 1
else
return 2
# Good
if (foo)
return 1
return 2
return 1 if foo else 2
I personally avoid else blocks when possible. See the Anti-if Campaign
Also, they don't charge 'extra' for the line, you know :p
"Simple is better than complex" & "Readability is king"
delta = 1 if (A > B) else -1
return A + delta
Regarding coding style:
Most coding standards no matter language ban multiple return statements from a single function as bad practice.
(Although personally I would say there are several cases where multiple return statements do make sense: text/data protocol parsers, functions with extensive error handling etc)
The consensus from all those industry coding standards is that the expression should be written as:
int result;
if(A > B)
{
result = A+1;
}
else
{
result = A-1;
}
return result;
Regarding efficiency:
The above example and the two examples in the question are all completely equivalent in terms of efficiency. The machine code in all these cases have to compare A > B, then branch to either the A+1 or the A-1 calculation, then store the result of that in a CPU register or on the stack.
EDIT :
Sources:
MISRA-C:2004 rule 14.7, which in turn cites...:
IEC 61508-3. Part 3, table B.9.
IEC 61508-7. C.2.9.
With any sensible compiler, you should observe no difference; they should be compiled to identical machine code as they're equivalent.
Version A is simpler and that's why I would use it.
And if you turn on all compiler warnings in Java you will get a warning on the second Version because it is unnecesarry and turns up code complexity.
This is a question of style (or preference) since the interpreter does not care. Personally I would try not to make the final statement of a function which returns a value at an indent level other than the function base. The else in example 1 obscures, if only slightly, where the end of the function is.
By preference I use:
return A+1 if (A > B) else A-1
As it obeys both the good convention of having a single return statement as the last statement in the function (as already mentioned) and the good functional programming paradigm of avoiding imperative style intermediate results.
For more complex functions I prefer to break the function into multiple sub-functions to avoid premature returns if possible. Otherwise I revert to using an imperative style variable called rval. I try not to use multiple return statements unless the function is trivial or the return statement before the end is as a result of an error. Returning prematurely highlights the fact that you cannot go on. For complex functions that are designed to branch off into multiple subfunctions I try to code them as case statements (driven by a dict for instance).
Some posters have mentioned speed of operation. Speed of Run-time is secondary for me since if you need speed of execution Python is not the best language to use. I use Python as its the efficiency of coding (i.e. writing error free code) that matters to me.
I know the question is tagged python, but it mentions dynamic languages so thought I should mention that in ruby the if statement actually has a return type so you can do something like
def foo
rv = if (A > B)
A+1
else
A-1
end
return rv
end
Or because it also has implicit return simply
def foo
if (A>B)
A+1
else
A-1
end
end
which gets around the style issue of not having multiple returns quite nicely.
From a performance point of view, it doesn't matter in Python, and I'd assume the same for every modern language out there.
It really comes down to style and readability. Your second option (if-else block) is more readable than the first, and a lot more readable than the one-liner ternary operation.