Functional programming in language Python
Though users usually think about Python as of procedural and object-oriented language, he contains all necessary for support completely the functional approach to programming.
In this clause{article} the general{common} concepts of functional programming are considered{examined} and ways of realization of the functional approach on Python are illustrated.
What is Python?
Python - freely distributed, very much vysokourovnevyj the interpretive language developed by Guido van Rossumom (Guido van Rossum). He combines transparent syntax with powerful (but unessential) object-oriented semantics. Python it is accessible almost on all platforms existing nowadays and possesses very high bearableness between platforms.
What is the functional programming?
It is the best way to begin with trudnejshego a question - and what, actually, such " functional programming (FP) "? - " it when you write one of possible{probable} answers in language like Lisp, Scheme, Haskell, ML, OCAML, Clean, Mercury or Erlang (or for some other) ". This answer, certainly, is correct, but not strongly clears up essence. Unfortunately, to receive precise opinion that such FP, appears very difficultly even among actually functional programmers. The parable is recollected three slepcakh and the elephant. It is possible to define{determine} also FP, having opposed it{him} to " imperative programming " (to that you do{make} in languages like C, Pascal, C ++, Java, Perl, Awk, TCL and on many others - on extreme to a measure, mostly).
Though the author in every way welcomes advice{councils} on the part of those who better him{it} knows a subject, he could characterize approximately functional programming as possessing at least several of the following properties. In the languages named functional, the below-mentioned approaches are well supported, and all other approaches are supported badly or not supported at all:
* Functions - objects of the first class. I.e., everything, that it is possible to do{make} with "data", it is possible to do{make} and with functions (like transfer of function of other function as parameter).
* Use rekursii as the basic structure of the control of a stream of management. In some languages there is no other design of a cycle, except for rekursii.
* Accent{Stress} on processing of lists (lists, from here name Lisp - LISt Processing). Lists with recursive detour of sublists often are used as replacement of cycles.
* "Pure{Clean}" functional languages avoid by-effects. It excludes almost the approach everywhere distributed in imperative languages at which the same variable consistently appropriates{gives} various values for tracking a status of the program.
* FP does not approve or completely forbids statements (statements), using instead of it calculation of expressions (i.e. functions with arguments). In a limiting case, one program is one expression (plus additional definitions).
* FP it is accented that should be calculated instead of as.
* Most part FP uses functions of " the high order " (the functions operating functions, operating functions).
Defenders of functional programming prove, that all these characteristics result in faster development of shorter and correct code. Moreover, high theorists from computer science, logic and mathematics find, that process of the proof of formal properties for functional languages and programs is much easier, than for imperative.
The functionalities inherent Python
Python supports the most part of characteristics of functional programming, since version Python 1.0. But, as the majority of opportunities Python, they are present at very much mixed language. As well as with object-oriented opportunities Python, you can use that is necessary for you, and to ignore the everything else (while it is not required to you). In Python 2.0 " the syntactic ornament " - list embeddings (list comprehensions) has been added very successful. Though and not adding essentially new opportunities, list embeddings do{make} use of many old opportunities much more pleasantly.
Base elements FP in Python - functions map (), reduce (), filter () and the operator lambda. In Python 1.x function apply (), convenient for direct application of function to the list, returned another is entered also. Python 2.0 gives for this purpose the improved syntax. A little bit unexpectedly, but and all several base operators it is almost enough these functions for a spelling of any program on Python; in particular, all managing statements ('if', 'elif', 'else', 'assert', 'try', 'except', 'finally', 'for', 'break', 'continue', 'while', 'def') it is possible to present in functional style, using exclusively functions and operators. In spite of the fact that the problem{task} of real removal{distance} of all commands of management of a stream, probably, is useful only to performance on competition " unintelligible Python " (with a code appearing as the program on Lisp'?), it is necessary to understand how FP expresses managing structures through calls of functions and rekursiju.
Exception of commands of management by a stream
The first what it is necessary to recollect in our exercise - that Python " closes nakorotko " calculation logic vyrazhenij.1 Appears, it gives an equivalent of the block 'if'/'elif '/'else' as expression. So:
* ------"Short-circuited" conditional calls in Python-----*
* Usual managing designs
if <cond1>: func1 ()
elif <cond2>: func2 ()
else: func3 ()
* Equivalent " nakorotko closed " expression
(<cond1> and func1 ()) or (<cond2> and func2 ()) or (func3 ())
* An example " nakorotko closed " expressions
>>> x = 3
>>> def pr (s): return s
>>> (x == 1 and pr ('one')) or (x == 2 and pr ('two')) or (pr ('other'))
'other'
>>> x = 2
>>> (x == 1 and pr ('one')) or (x == 2 and pr ('two')) or (pr ('other'))
'two'
It would seem, our version of conditional calls with the help of expressions - no more, than salonnyj focus; however all becomes much more interesting if to take into account, that the operator lambda can contain only expressions! Time as we have just shown, expressions can contain conditional blocks, using short circuit, expression lambda allows to present in the general{common} form conditional returned values. Being based on the previous example:
* ---------Lambda with short-circuited conditionals in Python-------*
>>> pr = lambda s:s
>>> namenum = lambda x: (x == 1 and pr ("one"))
... or (x == 2 and pr ("two"))
... or (pr ("other"))
>>> namenum (1)
'one'
>>> namenum (2)
'two'
>>> namenum (3)
'other'
Functions as objects of the first class
The resulted examples already have testified, though also unevident image, the status of functions as objects of the first class in Python. The matter is that, having created object of function by the operator lambda, we have made extremely the general{common} action. We had an opportunity to adhere our object to names pr and namenum in accuracy in the same way as could adhere to these names number 23 or a line "spam". But the same way, as number 23 can be used, not adhering to any name (for example as argument of function), we can use the object of function created lambda, not adhering to any name. Function in Python - only one more value with which it is possible to make something.
The main thing, that we do{make} with our objects of the first class - we pass them in the built - in functions map (), reduce () and filter (). Each of these functions accepts object of function as the first argument. map () applies the transferred{handed} function to each element in the transferred{handed} list (lists) and returns the list of results. reduce () applies the transferred{handed} function to each value in the list and to the internal store of result; for example, reduce (lambda n, m:n*m, range (1,10)) means 10! (a factorial 10 - to increase each element on result of the previous multiplication). filter () applies the transferred{handed} function to each element of the list and returns the list of those elements of the initial list for which the transferred{handed} function has returned value of the validity. We also often pass functional objects to our own functions, but is $more often to some combinations of the above mentioned built - in functions.
Combining these three built - in FP-functions, it is possible to realize unexpectedly wide range of operations of a stream of management, not resorting to statements (statements), and using only expressions.
Functional cycles in Python
Replacement of cycles by expressions as is simple, as well as replacement of conditional blocks. 'for' can vprjamuju be translated in map (). The same as and with conditional performance, we need to simplify the block of statements up to one call of function (we are close to learning to do{make} it generally):
* ----------the Functional cycle 'for' in Python----------*
for e in lst: func (e) * a cycle on the statement 'for'
map (func, lst) * a cycle based on map ()
By the way, the similar technics{technical equipment} is applied to realization of consecutive performance of the program, using the functional approach. I.e., imperative programming generally will consist of the statements demanding " to make it, then to make that, then to make something else ". ' map () ' allows to express it so:
* -----Functional consecutive performance in Python----------*
* We shall create auxiliary function of a call of function
do_it = lambda f: f ()
* Let f1, f2, f3 (etc) - the functions which are carrying out efficiency duties
map (do_it, [f1, f2, f3]) * the consecutive performance realized on map ()
Generally, all main program can be a call ' map () ' with the list of functions, which should be called consistently to execute the program. One more convenient property of functions as objects - that you can place them in the list.
To translate 'while' vprjamuju it is little bit more complex{difficult}, but quite it turns out:
* --------the Functional cycle 'while' in Python----------*
* Usual (osnovanyj on the statement 'while') a cycle
while <cond>:
<pre-suite>
if <break_condition>:
break
else:
<suite>
* A recursive cycle in functional style
def while_block ():
<pre-suite>
if <break_condition>:
return 1
else:
<suite>
return 0
while_FP = lambda: (<cond> and while_block ()) or while_FP ()
while_FP ()
Our variant 'while' still demands function while_block () which in itself can contain not only expressions, but also statements (statements). But we could continue the further exception of statements in this function (as, for example, replacement of the block 'if/else' in the above described pattern on short-circuited expression). Besides, usual spot check <cond> (like ' while myvar == 7 ') hardly appears useful, as the body of a cycle (in the submitted kind) cannot change any variable (though global variables can be changed in while_block ()). One of ways to apply more useful condition - to force while_block () to return more intelligent value and to compare it{him} to a condition of end. It is necessary to look at a real example of exception of statements:
* ----------the Functional cycle 'echo' in Python------------*
* The imperative version " echo () "
def echo_IMP ():
while 1:
x = raw_input (" IMP - ")
if x == 'quit':
break
else
print x
echo_IMP ()
* The service function realizing " identity with a by-effect "
def monadic_print (x):
print x
return x
* FP the version " echo () "
echo_FP = lambda: monadic_print (raw_input (" FP - ")) == 'quit' or echo_FP ()
echo_FP ()
We have reached{achieved} that have expressed the small program including an input / conclusion, cycles and conditions as pure{clean} expression with rekursiej (actually - as functional object which if necessary can be transferred{handed} anywhere). All of us still use service function monadic_print (), but this function completely general and can be used in any functional expressions which we shall create later (it is unitary expenses) .23 Notice, that any expression containing monadic_print (x) is calculated the same as if it contained simply x. In FP (in particular, in Haskell) there is a concept of "monad" for function which " does not do{make} anything, and causes a by-effect at performance ".
Exception of by-effects
After all done job on disposal of completely intelligent designs and their replacement by the unintelligible enclosed expressions, there is a natural question - " What for?! ". Re-reading my descriptions of characteristics FP, we can see, that all of them are achieved in Python. But the major (and, most likely, to the greatest degree really used) the characteristic - exception of by-effects or, at least, restriction of their application by special areas like monads. The huge percent{interest} of program mistakes and the main problem demanding application of debuggers, happens that variables receive incorrect values during performance of the program. Functional programming bypasses this problem, is simple at all not appropriating{giving} value to variables.
Let's look at completely usual site of an imperative code. His{its} purpose - to unpack{print out} the list of pairs numbers, whose product is more 25. The numbers making pairs, undertake from two other lists. All this rather reminds that programmers really do{make} in many sites of the programs. The imperative approach to this problem{task} could look so:
* ---the Imperative code for " a seal of products "----*
* Procedural style - search of the big products with the help of the enclosed cycles
xs = (1,2,3,4)
ys = (10,15,3,22)
bigmuls = []
*... An other code...
for x in xs:
for y in ys:
*... An other code...
if x*y> 25:
bigmuls.append ((x, y))
*... An other code...
*... An other code...
print bigmuls
This project is too small that something went not so. But, probably, he is built - in a code intended for achievement of set of other purposes at the same time. Section, kommentirovannye as " *... An other code... " - places where by-effects with the greatest probability can lead to to mistakes. In any of these points variables xs, ys, bigmuls, x, y can get unexpected values in a hypothetical code. Further, after end of this piece of a code all variables can have values which can are expected, and can and not be expected posdedujuhhim a code. It is obvious, that inkapsuljacija in functions / objects and careful management of area of visibility can be used to be protected from this sort of problems. You also can delete always ('del') your variables after use. But, in the practice, the specified type of mistakes is rather usual.
The functional approach to our problem{task} completely excludes the mistakes connected to by-effects. The possible{probable} decision could be such:
* ---the Functional code for a search / seal of the big products on Python----*
bigmuls = lambda xs, ys: filter (lambda (x, y):x*y> 25, combine (xs, ys))
combine = lambda xs, ys: map (None, xs*len (ys), dupelms (ys, len (xs)))
dupelms = lambda lst, n: reduce (lambda s, t:s+t, map (lambda l, n=n: [l] *n, lst))
print bigmuls ((1,2,3,4), (10,15,3,22))
We connect in an example anonymous ('lambda') functions with names, but it is not necessary. Instead of it we could enclose definitions simply. We used names as for the sake of the greater readership, and because combine () - in any case excellent{different} service function (generates the list of all possible{probable} pairs elements from two lists). In turn, dupelms () basically only an accessory combine (). Though this functional example is more verbose, than imperative, at a reuse of service functions the code in actually bigmuls () appears, probably, more laconic, than in an imperative variant.
Real advantage of this functional example that in him is absolute any variable does not change the value. Any unexpected collateral influence on the subsequent code (or on the part of the previous code) is simply impossible. Certainly, in itself absence of by-effects does not guarantee a faultlessness of a code, but in any case this advantage. However notice, that Python, as against many functional languages, does not prevent repeated attachment of names bigmuls, combine and dupelms. If is farther during performance of the program combine () something will start to mean another - alas! It would be possible to develop the class - single (Singleton) for support of unitary linkage of such type (napr. 's.bigmuls', etc.), but it is beyond present{true} clause{article}.
Still it is necessary to note, that a problem{task} which we have just solved, it is cut out in accuracy under new opportunities Python 2.0. Instead of the above-stated examples - imperative or functional - the best (and functional) the technics{technical equipment} looks as follows:
* -----Code Python for "bigmuls" with use of list embeddings (list comprehensions)-----*
print [(x, y) for x in (1,2,3,4) for y in (10,15,3,22) if x*y> 25]
The conclusion
This clause{article} has shown ways of replacement practically to any design of management of a stream in Python on a functional equivalent (getting rid thus from by-effects). Effective translation of the concrete program demands additional considering, but we have seen, that built - in functional primitivy are full and the general{common}. In subsequent clauses{articles} we shall consider more powerful approaches to functional programming; and, I hope, we can consider more in detail "pro" and "contra" the functional approach.

|