[CS61A]Lecture #11: Sequences (II) and Data Abstraction

仅用作个人学习记录
Reference:https://inst.eecs.berkeley.edu/~cs61a

Multiple Variables

x, y = (1, 9)
x, y = (2, 2)
...

在 Python 中,我们常见到上面这种写法,此时等号右边一定是某种序列,且序列的元素个数与左边相等。就像我们期望的那样,序列的第一个值赋给 x,第二个值赋给 y。

>>> L = [ (1, 9), (2, 2), (5, 6), (3, 3) ]
>>> same = 0
>>> for x, y in L:
... 	if x == y:
... 		same += 1
>>> same
2

Two Iterations at Once

zip是 Python 内置的函数,它将两个列表压缩在一起,返回一个generator,这个会在后续讲到。

zip 函数可以组合多个序列:

>>> list(zip([1, 2, 5, 3], [9, 2, 6, 3, 10]))
[(1, 9), (2, 2), (5, 6), (3, 3)]
>>> # Length of result is that of shortest sequence
>>> list(zip([1, 4, 7, 10], [2, 5, 8, 11], [3, 6, 9, 12, 15]))
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12)]

如果传递给 zip 的两个序列的长度不一致,会以最小长度为准。

>>> beasts = ["aardvark", "axolotl", "gnu", "hartebeest"]
>>> for n, animal in zip(range(1, 5), beasts):
... 	print(n, animal)
1 aardvark
2 axolotl
3 gnu
4 hartebeest

Modifying Lists

>>> L = [1, 2, 3, 4, 5]
>>> L[2] = 6
>>> L
[1, 2, 6, 4, 5]
>>> L[1:3] = [9, 8]
>>> L
[1, 9, 8, 4, 5]
>>> L[2:4] = [] # Deleting elements
>>> L
[1, 9, 5]
>>> L[1:1] = [2, 3, 4, 5] # Inserting elements
>>> L
[1, 2, 3, 4, 5, 9, 5]
>>> L[len(L):] = [10, 11] # Appending
>>> L
[1, 2, 3, 4, 5, 9, 5, 10, 11]
>>> L[0:0] = range(-3, 0) # Prepending
>>> L
[-3, -2, -1, 1, 2, 3, 4, 5, 9, 5, 10, 11]

当然上述这些操作可以用内置的方法完成,但那涉及到面向对象的思想,会在后续介绍。

List Comprehensions

[ <map expression> for <var> in <sequence expression> if <filter expression> ]

更短的版本,选择不过滤:

[ <map expression> for <var> in <sequence expression>

遍历sequence,如果该元素使得filter expression为真,则执行map expression并将结果按序放入要返回的列表中。

执行过程:

  • 在当前环境下创建一个新的局部环境运行List Comprehensions
  • 创建一个空list用来存放map expression的值。
  • 遍历sequence,若使得filter expression为真,计算map expression,并将其放入list。如果没有if内容直接执行map expression
  • 返回list
>>> [ (a, b) for a in range(10, 13) for b in range(2) ]
[(10, 0), (10, 1), (11, 0), (11, 1), (12, 0), (12, 1)]
>>> [(a, b) for a in range(4) for b in range(a, 4)]
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]

通过上面两个例子,可以发现嵌套for语句的执行顺序就如同C语言中的嵌套循环。

>>> [0 for i in range(5)]
[0, 0, 0, 0, 0]
>>> [0 for _ in range(5)]
[0, 0, 0, 0, 0]

常见的惯例是,将单下划线字符用于for头部,如果这个名称在语句组中不会使用。要注意对解释器来说,下划线只是另一个名称,但是在程序员看来中具有固定含义,它表明这个名称不应出现在任何表达式中。

Exercise I

def matches(a, b):
    """Return the number of values k such that A[k] == B[k].
    >>> matches([1, 2, 3, 4, 5], [3, 2, 3, 0, 5])
    3
    >>> matches("abdomens", "indolence")
    4
    >>> matches("abcd", "dcba")
    0
    >>> matches("abcde", "edcba")
    1
    """
    #return sum([1 for i in range(len(a)) if a[i] == b[i]]) # error when len(a) != len(b)
	return sum([1 for x, y in zip(a, b) if x == y])

Exercise II

def triangle(n):
    """Assuming N >= 0, return the list consisting of N lists:
    [1], [1, 2], [1, 2, 3], ... [1, 2, ... N].
    >>> triangle(0)
    []
    >>> triangle(1)
    [[1]]
    >>> triangle(5)
    [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]
    """
    return [list(range(1, i + 1)) for i in range(1, n + 1)]

Data Abstraction

数据抽象允许我们像操作基本数据单元那样操作复合值。

数据抽象是一种方法论,使我们将复合数据对象的使用细节与它的构造方式隔离。

抽象数据类型 (ADT)(如上一讲中的对 pair 抽象)表示某种事物及其上的操作。

对于每种类型,我们定义一个接口,为该类型数据的用户描述可用的操作(Application Programmer’s Interface–API)。通常,接口由函数组成。这些函数的规范(句法和语义)的集合构成了类型的规范。

其中一些功能属于常见类别:

  • Constructors:创建一个新的类型实例

  • Accesors:返回类型实例的属性

  • Mutators:修改类型实例

Rational Numbers

def make_rat(n, d):
    """The rational number N/D, assuming N, D are integers, D!=0"""
    g = gcd(n, d)
    n //= g; d //= g
    return (n, d)

def numer(r):
    """The numerator of rational number R in lowest terms."""
    return r[0]

def denom(r):
    """The denominator of rational number R in lowest terms.
    Always positive."""
    return r[1]


def add_rat(x, y):
    return make_rat(numer(x) * denom(y) + numer(y) * denom(x),
                    denom(x) * denom(y))

def mul_rat(x, y):
    return make_rat(numer(x) * numer(y), denom(x) * denom(y))

def str_rat(r):  # (For fun: a little new Python string magic)
    return str(numer(r)) if denom(r) == 1 else f"{numer(r)}/{denom(r)}" 

def equal_rat(x, y):
    return numer(x) * denom(y) == numer(y) * denom(x)


def exact_harmonic_number(n):
    """Return 1 + 1/2 + 1/3 + ... + 1/N as a rational number.
    >>> str_rat(exact_harmonic_number(1))
    '1'
    >>> str_rat(exact_harmonic_number(3))
    '11/6'
    >>> str_rat(exact_harmonic_number(10))
    '7381/2520'
    """
    s = make_rat(0, 1)
    for k in range(1, n + 1):
        s = add_rat(s, make_rat(1, k))
    return s

Layers of Abstraction

在这里插入图片描述

这些黑线代表抽象壁垒。使用有理数的程序仅仅通过算术函数来操作它们:add_ratmul_rateq_rat。相应地,这些函数仅仅由构造器和选择器make_ratnumerand denom来实现,它们本身由元组实现。元组如何实现的字节和其它层级没有关系,只要元组支持选择器和构造器的实现。

壁垒上层不会使用到任何壁垒下层的东西。壁垒下层仅仅使用壁垒上层提供的操作而对如何实现的并不关心。

抽象壁垒使程序更易于维护和修改。当壁垒上层需要改动,只要不改变接口,则壁垒下层就无需改动。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值