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

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

本章中的编程思路总结:
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(curIndex0
        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(cur1
        r = int(dsc[cur])
        cur+=1
        line = []
        while(curand 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

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

第一个问题是后面两个问题的基础。我的计算方法用一张图表示如下:
《编程珠玑》习题练习In Python——第三章 数据决定程序结构_第1张图片
这里要计算2017年某天到2020年某天的天数差X。只需要分别计算两个时间点在该年中的天数A,B,以及两个时间点年份区间的天数和Y。X = Y+B-A。需要判断两个天数的先后。程序分为几个部分,首先是对时间字符串的解释程序:

def InterpDate(ds):# 解释日期字符串
    date = [0,0,0] # 年月日
    dInd = 0
    sInd = 0
    while(sInd1
        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 0
def 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: 12
Sun 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(curif 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 \ !

你可能感兴趣的:(算法,算法)