《编程珠玑》习题练习In Python——第三章 数据决定程序结构

来源:互联网 发布:淘宝优惠券图片头像 编辑:程序博客网 时间:2024/06/05 00:53

本章的主旨是:数据视图决定了程序结构。简单的数据表示可以使用简单高效的程序来处理。好的数据表示是好的程序的前提。正如好的食物原料是做出美味食物的前提。

本章中的编程思路总结:
1、尽量使用数组处理重复数据。
2、定义简单的表示语言和一个语言的解释器来表示复杂的数据结构。

习题1

税收计算。准确说是超额累进税率计算。使用两个数组分别定义不同等级税收的区间,和该区间的税率。一个循环便可解决问题。最后一个区间的上限应该设置为无穷,Python可以使用float('inf')来表示。
代码如下:

def test_1():    tax_strt = [2000,3000,4000,6000,float('inf')]    tax_rate = [0.14,0.17,0.24,0.40]    income = 20000    tax = 0    for i in range(len(tax_strt)-1):        if income < tax_strt[i]:            break        tax += (min(income-tax_strt[i],tax_strt[i+1]-tax_strt[i]))*tax_rate[i]    print(tax)

习题2

k阶常系数线性递归定义的级数如下:

an=c1an1+c2an2+...+ckank+ck+1

其中c为常数。编写一个程序,输入为k,a1,a2,...ak,c1,c2...ck+1m,输出为a1am

著名的斐波那契数列其实就是这个级数在k=2的时候的一种特殊形式。斐波那契数列使用递归法计算时间复杂度是指数级别的。具体证明要用到母函数的方法。总之,递归法不能解决这个问题,那么可以从序号更小的元素开始依次计算,每次计算出的值存储在数组中。每计算一个元素,只需要用到比它小的k个元素,因此更进一步,可以只保存这k个数。实现代码:

def test_2():    k = 5    m = 6    c = list(range(1,k+2))    a = list(range(1,k+1))    curIndex = k    cur = 0    while(curIndex<m):        cur = 0        for i in range(0,k):            cur += c[i]*a[(curIndex+i)%k]        cur += c[-1]        a[curIndex%k] = cur        curIndex +=1    print(cur)

习题3

编写一个函数,输入为一个大写字母,输出为一个数组,该数组用图形化的形式表示该字母。

非常清楚记得大一学C++就有做过这种题目。要求输入一个菱形或者三角形。当时整个程序写了大量的输出代码,根据每行输出有规律的变化可以使用一些循环简化代码。这样做的问题在于,将数据内容编码在了程序之中。也就是说,程序只能输出菱形,如果要输出三角形,整个代码都需要修改。

这个题更进一步,要根据输入来输出不同的图形。现在就不可能为每个字母写一个子程序了。根据数据和程序分离的思想。应该将图形表示为一种简单的数据形式,将这种数据输入一个绘制程序应该要可以获得不同的图形。这就是定义一种描述语言同时实现该语言的解释器的编程方法。

首先定义一种描述图形的语言,结构如:r3s3#3s3。有三种特殊符号,r代表行重复,s代表空格,空格忽略,其他符号则代表该符号本身。除了r之外,符号后面接数字代表该符号在一行中重复的次数,r符号则表示该行重复的次数。数字均是一位数。上面的语句输出为:

   ###      ###      ###   

那么如字母I可以表示为:l2#6 l4s2#2s2 l2#6,输出为:

############  ##    ##    ##    ##  ############

实现这个语言的解释器:

def InterpretLetter(dsc):    cur = 0    pic = []    while(cur<len(dsc)):        cur+=1        r = int(dsc[cur])        cur+=1        line = []        while(cur<len(dsc) and dsc[cur]!='l'):            char = dsc[cur]            if char ==' ':                cur+=1                continue            elif char =='s':                char = ' '            cur +=1            time = int(dsc[cur])            line += [char]*time            cur +=1        for i in range(r):            pic.append(line)    return pic

测试程序:

def test_3():    dic ={        'C': "l2#6 l4#2 l2#6",        'E':"l2#7 l2#2 l2#7 l2#2 l2#7",        'H':"l4#2s3#2 l2#7 l4#2s3#2",        "I":"l2#6 l4s2#2s2 l2#6"    }    letter = "I"    pic = InterpretLetter(dic[letter])    for l in pic:        print("".join(l))

这个描述语言的能力有限,描述复杂图形是及其复杂的。但是在绘制字母任务中勉强可以胜任。合理的设计应该在功能和复杂度上取得平衡。

习题4

编写日期处理函数,处理以下问题:给定两个日期,计算两者天数差;给定一个日期,返回星期;给出年月,使用字符数组生成该月日历。

第一个问题是后面两个问题的基础。我的计算方法用一张图表示如下:
这里写图片描述
这里要计算2017年某天到2020年某天的天数差X。只需要分别计算两个时间点在该年中的天数A,B,以及两个时间点年份区间的天数和Y。X = Y+B-A。需要判断两个天数的先后。程序分为几个部分,首先是对时间字符串的解释程序:

def InterpDate(ds):# 解释日期字符串    date = [0,0,0] # 年月日    dInd = 0    sInd = 0    while(sInd<len(ds)):        v = ds[sInd]        sInd += 1        if v == '/':            dInd +=1            if dInd >= len(date):                raise Exception("date error")        else:            v = int(v)            date[dInd] = date[dInd]*10 + v    return date

计算二月的天数:

def GetFebDay(year):      if year%4 ==0 and year % 100 != 0 or year%400 ==0:          return 29      else:          return 28

计算时间是一年中的第几天:

MONTH_DAY = [31,0,31,30,31,30,31,31,30,31,30,31]def DaysPassInYear(date):# 计算当年过去几天    day = 0    day += sum(MONTH_DAY[:(date[1]-1)]) # 过去的月    if date[1]>2:        day += GetFebDay(date[0]) # 本月过去的日    day += date[2]    return day

计算一年总共几天:

def DaysInYear(year):# 计算一年几天    return sum(MONTH_DAY)+GetFebDay(year)

最后计算天数差就很好算了:

def CmpDate(d1,d2): # 比较d1 d2 两个日期    if d1[0] * 10 ** 4 + d1[1] * 10 ** 2 + d1[2] < d2[0] * 10 ** 4 + d2[1] * 10 ** 2 + d2[2]:        return -1    if d1[0] * 10 ** 4 + d1[1] * 10 ** 2 + d1[2] > d2[0] * 10 ** 4 + d2[1] * 10 ** 2 + d2[2]:        return 1    else:        return 0def CalDay(ds1,ds2): # 计算两日期间的天数差。输入字符串     d1,d2 = InterpDate(ds1),InterpDate(ds2)     if CmpDate(d1,d2) < 0: #保证 d1更大         tmp = d1         d1 = d2         d2 = tmp     day1 = DaysPassInYear(d1)     day2 = DaysPassInYear(d2)     dayYear = 0     for y in range(d2[0],d1[0]):         dayYear+=DaysInYear(y)     return dayYear+day1-day2

第二个问题,计算星期。在第一个问题基础上,只要知道某一天的星期,就可以通过天数差来推算其他任意一天的星期了。不过上一问返回非负数,这一问里面需要增加这个时间差的正负。将写这段代码的时间2016年12月3日星期6作为基准,得到代码:

def GetWeek(ds): # 给定日期,计算星期。输入字符串    day = CalDay(ds,STD_DS)    if CmpDate(InterpDate(ds),InterpDate(STD_DS)) < 0:        day = day*-1    return (day+STD_WEEK)%7

第三问实现没有问题,但是还是应该遵循一个原则,尽量使用循环,增加灵活性减少程序复杂度:

def GetCalend(year,month): # 给年月,输出日历     cal =[]     cal.append("Year: "+str(year) +"  Month: " + str(month))     line = []     for w in WEEKS:         line.append(w)         line.append("\t")     cal.append(line)     line =[]     wk = GetWeek(''.join([str(year),'/',str(month),'/1']))     line += ["\t"]*wk     day = 1     md = MONTH_DAY[month-1]     if month ==2:         md = GetFebDay(year)     while(day <= md):         line+= [str(day)]+["\t"]         day+=1         wk = (wk+1)%7         if wk == 0:             cal.append(line)             line = []     if len(line)>0:         cal.append(line)     return cal

2016年12月显示如下:

Year: 2016  Month: 12Sun Mon Tue Wen Thu Fri Sat                 1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  

哈哈,好漂亮。

习题5

给一个单词和后缀列表。匹配该单词的后缀。

我理解的题目意思就是这么简单,答案考虑翻转字符什么我觉得完全没必要。代码:

def test_5():    SUFFIXS = ['et-ic','al-is-tic','s-tic','p-tic']    word = "ppap-tic"    s = None    for suf in SUFFIXS:        strt = len(word)-len(suf)        find = True        for i in range(len(suf)):            if word[strt+i] != suf[i]:                find = False                break        if find:            s = suf            break    print(word,s)

习题6

编写一个格式信件发生器。也就是给定一个文本模板和文本变量,自动将变量填充到模板中。Python中可以直接用format函数实现。不过这里我们要自己实现这个功能。

首先要定义文本模板的格式,使用$加一个序号n表示第n变量。$和序号必须相连。另外定义\为转义字符。代码如下:

def InterpretText(ts,args):    t = []    cur = 0    getArg = False    argNum = 0    escape = False    while(cur<len(ts)):        c = ts[cur]        if getArg:            if ord(c) in range(ord('0'),ord('9')+1):                argNum = argNum*10 + ord(c)-ord('0')            else:                t.append(args[argNum])                t.append(c)                argNum = 0                getArg = False        elif escape:            t.append(c)            escape = False        elif c == '\\':            escape = True        elif c == "$":            getArg = True        else:            t.append(c)        cur+=1    return ''.join(t)

测试代码:

def test_6():    ts = r"Hello \$ $0 \\ !"    text = InterpretText(ts,["World"])    print(text)

输出:

Hello $ World \ !
0 0