The familiar song came from a distance,
Why are those voices so weak.
Long time no see. How are you now?
Is there such a song,
Will let you gently follow and,
As our lives rise and fall,
A theme song sung together;
Is there such a song,
Will suddenly remind you of me,
Make you happy and worry,
Such a me
The music is over and back to business. Recently, I browsed LeetCode and found a very interesting topic. When I tried to solve it in Python, I actually used collection, map function, zip function, lambda function and sorted function. The debugging process also involved the concepts of iterator, generator and list derivation. A seemingly very simple topic, although the final code can be combined into one line, it almost uses the programming skills of Python once. It can be said that "the subtle points see the spirit"! Through this topic, you may really understand python programming from now on.
This problem is called "lucky numbers in the list". What is a lucky number? In the integer list, if the frequency of a number is equal to its value, we call it "lucky number". For example, in the list [1, 2, 2, 3], the number 1 and number 2 appear 1 and 2 respectively, so they are lucky numbers, but 3 only appears once, and 3 is not a lucky number.
After understanding the concept of lucky numbers, let's try to find the lucky numbers in the list [3, 5, 2, 7, 3, 1, 2, 4, 8, 9, 3]. This process can be divided into the following steps:
- Find numbers that do not repeat in the list
- Count the number of times each number appears in the list
- Find those numbers whose occurrences are equal to the number itself
Step 1, find the numbers that are not repeated in the list
Find out the non duplicate numbers in the list, that is, remove the duplicate elements in the list, referred to as "de duplication". The simplest way to remove duplicates is to use collections.
>>> arr = [3,5,2,7,3,8,1,2,4,8,9,3] >>> unique = set(arr) >>> unique {1, 2, 3, 4, 5, 7, 8, 9}
Step 2, count the number of times each number appears in the list
As we know, the list object comes with a count() method, which can return the number of times an element appears in the list. The specific usage is as follows:
>>> arr = [3,5,2,7,3,8,1,2,4,8,9,3] >>> arr.count(8) # Element 8 appears twice in array arr 2
Next, we only need to traverse each element after de duplication, count their occurrence times one by one, and save them into an appropriate data structure. This step is all right.
>>> arr = [3,5,2,7,3,8,1,2,4,8,9,3] >>> unique = set(arr) # Remove duplicate elements >>> pairs = list() # Empty list, which is used to save tuples composed of array elements and occurrences >>> for i in unique: pairs.append((i, arr.count(i))) >>> pairs [(1, 1), (2, 2), (3, 3), (4, 1), (5, 1), (7, 1), (8, 2), (9, 1)]
As a novice, it's good to write the code like this. However, a aspiring programmer will never be complacent and stagnant. Their favorite thing to do is to try their best to eliminate the for loop, such as using mapping functions and filter functions to replace the for loop; Even if the for loop cannot be rejected, they will hide the loop as much as possible, such as in the list derivation. Since it is necessary to call the count() method of the list for each element, it is most suitable to replace the for loop with the map function.
>>> m = map(arr.count, unique) >>> m <map object at 0x0000020A2D090E08> >>> list(m) # The generator can be converted to a list [1, 2, 3, 1, 1, 1, 2, 1] >>> list(m) # The generator can only be used once. After it is used, it will be cleaned automatically []
The map function returns a generator, which can be traversed like a list, but we can't see the elements as intuitively as a list, unless we use list() to turn the generator into a list (in fact, we don't need to turn the generator into a list). Please note that the generator is different from the iterator, or the generator is a special iterator, which can only be traversed once. After traversal, it will disappear automatically. Iterators can iterate. For example, the range() function returns the iterator:
>>> a = range(5) >>> list(a) [0, 1, 2, 3, 4] >>> list(a) [0, 1, 2, 3, 4]
With generators and iterators, we have to get back to the original topic. Using the map mapping function, we get the number of occurrences of each element, and need to form tuples with the corresponding elements one by one. In this case, the zip () function is used. The zip() function creates a generator to aggregate the elements of each iteratable object (iterator, generator, list, tuple, set, string, etc.). The elements are aggregated according to the same subscript. If the length is different, the elements greater than the shortest iteration object length are ignored.
>>> m = map(arr.count, unique) >>> z = zip(unique, m) >>> z <zip object at 0x0000020A2D490508> >>> list(z) [(1, 1), (2, 2), (3, 3), (4, 1), (5, 1), (7, 1), (8, 2), (9, 1)] >>> list(z) []
Obviously, the zip() function also returns a generator, which can only be used once and then disappears.
Step 3, find those numbers whose occurrences are equal to the number itself
With each element and its number of occurrences, we just need to loop through... No, please wait a minute. Why do we have to loop? We just need to filter every element and find out those tuples whose occurrence times are equal to the element itself. Why not try the filter() function?
>>> def func(x): # Parameter x is a tuple type if x[0] == x[1]: return x >>> m = map(arr.count, unique) >>> z = zip(unique, m) >>> f = filter(func, z) >>> f <filter object at 0x0000020A2D1DD908> >>> list(f) [(1, 1), (2, 2), (3, 3)] >>> list(f) []
The filter function filter() accepts two parameters. The first parameter is a function to judge whether an element meets the filter conditions. The second parameter is the iterative object to be filtered. The filter() function also returns a generator, which can only be used once and then disappears.
Writing here, we're almost done. However, as a aspiring programmer, can you tolerate func() as a strange looking function? The answer is no! You will replace it with lambda function. In addition, maybe we need to sort the results according to the size of the elements. With sorting, the complete code is as follows:
>>> arr = [3,5,2,7,3,8,1,2,4,8,9,3] >>> unique = set(arr) >>> m = map(arr.count, unique) >>> z = zip(unique, m) >>> f = filter(lambda x:x[0]==x[1], z) >>> s = sorted(f, key=lambda x:x[0]) >>> print('The lucky numbers are:', [item[0] for item in s]) The lucky numbers are:[1, 2, 3]
The ultimate code, one line
If you've ever had a painful experience of being ravaged by code that looks like a heavenly book and can realize complex functions in one line, now you can also write the above code in one line to ravage others.
>>> arr = [3,5,2,7,3,8,1,2,4,8,9,3] >>> print('The lucky numbers are:', [item[0] for item in sorted(filter(lambda x:x[0]==x[1], zip(set(arr), map(arr.count, set(arr)))), key=lambda x:x[0])]) The lucky numbers are:[1, 2, 3]
Dramatic reversal, I really understand Python this time!
Some people say, why bother so much? Isn't it simpler and easier to read? Sure enough, I really think too much!
>>> arr = [3,5,2,7,3,8,1,2,4,8,9,3] >>> [x for x in set(arr) if x == arr.count(x)]