We'd better start with the hardest question: "What is functional programming (FP)?" "One answer might be that FP is what you do when you're programming with Lisp, Scheme, Haskell, ML, OCAML, clean, Mercury, Erlang (or some other) language. This is a safe answer, but it is not clear what the problem is. Unfortunately, even functional programmers themselves have a hard time understanding exactly what FP is. The story of "elephant" seems appropriate to describe the situation. You can also safely compare the FP with "command programming" (for example, C, Pascal, C + +, Java, Perl, Awk, TCL, and most other languages, at least to a large extent).
From a personal point of view, I will roughly portray function programming as having at least a few of the following characteristics. A functional language makes these things simple and makes other things difficult or impossible:
- A function is the first Class (object). That is, every action that can be made to "data" can be done using the function itself (for example, passing a function to another function).
- Recursion is used as the primary control structure. In some languages, there are no other "looping" constructs.
- Focus on List processing in lists (for example, name Lisp). Lists are often used in conjunction with recursion of sub-lists to replace loops.
- The "pure" function language avoids side effects. This does not include the most prevalent pattern in the command language, specifying the first one, and then assigning another value to the same variable to track program state.
- The FP does not encourage or allow statements at all, instead of using expressions to evaluate (in other words, the function plus an argument). In a very pure case, a program is an expression (plus a supported definition).
- The FP cares about what to calculate and not how to calculate it.
- Many FP take advantage of the "higher level" function (in other words, the function operates on some functions, and these functions operate on other functions).
Proponents of functional programming believe that all of these characteristics lead to faster development of shorter and less-error code. Moreover, advanced theorists in the fields of computer science, logic, and mathematics have found it much easier to justify the formal performance of functional languages and programs than command languages and programs.
Inherent Python function capabilities
Since Python 1.0, Python has had most of the FP features listed above. But for most Python features, they are presented in a very mixed language. In large part because of the Python OOP feature, you can ignore the rest of the part you want to use (until you need it later). Using Python 2.0, the list connotation adds some great "syntactic whitewash". Although the list connotation does not add any new capabilities, they make many of the old capabilities look a lot better.
The basic elements of FP in Python are function map (), reduce () and filter (), and operator Lambda. In Python 1.x, the Apply () function is also handy for applying a function's list return value directly to another function. Python 2.0 provides an improved syntax for this purpose. It may be surprising, but few of these functions (and basic operators) are nearly enough to write any Python program; In particular, all flow control statements (if, elif, else, assert, try, except, finally, for, break , continue, while, Def) can be processed using only FP functions and operators in a function style. While virtually eliminating all flow control commands in a program may be useful only for joining the "chaotic Python" race (with code that looks very like Lisp), it is worthwhile to understand how the FP uses functions and recursion to represent flow control.
Eliminate flow control statements
The first thing to consider when we do the elimination of contact is the fact that Python "shorted out" the evaluation of the Boolean expression. This provides an expression version of the If/elif/else block (assuming that each block invokes a function, which is usually always possible). Here's how:
Listing 1. "Short-circuit" conditional call in Python
# Normal statement-based Flow control if
: func1 () elif
: Func2 () else : func3 () # equivalent "Short circuit" expression (and
func1 ()) or (
and
Func2 ()) or (func3 ()) # Example "Short circuit" expression >>> x = 3 > >> DEFPR (s): return s >>> (x==1 and PR ( ' one ')) or (x==2 and PR ( ')) or (pr (' other ')) ' other ' >>> x = 2 >>> ( X==1 and PR (' one ')) or ( x==2 and pr ( ')) or (pr ( ' Other ') '
The conditional invocation of the expression version seems to be just a positional trick, but it's even more interesting if we notice that the lambda operator must return an expression. Because-as shown earlier-the expression can contain a conditional block by a short-circuit, the lambda expression is very common in the return value of the expression condition. Build on our example:
Listing 2. LAMBDA Short Circuit in Python
>>> PR = Lambda s:s >>> namenum = lambda x: (x==1 and PR ( "one")) \ .... or (x==2 and PR ( "both")) \ .... or (pr ( "other")) >>> Namenum (1) ' one ' >>> namenum (2) ' one ' >>> Namenum (3) ' other '
function as the first class object
The above example already shows the status of the first class where the function is in Python, but in a very subtle way. When creating a function object using lambda operations, we have some completely normal things. Because of this, we can bind the object to the name "PR" and "Namenum", using a method that is identical to the method that binds the number 23 or the string "spam" to those names. But just as we can use the number 23 without having to bind it to any name (in other words, like a function argument), we can use a function object created with lambda without binding it to any name. A function is just another value that we perform some action on in Python.
The primary action we perform on the first class of objects is to pass them to the FP built-in function map (), reduce (), and filter (). Each of these functions accepts a function object as its first argument.
Map () Executes the passed function on each corresponding item in the specified list, and returns a list of results.
Reduce () performs a pass-through function on each successive item, returning the internal accumulation of the final result, such as reduce (lambda n,m:n*m, Range (1,10)), which means "factorial of 10" (in other words, the multiplication of the previous multiplication of each item).
Filter () uses the passed function to "evaluate" each item in the list, and then returns a screened list of items that passed the pass-through function test.
We also often pass function objects to our own custom functions, but they are usually equivalent to the combination of the built-in functions described above.
By combining these three FP built-in functions, you can perform an astonishing series of "flow" operations (none using statements, only expressions).
function loops in Python
Replacing a loop is as simple as replacing a condition block. For can be converted directly to map (). For our conditional execution, we need to simplify the block of statements into a single function call (we are getting closer to the usual practice):
Listing 3. function ' for ' loops in Python
For e in lst:func (e) # statement-based Loopmap (FUNC,LST) # map ()-based Loop
In addition, there are similar techniques for the function methods of continuous program flow. That is, command programming usually contains close to "Do this, then do that, then do other things." "Such a statement. Map () Let's just do this:
Listing 4. function continuous operation in Python
# Let's create an execution utility functiondo_it = Lambda f:f () # Let F1, F2, F3 (etc) is functions that per Form Actionsmap (Do_it, [f1,f2,f3]) # Map ()-based action sequence
In general, our entire main program can be a map () expression and a series of functions that need to be executed to complete the program. Another handy feature of the first class of functions is that they can be placed in a list.
While transitions are slightly more complex, but can still be done directly:
Listing 5. function ' while ' Loop in Python
# statement-based while Loop and
:
if
: Break else :
# Fp-style recursive while Loopp defwhile_block ():
if
: Return 1 Else:
return 0while_fp = lambda: (and
while_block ()) or WHILE_FP () WHILE_FP ()
c29/>
A while conversion still requires the While_block () function, which itself contains statements rather than just expressions. But we need to do a further elimination of the function (for example, short-circuiting the if/else in the template). Also, because the loop body (by design) cannot change any of the variable values, it is difficult to use in a generic test, such as while myvar==7 (then the entire content will be modified in While_block ()). One way to add more useful conditions is to have While_block () return a more interesting value, and then compare the return value to the termination condition. It is necessary to take a look at the specific examples of these elimination statements:
Listing 6. function ' echo ' Loop in Python
# imperative version of "Echo ()" defecho_imp (): while 1:x = Raw_input ( "IMP--") if x ==< c7/> ' quit ': Break else print xecho_imp () # utility function for "identity with Side-effect" Defmonadic_print (x): print x return x # FP version of "Echo ()" ECHO_FP = Lambda : Monadic_print (raw_input ( "FP--")) = = ' quit ' or echo_fp () echo_fp ()
What we've done is try to represent a small program that involves I/O, loops, and conditional statements as a pure expression with recursion (in fact, if needed, it can be represented as a function object that can be passed anywhere else). We do still take advantage of the utility function Monadic_print (), but this function is completely generic and can be reused in every function program expression we create later (it's a one-time cost). Note that any expression that contains Monadic_print (x) evaluates to the same result as if it contains only X. FP (especially Haskell) has a "single body" meaning for the function "do not do anything and have side effects in the process".
Eliminate side effects
After the work of removing the perfect, meaningful statement instead of the obscure, nested expression, a natural question is: "Why?!" "All my descriptions of FP are done using Python. But the most important feature--perhaps the most useful feature in a specific case--is that it eliminates side effects (or at least some specific areas, such as a single one, with some diversion). The vast majority of program errors--and the problems that prompt programmers to turn to debugging--occur because the variables contain unexpected values during the execution of the program. A function program avoids this particular problem by simply not assigning a value to the variable at all.
Let's look at a fairly common command code. Its purpose is to print a list of several pairs of numbers with a Chuyang product greater than 25. The numbers that make up the pairs themselves are picked up from a list of two other lists. This is similar to what programmers actually do in their program segment. The command method for this purpose is as follows:
Listing 7. Command Python code for "Print Mahayana product"
# Nested Loop procedural style for finding big Productsxs = (1,2,3,4) ys = (10,15,3,22) bigmuls = [] # ... more stuff...< C1/>for x in xs: for y in ys: # ... more stuff ... If x*y >: bigmuls.append ((x, y) # ... more stuff...# ... more stuff ... Print Bigmuls
The project is so small that nothing can go wrong. But our purpose may be embedded in code that implements many other purposes at the same time. Those parts of the comment with "more stuff" are places where side effects can cause errors to occur. At any one of these places, the variables xs, YS, Bigmuls, X, y are likely to get unexpected values in the assumed abridged code. Furthermore, after executing this piece of code, all variables may have values that may or may not be needed later in the code. It is clear that this type of error can be prevented by using the encapsulation of the function/instance form and the consideration of the scope. And, you can always go after executing the variables del them. In practice, however, these identified types of errors are very common.
The function method of the target completely eliminates these side-effects errors. The following is a possible piece of code:
Listing 8. Function Python code for "Printing Mahayana product"
Bigmuls = Lambda xs,ys:filter ( Lambda (x, y): x*y >, 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))
In the example, we bind the anonymous (lambda) function object to the name, but this is not necessarily necessary. We can only nest definitions. This is done for readability purposes, but also because combine () is a good utility function that is readily available (a list of all pairs of elements from both input lists). The subsequent dupelms () is primarily a way to help combine () to play a role. Even though this function example is more verbose than the command example, the new code in Bigmuls () may itself be less than the number of code in the command version, once the utility function can be reused.
The real advantage of this function example is that there is absolutely no variable to change any of these values. There are no possible unintended side effects in later code (not in earlier code). Obviously, it does not have side-effects and does not guarantee that the code is correct, but even so, it is an advantage. Note, however, that Python (unlike many function languages) does not prevent rebinding of names Bigmuls, combine, and Dupelms. If combine () starts to have other meanings in the later part of the program, all efforts are wasted. You can gradually build a Singleton class to contain this type of immutable binding (such as s.bigmuls, etc.), but this column does not cover this content.
A particular concern is that our specific purpose is to customize the new features in Python 2. The best (and also the function) technique is neither an example of the command provided above, nor a function instance, but:
Listing 9. List of "Bigmuls" Python code
Print [(x, y) for x in (1,2,3,4) for y in (10,15,3,22) if x*y > 25]
Conclusion
I have described the methods used to replace each Python flow control construct with the function equivalents (side effects are eliminated in the process). The effective conversion of a particular program brings some additional considerations, but we already know that the built-in functions are regular and complete. In a later column, we'll consider some of the more advanced functional programming techniques, and hopefully explore the more pros and cons of a functional style.