In lazy evaluation , we introduced the concept of "lazy list", this concept is actually part of the native support Python This is popular with novice troubled generator and iterator up. But before, we must first review the function of the list.

From two-tuple to list

First, we can use \(\lambda\) calculus to define a two-element tuple, or pair :

  1. pair: \(\lambda a b f.f a b\)
  2. first: \(\lambda p. p(\lambda a b. a)\)
  3. second: \(\lambda p. p(\lambda a b.b)\)

The specific implementation is as follows:

pair = lambda a: lambda b: lambda f: f(a)(b)
first = lambda p: p(lambda a: lambda b: a)
second = lambda p: p(lambda a: lambda b: b)

We can define the test:

>>> p = pair(1)(2)
>>> first(p)
1
>>> second(p)
2

Of course, with pair , it is not difficult to define a list, that is, the following combination is good (we still use the python comes with 0616fb775ddd2c):

(1, (2, 3, (4, ()))))

tuples and class / type to define the list in the following chapters. But in this article, let's go back to the python concept of 0616fb775ddd59 before to see how functional programming handles the traversal problem.

List operation

List operation is an important concept of functional programming. In real time, it implements traversal of a linear result through recursion. For example, the following C-style code:

ls = [1, 2, 3, 4]

for i in range(0, len(ls)):
    ls[i] = ls[i] + 1

Here there were two effects, one i increment, and the other is ls situ operation. Moreover, they also use the concept of variable Of course, there is nothing wrong with this way of writing, and maintainability is acceptable, which can be regarded as a tolerable side effect. Of course, our simplest implementation is equivalent to everyone knows that it is a list expression (of course, in fact, it still has side effects):

[i + 1 for i in ls]

Of course, most people have also seen the complete operation of list expressions, which can bring their own filters:

[i + 1 for in in ls if i % 2 == 0]

This is the simplest operation of functional programming to traverse data. Of course, they also have names, map and filter . In Python , they return iterable objects (we can call list convert to list ):

map(lambda x: x + 1, ls) # [i + 1 for i in ls]
filter(lambda x: x % 2 == 0, ls) # [i for i in ls if x % 2 == 0]

Another commonly used list operation is reduce , which plays a role of aggregation. As long as we define a binary operation , the list can be merged from the beginning to the end aggregation operation.

reduce operation view is the process of summarizing values after traversal. For example, we want to achieve ls . In general procedural programming, we will use the following method:

res = 0

for i in ls:
    res += i # 或者和下面更类似的写法 res = res + i

However, using reduce , we only need the following code to complete.

from functools import reduce

reduce(lambda x: x + y, ls)

The specific calculation process is as follows:

  1. Get ls first value 1 and second value 2 , applied lambda x, y: x + y , to give 3 .
  2. Get ls third value 3 , the first step in applying the results of 3 and lambda get 6 .
  3. Get ls third value 4 , apply the results of the second 6 and lambda get 10
  4. The calculation is completed and the result is returned.

But, in fact, if you look at Python of reduce function parameters, we find that it can be brought into the initial value, the initial value when brought in all kinds of functional statements, usually called it fold_left / foldl function. The effect of this with or without the initial value will be much different. The first one is to deal with the problem that the list is empty:

reduce(lambda x, y: x + y, []) # 报错
reduce(lambda x, y: x + y, [], 0) # return 0

We can even correspond this to the various elements of the previous procedural programming. 0 equivalent to res = 0 , and lambda x, y: x + y is res = res + i . But, in fact, foldl than reduce more powerful level, that the operation itself can involve different types. If we use the type flag, we will find that reduce function itself can only be Callable[[S, S], S] / (S, S) -> S , but in fact, in many scenarios, what we need is a type replacement. for example:

  1. [1, 2, 3, 4] => "1234"
  2. [1, 2, 3, 4] => [[1], [2], [3], [4]]
  3. ...

If you simply use reduce we can not operate this relates to type conversion, foldl into binary arithmetic type annotation is Callable[[S, T], S] / (S, T) -> S . This allows us to achieve this by setting an initial value of another type. For example, in the above example of converting to a string, we can easily find the following binary operations (note the order):

lambda x, y: x + str(y)

The initial value only needs to set an empty "" string, which is achieved as follows (try to implement [1, 2, 3, 4] => [[1], [2], [3], [4]] !):

reduce(lambda x, y: x + str(y), ls, "")

Summarize

In this article, we reviewed the Python , and introduced functional programming to implement common data traversal problems in procedural styles through list expressions/list operations to avoid the inevitable side effects in for / while Next, we will use pair to implement a list from scratch, and then we will enter the concept of a formal lazy list to see how lazy lists deal with such problems, and use functional thinking about the concepts of streaming and threading.


三次方根
1.2k 声望101 粉丝