1. Functional Programming Overview
1.1. What is functional programming?
Functional programming uses a series of functions to solve problems. The function accepts only input and produces output, and does not contain any internal state that can affect the resulting output. In any case, calling a function with the same parameters will always produce the same result.
In a function-based program, the input data "flows" through a series of functions, each of which produces output based on its input. The functional style avoids writing functions that have "boundary effects" (side effects): Modify the internal state, or other changes that cannot be reflected on the output. Functions that have no boundary effect are called "purely functional" (purely functional). Avoiding the boundary effect means not using a data structure that is mutable when the program is running, and the output depends only on the input.
Functional programming can be thought to stand on the opposite side of object-oriented programming. Objects usually contain internal states (fields), and many functions that can modify these states, and the program is made up of constantly changing states, while functional programming avoids state changes and works by passing data flows between functions. This is not to say that you cannot use both functional programming and object-oriented programming at the same time, in fact, complex systems typically use object-oriented technology modeling, but a mix of functional styles can also give you extra benefit from functional style.
1.2. Why use functional programming?
Functional style is generally considered to have the following advantages:
1. Logic can be proven
This is an academic advantage: no boundary effect makes it easier to logically prove that the program is correct (rather than passing the test).
2. Modular
Functional programming advocates the simple principle that a function only does one thing, splitting a large function into the smallest possible module. Small functions are easier to read and check for errors.
3. Modular
Small functions are easier to combine to form new functions.
4. Easy Commissioning
Fine-grained, well-defined functions make debugging easier. When the program is not working properly, each function is to check whether the data is the correct interface, can be faster to eliminate the problem of code, to locate the problem in place.
5. Easy to test
Functions that do not depend on the state of the system do not need to construct test piles before testing, making it easier to write unit tests.
6. Higher productivity
Functional programming produces less code than other technologies (often about half of other technologies) and is easier to read and maintain.
1.3. How do I identify functional styles?
Languages that support functional programming often have the following characteristics, and code that uses these features can be thought of as functional:
function is a one-class citizen
The function can be passed as a parameter, or returned as a return value. This feature makes the template method pattern very easy to write, which also causes the pattern to be used more frequently.
Take a simple set of sorts as an example, assuming that LST is a set of numbers and has a sort method of how sort needs to be determined in order as an argument.
If the function cannot be a parameter, the LST sort method can only accept ordinary objects as arguments. So we need to define an interface first, then define a class that implements the interface, and finally pass an instance of the class to the sort method, which is called by the Compare method of the instance, like this:
Copy the Code code as follows:
#伪代码
Interface Comparator {
Compare (O1, O2)
}
LST = List (range (5))
Lst.sort (Comparator () {
Compare (O1, O2) {
return O2-O1//reverse order
})
As we can see, we have defined a new interface, a new type (here is an anonymous class), and new object only in order to invoke a method. What if this method can be passed directly as a parameter? It should look like this:
Copy CodeThe code is as follows:
def compare (O1, O2):
return O2-o1 #逆序
LST = List (range (5))
Lst.sort (Compare)
Note that the previous code has used the anonymous class technique to save a lot of code, but it is still not as simple and natural as a direct transfer function.
anonymous function (lambda)
Lambda provides the ability to quickly write simple functions. For the occasional behavior, Lambda lets you no longer need to jump to another location to write a function while encoding.
A lambda expression defines an anonymous function, and if the function is used only in the encoded location, you can define it in the field and use it directly:
Copy the Code code as follows:
Lst.sort (Lambda O1, O2:o1.compareTo (O2))
Believe in this small example you can also feel the strong productivity:)
Built-in template functions for encapsulating control structures
In order to avoid the boundary effect, the functional style avoids using variables as much as possible, while the loop variables and the temporary variables produced in the process that are defined only to control the process are undoubtedly the most necessary to avoid.
If we need to filter on just the number of episodes to get all the positive numbers, the code using the directive style should look like this:
Copy the Code code as follows:
Lst2 = List ()
For I in range (len (LST)): #模拟经典for循环
If Lst[i] > 0:
Lst2.append (Lst[i])
This code shows the entire process from creating a new list, looping, extracting elements, judging, adding to a new list, as if the interpreter were a fool who needs hands-on guidance. However, the "filter" action is very common, why the interpreter can not grasp the filtering process, and we just need to tell it filter rules?
In Python, filtering is implemented by a built-in function called filter. With this function, the interpreter learns how to "filter", and we just need to tell it the rules:
Copy the Code code as follows:
Lst2 = filter (lambda n > 0, LST)
The benefit of this function is not simply to write a few lines of code less.
After encapsulating the control structure, the code simply needs to describe the functionality rather than the practice, which makes the code clearer and more readable. Because it avoids the disturbance of the control structure, the second piece of code clearly makes it easier to understand its intentions.
In addition, because the index is bypassed, it is unlikely that the code will trigger an exception in the subscript, unless you manually create one.
Functional programming languages typically encapsulate several common actions such as "filtering" as template functions. The only drawback is that these functions require a small amount of learning costs, but this must not obscure the benefits of using them.
Closures (closure)
Closures are functions that bind variables (but not global variables) to external scopes. In most cases, an external scope refers to an external function.
A closure contains a "reference to a variable name" in its own function body and required external functions. Referencing a variable name means that the variable name is bound, not the object that the variable actually points to, and if the variable is re-assigned, the new value will be accessible in the closure.
Closures make functions more flexible and powerful. Even if the program runs until it leaves the external function, if the closure is still visible, the bound variable is still valid, and each time it runs to an external function, the closure is recreated, and the bound variable is different, and there is no need to worry that the variable bound in the old closure will be overwritten by the new value.
Go back to the example of filter number set just now. Suppose that the boundary value of 0 in the filter condition is no longer fixed, but is controlled by the user. If there is no closure, then the code must be modified to:
Copy the Code code as follows:
Class Greater_than_helper:
def __init__ (self, minval):
Self.minval = Minval
def Is_greater_than (Self, Val):
return val > Self.minval
def my_filter (LST, minval):
Helper = Greater_than_helper (minval)
Return filter (Helper.is_greater_than, LST)
Please note that we have now written a function my_filter for the filtering function. As you can see, we need to hold another operand minval elsewhere (in this case, class Greater_than_helper).
If closures are supported, because closures can use variables from outside scopes directly, we no longer need to greater_than_helper:
Copy the Code code as follows:
def my_filter (LST, minval):
return filter (lambda n > minval, LST)
It can be seen that closures do not affect readability while also saving a lot of code.
Functional programming languages provide varying degrees of support for closures. In Python 2.x, closures cannot modify the value of a bound variable, and all behavior that modifies a bound variable is treated as a new local variable with the same name and a bound variable hidden. A new keyword, nonlocal, is added to Python 3.x to support modifying binding variables. But regardless of the level of support, you can always access (read) the bound variable.
Built-in immutable data structures
To avoid the boundary effect, immutable data structures are an integral part of functional programming. The immutable data structure ensures consistency and greatly reduces the difficulty of troubleshooting the problem.
For example, a tuple (tuple) in Python is immutable, and all operations on tuples cannot alter the contents of a tuple, and all attempts to modify the contents of a tuple produce an exception.
Functional programming languages typically provide two versions of the data structure (mutable and immutable) and recommend the use of immutable versions.
Recursive
Recursion is another way to replace loops. Recursion is actually a very common form of functional programming and can often be seen in some algorithms. But in the end, it is because we seldom use recursion in general. If a recursive cannot be optimized by a compiler or interpreter, it is easy to generate stack overflows, while complex recursion tends to confuse people rather than loops, so many best practices point to using loops rather than recursion.
The use of recursion is not a concern in this series of essays.
<第一节完>