Berkeley's SICP in python3(四)

上面一篇文章写了前两小结的内容和程序,这篇文章继续第三第四小结。

第二章 构建对象抽象

3序列

序列是有序数据值的集合。

3嵌套组

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

递归列表

>>> (1, (2, (3, (4, None))))
(1, (2, (3, (4, None))))

对于递归列表来说,非为两部分:1:第一个元素 2:其余元素(同时也是列表)

empty_rlist = None
def make_rlist(first, rest):
    """
    make a recursive list from its first element and the rest
    """
    return (first, rest)
def  first(s):
    """
    Return the first element of recursive list s
    """
    return s[0]
def rest():
    """
    Return the rest of the element of a recursive list s.
    """
    return s[1]

这里用了一个构造器,两个选择器和一个常量实现的递归列表的抽象数据类型。

counts = make_rlist(1,make_rlist(2, make_rlist(3, empty_rlist)))

print(first(counts))  #1
print(rest(counts))   #(2, (3, None))

用我们定义的抽象数据类型,可以实现序列的两种行为,求长度和根据下标求元素。

def len_rlist(s):
    """
    Return the length of recursive list s.
    """
    length = 0
    while empty_rlist != s:
        s, length = rest(s), length + 1
    return length
def getitem_rlist(s, i):
    """
    Return the element at index i of recursive list s.
    """
    while i > 0:
        s, i = rest(s), i - 1
    return first(s)

print(len_rlist(counts))    #3
print(getitem_rlist(counts ,1))  #2

这样定义的各种方法在计算时会有大量的计算开销,在后续会学习到以较小的时间开销来实现这两个计算。

元组2

内置元组也具有求长度和元素选择两个功能,表现出了序列的两个主要的行为抽象。

>>> digits = (1, 8, 2, 8)
>>> len(digits)
4
>>> digits[3]
8

元组也可以进行加和乘运算。

>>> (2, 7) + digits * 2
(2, 7, 1, 8, 2, 8, 1, 8, 2, 8)。

映射:一种将一个序列转换成另一个序列的强大的方法:对每个元素,用一个函数计算并收集结果。
这个计算的一般形式称为映射函数序列。可以用内置函数map。此函数返回一个对象而不是一个序列,但是可以用过调用tuple将其转换成序列。

>>> alternates = (-1, 2, -3, 4, -5)
>>> tuple(map(abs, alternates))
(1, 2, 3, 4, 5)
>>> type(map(abs, alternates))
<class 'map'>

序列遍历

此处讲到了while和for两种对序列进行遍历的方法。
同时讲到了序列拆封。

>>> pairs = ((1, 2), (2, 2), (2, 3), (4, 4))
>>> num = 0
>>> for x, y in pairs:
    if x == y:
        num = num + 1   
>>> num
2

range()函数:内置类型,range(1,10) #include 1,but not 10

序列抽象

上面已经介绍过tuple和range两种内置数据类型。
in(not in) 可以判断一个元素是否在上面类型中。
切片:序列还包含有更小的序列。

t = (2, 5, 6, 7, 3, 8)
t1 = t[2:4]  #6,7
t2 = t[2:] #6, 7, 3, 8
t3 = t[:3] #2, 5, 6

3.6字符串

熟记比较常用的内置函数:

hobby = 'basketball'
print( len(hobby) )  #10
print( hobby[2] )        #s
print( ('play ' + hobby) * 2 )
print( 'lay' in 'play basketball' )
print( hobby.count('b') )
print( hobby.count('ball') )

比较常用的就是用str()函数,可以将数字转换程字符串。

传统接口

filter和map都是对序列操作:
map():两个参数,序列和函数,将函数应用到每一个序列元素值并返回结果。
filter():两个参数,序列和布尔函数,将函数应用到每一个序列元素值并返回结果是ture的值。

def fib(k):
    """
    Compute thee kth Fibon number
    """
    prev, cour = 1, 0 #curr is first Fibonacci number
    for _ in range(k - 1):
        prev, curr = curr, curr + prev
    return curr

def iseven(n):
    return n % 2 == 0

nums = (5, 6, -7, -8, 9)
print( tuple(filter(iseven, nums)) )   #(6, -8)
print( sum(map(abs, nums)) )   #35

生成器表达式:
<map expression> for <name> in <sequence expression> if <filter expression>

reduce函数:累积值

from operator import mul,add
from functools import reduce
print( reduce(add, (2,4,6)) )

4.可变数据

目前我们学习的内置类型都是numbers, Booleans,tuples,ranges,strings,都是不变的对象。设计模块化程序的一个技巧能随时间变化的数据类型。

4.1局部变量

关于局部变量和全局变量的区别不多数,程序示例如下:

def make_withdraw(balance):
    def withdraw(amount):
        nonlocal balance
        if amount > balance:
            return 'INsufficient funds'
        balance = balance - amount
        return balance
    return withdraw

wd = make_withdraw(20)
print( wd(5) )  #15
print( wd(5) )  #10

如上图所示,尽管调用wd(),参数也一致,得到的结果却不同。

4.2非局部赋值的好处

关于函数make——withdraw(),我们可以再次调用。

wd = make_withdraw(20)
print( wd(5) )  #15
print( wd(3) )  #12

wd = make_withdraw(10)
print( wd(5) )  #5
print( wd(3) )  #2

可以看到,尽管balance是全局变量,但各函数之间并不能访问balance变量。从高层次来看,构建出了一个随历史记录变化的程序。

4.3非局部赋值的开销

尽管有并发,但这种赋值是构建模块化程序一个强有力的工具。
程序的不同部分有自己的执行环境,在项目中可以单独执行。不仅如此,利用这个功能, 我们可以实现可变的数据类型。

4.4 Lists(列表)

lists是python中非常强大的一种内置数据类型。和元组类似,区别在于list是一种可变数据类型。
常用的操作如下:

>>> chinese_suits = ['coin', 'string', 'myraid']
>>> suits = chinese_suits
>>> suits.pop()
'myraid'
>>> suits.remove('string')
>>> suits
['coin']
>>> suits.append('cup')
>>> suits.extend(['sword', 'club'])
>>> suits[2] = 'spade'
>>> suits
['coin', 'cup', 'spade', 'club']
>>> suits[0:2] = ['heart', 'diamond']
>>> suits
['heart', 'diamond', 'spade', 'club']
>>> 

上述操作是最基本的操作,此外,还有插入,排序,翻转等可变操作。值得注意的是,这些操作只是改变了list值,而不是新创造了一个list。
因为没有创造新的list,因此改变suits的值,

>>> chinese_suits
['heart', 'diamond', 'spade', 'club']
>>> nest = list(suits)
>>> nest
['heart', 'diamond', 'spade', 'club']
>>> nest[0] = suits
>>> suits.insert(2, 'Joker')
>>> nest
[['heart', 'diamond', 'Joker', 'spade', 'club'], 'diamond', 'spade', 'club']
>>> nest[0].pop(2)
'Joker'
>>> suits
['heart', 'diamond', 'spade', 'club']

可以看到,对nest,suits的操作都相互影响到了彼此。引申出了两种操作,is==。前者要求相等且当任一值内元素改变时,另一个也改变。后者只需对应元素相等。前者(identity)的 要求比后者(equality)高。

>>> suits is nest[0]
True

>>> suits is ['heart','diamond','spade','club']
False

>>> suits == ['heart','diamond','spade','club']
True

4.5 字典

字典包含了键值对,键和值多时对象。字典的目的是提供一种存储和查找返回值的抽象方法,其索引并不是有连续整数来完成,而是靠键。
字典的每一个键只有一个值与之对应。添加新的键值对和改变值都可以通过赋值语句实现。

>>> numerals = {'I': 1.0, 'V': 5, 'X': 10}
>>> numerals['X']
10
>>> numerals['I'] = 1
>>> numerals['L'] = 50
>>> numerals
{'I': 1, 'L': 50, 'X': 10, 'V': 5}

需要特别注意的,对于添加的L值,并不是在字典的最后面。字典是一种无序的键值对集合,输出会按照一定的顺序输出,但是这个顺序用户是不知道的。
字典抽象也提供很多方法来获得其键,值。

>>> numerals.keys()
dict_keys(['I', 'L', 'X', 'V'])

>>> numerals.values()
dict_values([1, 50, 10, 5])

>>> numerals.items()
dict_items([('I', 1), ('L', 50), ('X', 10), ('V', 5)])

dict构造器也可以将接受一些键值对来构造字典。

>>> dict([(3, 9), (4, 16), (5, 25)])
{3: 9, 4: 16, 5: 25}

关于字典有两点限制:键不能是可变的内置类型,每个键有且只有唯一的值与之对应。
词典还有一个get()方法,返回对应键的值。若若键存在,返回值或缺省值。参数是键和缺省值。

>>> numerals.get('B',50)
50
>>> numerals
{'I': 1, 'L': 50, 'X': 10, 'V': 5}
>>> numerals.get('L',0)
50

字典也可以用表达式来生成。

>>> {x:x*x for x in range(3, 6)}
{3: 9, 4: 16, 5: 25}

实现:也可以用目前已知道的内置类型tuple来实现字典这种数据类型。
程序如下:

def make_dict():
    """
    Return a function implementation of a dictionary
    """
    records = []
    def getitem(key):
        for k, v in records:
            if k == key:
                return v
    def setitem(key, value):
        for item in records:
            if item[0] == key:
                item[1] = value
                return
        records.append([key, value])
    def diapatch(message, key = None, value=None):
        if message == 'getitem':
            return getitem(key)
        elif message == 'setitem':
            setitem(key, value)
        elif message == 'keys':
            return tuple(k for k, _ in records)
        elif message == 'value':
            return tuple(v for _, v in records)
    return diapatch
>>> d = make_dict()
>>> d('setitem', 3 ,9)
>>> d('setitem', 4, 16)
>>> d('getitem',3)
9
>>> d('keys')
(3, 4)
>>> d('value')
(9, 16)

目前已经学习了非常重要的python内置类型,tuple,list,string,dict。并且知道怎么利用现有的知识去实现它,实现了抽象数据类型。

4.6一个例子

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值