伯克利CS 61A的笔记,用的2020 Fall版本
2.3.3 Sequence Processing
2.3.3.1 List Comprehensions
Many sequence processing operations can be expressed by evaluating a fixed expression for each element in a sequence and collecting the resulting values in a result sequence.
In Python, a list comprehension
is an expression that performs such a computation.
格式:[<map expression> for <name> in <sequence expression> if <filter expression>]
>>> odds = [1, 3, 5, 7, 9]
>>> [x+1 for x in odds]
[2, 4, 6, 8, 10]
>>> [x for x in odds if 25 % x == 0]
[1, 5]
To evaluate a list comprehension, Python evaluates the <sequence expression>, which must return an iterable value.
Then, for each element in order, the element value is bound to <name>, the filter expression is evaluated, and if it yields(得到) a true value, the map expression is evaluated.
The values of the map expression are collected into a list.
2.3.3.2 Aggregation
A third common pattern in sequence processing is to aggregate all values in a sequence into a single value. 函数sum, min,max等就是这样的例子。
下面的例子求完全数(perfect number),A perfect number is a positive integer that is equal to the sum of its divisors.
>>> def divisors(n):
return [1] + [x for x in range(2, n) if n % x == 0]
>>> divisors(4)
[1, 2]
>>> divisors(12)
[1, 2, 3, 4, 6]
>>> [n for n in range(1, 1000) if sum(divisors(n)) == n]
[6, 28, 496]
也可以用上面定义的divisors函数来求解这个问题:给定一个矩形的面积,求边长为整数的矩形的最小周长(finding the minimum perimeter of a rectangle with integer side lengths, given its area.)。
>>> def width(area, height):
assert area % height == 0
return area // height
>>> def perimeter(width, height):
return 2 * width + 2 * height
>>> def minimum_perimeter(area):
heights = divisors(area)
perimeters = [perimeter(width(area, h), h) for h in heights]
return min(perimeters)
>>> area = 80
>>> width(area, 5)
16
>>> perimeter(16, 5)
42
>>> perimeter(10, 8)
36
>>> minimum_perimeter(area)
36
>>> [minimum_perimeter(n) for n in range(1, 10)]
[4, 6, 8, 8, 12, 10, 16, 12, 12]
2.3.3.3 Higher-Order Functions
通用的函数模式可以抽象为高阶函数,那么通用的对sequence(序列)的处理方法(函数)也可以抽象为高阶函数(Higher-Order Functions)。
>>> def apply_to_all(map_fn, s):
return [map_fn(x) for x in s]
>>> def keep_if(filter_fn, s):
return [x for x in s if filter_fn(x)]
>>> def reduce(reduce_fn, s, initial):
reduced = initial
for x in s:
reduced = reduce_fn(reduced, x)
return reduced
>>> reduce(mul, [2, 4, 8], 1)
64
用高阶函数找完全数(perfect number)。
>>> def divisors_of(n):
divides_n = lambda x: n % x == 0
return [1] + keep_if(divides_n, range(2, n))
>>> divisors_of(12)
[1, 2, 3, 4, 6]
>>> from operator import add
>>> def sum_of_divisors(n):
return reduce(add, divisors_of(n), 0)
>>> def perfect(n):
return sum_of_divisors(n) == n
>>> keep_if(perfect, range(1, 1000))
[1, 6, 28, 496]
2.3.3.4 Conventional Names
In the computer science community, the more common name for apply_to_all is map and the more common name for keep_if is filter. 名字 map 比 apply_to_all 更常用,filter 比 keep_if 更常用。
In Python, the built-in map and filter are generalizations of these functions that do not return lists. python里面有内置map和filter,而且不返回列表。
The reduce function is built into the functools module of the Python standard library. reduce函数内置在python标准库的functools模块。
>>> apply_to_all = lambda map_fn, s: list(map(map_fn, s))
>>> keep_if = lambda filter_fn, s: list(filter(filter_fn, s))
>>> from functools import reduce
>>> from operator import mul
>>> def product(s):
return reduce(mul, s)
>>> product([1, 2, 3, 4, 5])
120
2.3.4 Sequence Abstraction
切片
>>> digits[0:2]
[1, 8]
>>> digits[1:]
[8, 2, 8]
一些题目
1. Maximum Subsequence
There are two key insights for this problem:
- You need to split into the cases where the ones digit is used and the one where it is not. In the case where it is, we want to reduce
t
since we used one of the digits, and in the case where it isn't we do not. - In the case where we are using the ones digit, you need to put the digit back onto the end, and the way to attach a digit
d
to the end of a numbern
is10 * n + d
.
def max_subseq(n, t):
"""
Return the maximum subsequence of length at most t that can be found in the given number n.
For example, for n = 20125 and t = 3, we have that the subsequences are
2
0
1
2
5
20
21
22
25
01
02
05
12
15
25
201
202
205
212
215
225
012
015
025
125
and of these, the maxumum number is 225, so our answer is 225.
>>> max_subseq(20125, 3)
225
>>> max_subseq(20125, 5)
20125
>>> max_subseq(20125, 6) # note that 20125 == 020125
20125
>>> max_subseq(12345, 3)
345
>>> max_subseq(12345, 0) # 0 is of length 0
0
>>> max_subseq(12345, 1)
5
"""
"*** YOUR CODE HERE ***"
if t == 0 or n == 0:
return 0
else:
use_last = max_subseq(n // 10, t - 1) * 10 + n % 10
not_use_last = max_subseq(n // 10, t)
return max(use_last, not_use_last)