Further understanding of function programming in Python

Source: Internet
Author: User
This article mainly introduces how to further understand function programming in Python. this article further discusses some key points of function programming in Python, from the IBM official technical documentation, for more information, see the most difficult question: "What is function programming (FP )?" One answer may be that FP is what you do when programming using languages such as Lisp, Scheme, Haskell, ML, OCAML, Clean, Mercury, Erlang (or others. This is a secure answer, but it cannot be clarified very accurately. Unfortunately, it is difficult for function programmers to have a consistent understanding of what FP is. It seems appropriate to use the story of "blindly touching people" to describe this situation. You can also safely program FP with "command programming" (operations performed using C, Pascal, C ++, Java, Perl, Awk, TCL, and most other languages, at least to a large extent.

From a personal perspective, I will roughly depict function programming as having at least the following features. Functional languages make these things simple and make other things difficult or impossible:

  • Function is the first class (object ). That is, each operation that can be performed on "data" can be performed using the function itself (for example, passing a function to another function ).
  • Recursion is used as the main control structure. In some languages, there are no other "loop" structures.
  • The focus is on LISt processing (for example, name Lisp ). Lists are often used together with sublists recursively to replace loops.
  • "Pure" function language can avoid side effects. This does not include the most common mode in the command language, that is, to specify the first, and then specify the other value to the same variable to track the state of the program.
  • FP does not encourage or allow statements at all. Instead, it uses expressions to evaluate values (in other words, adding independent variables to a function ). In pure cases, a program is an expression (with supported definitions ).
  • FP is concerned with computing rather than how to calculate.
  • Many FP use "higher-level" functions (in other words, functions operate on some functions, and these functions operate on other functions ).

Function programming advocates believe that all these features lead to faster development of less code and fewer errors. Moreover, senior theoretical scientists in computer science, logic, and mathematics have discovered that the formal performance of function languages and programs is much easier than that of command languages and programs.
Inherent Python function capabilities

Since Python 1.0, Python has seen most of the FP features listed above. But for most Python features, they are presented in a very mixed language. Largely because of the OOP feature of Python, you can ignore the rest of the part you want to use (until you need it later ). Using Python 2.0, the list content adds some great "syntactic whitewashing ". Although the list content does not add any new capabilities, they make many old capabilities look much better.

The basic elements of FP in Python are map (), reduce (), filter (), and operator lambda. In Python 1.x, the apply () function is convenient for directly applying the return values of a function list to another function. Python 2.0 provides improved syntax for this purpose. It may be surprising, but few of these functions (and basic operators) are almost enough to write any Python program; in particular, all flow control statements (if, elif, else, assert, try, deny T, finally, for, break, continue, while, def) you can use only FP functions and operators for processing in the function style. Although in fact eliminating all the flow control commands in the program may only be useful for joining the "chaotic Python" competition (and code that looks like Lisp, however, it is worthwhile to understand how FP uses functions and recursion to represent Stream Control.


Eliminate flow control statements

The first thing we need to consider when executing the elimination of contact is the fact that Python "short-circuited" the Boolean expression. In this way, the if/elif/else block of the expression version is provided (assuming that each part calls a function, it is always possible to do so ). The specific method is as follows:
Listing 1. "short circuit" condition 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(  'two'))   or   (pr(  'other'))   'other' >>> x = 2 >>> (x==1   and   pr(  'one'))   or   (x==2   and   pr(  'two'))   or   (pr(  'other'))   'two'
    
   
  
 

Conditional calls of expression versions seem to be just a bit tricky; however, it is even more interesting if we notice that lambda operators must return expressions. Because -- as shown in the preceding figure -- expressions can contain condition blocks through short circuits, lambda expressions are very common in expressing condition return values. Build in our example:
List 2. short-circuit Lambda 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'


Function as the first class object

The above example shows the status of the first class of a function in Python, but in a very subtle way. When using lambda to create function objects, we have some general things. Because of this, we can bind the object to the names "pr" and "namenum", using the same method as binding the number 23 or string "spam" to these names. But just as we can use number 23 without binding it to any name (in other words, like a function independent variable ), we can use a function object created with lambda without binding it to any name. A function is just another value for performing some operations on it in Python.

The main operation we perform on the first type of objects is to pass them to the built-in FP functions map (), reduce (), and filter (). Each of these functions accepts the function object as its first independent variable.

Map () executes the passed function for each corresponding item in the specified list and returns the result list.
Reduce () executes the passed function for each subsequent item and returns the internal accumulation of the final result. for example, reduce (lambda n, m: n * m, range )) it means "10 factorial" (in other words, the product of each forward multiplication of each item ).
Filter () uses the passed function to evaluate each item in the list, and then returns the list of checked items that pass the 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 above.

By combining these three built-in FP functions, you can execute an astonishing series of "stream" operations (not using statements, but using expressions ).


Function loop in Python

The replacement cycle is as simple as the replacement condition block. For can be directly converted to map (). For conditional execution, we need to simplify the statement block into a single function call (we are gradually approaching the usual practice ):
Listing 3. the Function 'for' loop in Python

for   e   in   lst: func(e)   # statement-based loopmap(func,lst)    # map()-based loop

In addition, there are similar technologies for function methods of continuous program streams. That is, command programming usually involves "doing this, then doing that, and then doing other things ." Such a statement. Map () let us do exactly this:
Listing 4. Continuous function operations in Python

# let's create an execution utility functiondo_it =   lambda   f: f()  # let f1, f2, f3 (etc) be functions that perform actionsmap(do_it, [f1,f2,f3])   # map()-based action sequence

Generally, our entire main program can be a map () expression and a series of functions required to complete the program. Another convenient feature of the first type of functions is that they can be put in a list.

While conversion is a little complicated, but it can still be performed directly:
Listing 5. the 'with' loop function in Python

# statement-based while loop  while   
 
  : 
  
      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()
        
       
      
     
    
   
  
 

While conversion still requires the while_block () function, which contains statements rather than expressions. However, we need to further eliminate this function (for example, short-circuit the if/else in the template ). In addition, because the cyclic subject (by design) cannot change any variable value It is difficult to use it in a general test, such as while myvar = 7 (then, all content will be modified in while_block ). One way to add more useful conditions is to let while_block () return a more interesting value, and then compare the returned value with the termination condition. It is necessary to take a look at the specific examples of these elimination statements:
Listing 6. 'echo 'loop in Python

# imperative version of "echo()"  defecho_IMP  ():   while   1: x = raw_input(  "IMP -- ")   if   x ==   '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 have done is to try to represent a small program involving I/O, loops, and condition statements into a pure expression with recursion (in fact, if you need, function objects that can be passed to any other place ). We do still use the utility function monadic_print (), but this function is completely generic and can be reused in every function expression we create later (it is a one-time cost ). Note that any expression containing monadic_print (x) evaluates the same result, just as it contains only x. FP (especially Haskell) has a "single body" meaning for a function that does not execute any operation and has side effects in the process.


Eliminate side effects

After removing perfect and meaningful statements and replacing them with obscure and nested expressions, a natural question is: "Why ?!" All my descriptions of FP are implemented using Python. But the most important feature-probably the most useful feature in a specific situation-is that it eliminates side effects (or at least has some restraining effect on some special fields, such as a single body ). The vast majority of program errors-and issues that lead programmers to turn to debugging for solutions-occur because variables contain unexpected values during program execution. Function programs do not assign values to variables at all, thus avoiding this special problem.

Let's look at a fairly common command code. It is used to print a list of digits with a multiplier value greater than 25. The numbers that make up each pair are selected from the other two lists. This operation is similar to the operations actually performed by programmers in their program segments. The command to achieve this is as follows:
Listing 7. Python code of the "print multiplication product" command

# Nested loop procedural style for finding big productsxs = (1,2,3,4)ys = (10,15,3,22)bigmuls = []  # ...more stuff...  for   x   in   xs:   for   y   in   ys:   # ...more stuff...     if   x*y > 25:  bigmuls.append((x,y))    # ...more stuff...# ...more stuff...  print   bigmuls

This project is too small to cause any errors. However, our goal may be embedded in code that needs to implement many other purposes at the same time. Comments with "more stuff" are the places where side effects may cause errors. In any of these places, the variables xs, ys, bigmuls, x, and y may obtain unexpected values in the hypothetical section code. Besides, after executing this code, all variables may have values that may or may not be needed later. Obviously, you can use function/instance encapsulation and related scope considerations to prevent such errors. Besides, you can always del the variables after they are executed. However, in practice, these error types are very common.

The target function methods completely eliminate these side effects. The following code is possible:
Listing 8. Python code for the function "print a multiplication product"

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))

In this example, we bind an anonymous (lambda) function object to the name, but this is not necessary. We can just nest the definition. This is intended for readability, but it is also because combine () is a well-known utility function that generates a list of all element pairs from two input lists ). The subsequent dupelms () is mainly a method that helps combine () to take effect. Even if the function example is longer than the command example, the new code in bigmuls () may be less than the code in the command version, given that the utility function can be reused.

The real advantage of this function example is that there is absolutely no variable to change any of the values. There are no unexpected side effects in later code (not in earlier code ). Obviously, it has no side effects and does not guarantee correct code, but even so, this is also an advantage. However, note that Python (unlike many function languages) cannot prevent rebinding of names bigmuls, combine, and dupelms. If combine () has other meanings at the beginning of a later part of the program, all efforts will be abandoned. You can gradually create a Singleton class to include this type of immutable binding (such as s. bigmuls), but this column does not cover this content.

One of the most noteworthy problems is that our specific purpose is to customize the new features in Python 2. The best (and function) technology is neither the command example provided above nor the function instance,:
Listing 9. "bigmuls" list connotation 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 introduced how to replace each Python flow control constructor with function equivalents (side effects are eliminated during the process ). Effective conversion of specific programs will bring about some additional considerations, but we already know that the built-in functions are general and complete. In a later column, we will consider some more advanced function programming technologies, and hope to explore more advantages and disadvantages of the function 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.