Further understanding of function programming in Python

Source: Internet
Author: User
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.

  • Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.