In this article, we are going to implement a List general article, we do not use classes to implement it here, but use the basic data structure, the binary tuple (a, b) and the empty tuple () to implement it. Both of these can be lambda , and the specific method can refer to the content of the previous article.

Let's consider, List (also called linked list). The most important thing is to create a pattern that can expand itself infinitely, save one value and the next data, for example, [1, 2, 3, 4] we can use (1, (2, (3, (4, ())))) . We must specify an end, this is () in it, () represents the meaning of the empty list and the end of the list at the same time. Very easily, we can define the list as follows (I have included a function here, just to isolate the data and prevent us from using the built-in comparison to implement some functions):

def cons(head, tail):
    def helper():
        return (head, tail)
    return helper

Then we define two functions to get the data inside, similar to first and second in the previous interface:

head = lambda cons_list: cons_list()[0]
tail = lambda cons_list: cons_list()[1]

We can define a function to represent the empty variable empty_list_base . After that, in order to facilitate the calculation, we can write a cons (of course, this implementation uses the *arg , we use this syntactic sugar feature by default):

def cons_apply(*args):
    if len(args) == 0:
        return empty_list_base
    else:
        return cons(args[0], cons_apply(*args[1:]))

In this way, we can easily complete the new List :

>>> cons_apply(1, 2, 3) # 返回cons(1, cos(2, cos(3, ())))

To facilitate comparison, we can also define a function to determine whether the lists are equal:

def equal_cons(this: ListBase[S], that: ) -> bool:
    if this == empty_list_base and that != empty_list_base:
        return False
    elif this != empty_list_base and that == empty_list_base:
        return False
    elif this == empty_list_base and that == empty_list_base:
        return True
    else:
        return head(this) == head(that) and equal_cons(tail(this), tail(that))

Now we can easily do some verification.

>>> assert equal_cons(cons_apply(1, 2, 3), cons(1, cons(2, cons(3, ()))))

Now what we need is to implement some list operations that do not need to be implemented in a loop, that is, map , fold_left and filter previous article.

map role is to function f bring value to every list that we bring f head of the list after, then map applied to tail , namely:

def map_cons(f, cons_list):
    if cons_list == ():
        return empty_list_base
    else:
        return cons(f(head(cons_list)), map_cons(f, tail(cons_list)))

In the same way, we can achieve filter and fold_left :

def filter_cons(f, cons_list):
    if cons_list == ():
        return empty_list_base
    else:
        hd, tl = head(cons_list), tail(cons_list)
        if f(hd):
            return cons(hd, filter_cons(f, tl))
        else:
            return tl

def fold_left_cons(f, init, cons_list):
    if cons_list == ():
        return init
    else:
        return fold_left_cons(f, f(init, head(cons_list)), tail(cons_list))

In this way, we can achieve some basic functions, such as [1, 2, 3, 4, 5] , filtering the even number and summing, then it can be written as:

>>> res = fold_left_cons(lambda x, y: x + y, 0,
>>>    filter_cons(lambda x: x % 2 == 0, 
>>>        map_cons(lambda x: x + 1,
>>>            cons_apply(1, 2, 3, 4, 5)
>>>    )))
>>> res == 12

Of course, this style of code is very poorly readable for nesting. Here we think of the and_then or compose functions we implemented before, which can be combined with these water pipe structures. However, it will be easier to write these functions by changing them to Curry. So you can use the style of function composition:

map_cons_curry = lambda f: lambda cons_list: map_cons(f, cons_list)
filter_cons_curry = lambda f: lambda cons_list: filter_cons(f, cons_list)
fold_left_cons_curry = lambda f: lambda init: lambda cons_list: fold_left_cons(f, init, cons_list)

The specific call is the following method:

>>> f = and_then(
>>>    map_cons_curry(lambda x: x + 1),
>>>    filter_cons_curry(lambda x: x % 2 == 0),
>>>    fold_left_cons_curry(lambda x, y: x + y)(0),
>>> )
>>>
>>> assert f(cons_apply(1, 2, 3, 4, 5)) == 12

If you use the fppy (click here to enter) the example that I maintain, you can also use a F_ , so that you can implement another class-based chaining method:

from fppy.base import F_, I

F_(I)\
    .and_then(map_cons_curry(lambda x: x + 1))\
    .and_then(filter_cons_curry(lambda x: x % 2 == 0))\
    .and_then(fold_left_cons_curry(lambda x, y: x + y)(0))\
    .apply(cons_apply(1, 2, 3, 4, 5)) # 返回12

In this article, we simply use the concepts of binary tuples, equality, and functions to maintain the results of a list, and can traverse calculations and filter through some list function alignment. In the next article, we will start to discuss the concepts of category and type roughly, which will facilitate our future discussion.


三次方根
1.2k 声望101 粉丝