1. 程式人生 > >【練習題】第四章--互動設計(Think Python)

【練習題】第四章--互動設計(Think Python)

1.寫一個函式叫做square(譯者注:就是正方形的意思),有一個名叫t的引數,這個t是一個turtle。用這個turtle來畫一個正方形。寫一個函式呼叫,把bob作為引數傳遞給square,然後再執行這個程式。

code:

import turtle

def square(t):
    for i in range(4):
        t.fd(100)
        t.lt(90)
        
bob = turtle.Turtle()
square(bob)
print(bob)
turtle.mainloop()

answer:

作用:學會將經常呼叫的動作變成一個函式,方便呼叫且看起來簡潔。即封裝

2.給這個square函式再加一個引數,叫做length(譯者注:長度)。把函式體修改一下,讓長度length賦值給各個邊的長度,然後修改一下呼叫函式的程式碼,再提供一個這個對應長度的引數。再次執行一下,用一系列不同的長度值來測試一下你的程式。

code:

import turtle

def square(t,length):
    for i in range(4):
        t.fd(length)
        t.lt(90)
        
bob = turtle.Turtle()
square(bob,200)
print(bob)
turtle.mainloop()

作用:more flexible。即泛化

3.複製一下square這個函式,把名字改成polygon(譯者注:意思為多邊形)。另外新增一個引數叫做n,然後修改函式體,讓函式實現畫一個正n邊的多邊形。提示:正n多邊形的外角為360/n度。

code:

import turtle

def polygon(t,n,length):
    for i in range(n):
        t.fd(length)
        t.lt(360/n)
        
bob = turtle.Turtle()
polygon(bob,5,200)
print(bob)
turtle.mainloop()

作用:more more flexible。更泛化

4.在寫一個叫做circle(譯者注:圓)的函式,也用一個turtle類的物件t,以及一個半徑r,作為引數,畫一個近似的圓,通過呼叫polygon函式來近似實現,用適當的邊長和邊數。用不同的半徑值來測試一下你的函式。

code:

import turtle
import math

def polygon(t,n,length):
    for i in range(n):
        t.fd(length)
        t.lt(360/n)
        
def circle(t,r,n):
    C=2*math.pi*r
    length=C/n
    polygon(t,n,length)
bob = turtle.Turtle()
circle(bob,100,300)//circle(Tirtle,圓的半徑,擬合用的多邊形邊數)
print(bob)
turtle.mainloop()

作用:呼叫函式來近似目標

5.在circle基礎上做一個叫做arc的函式,在circle的基礎上新增一個angle(譯者注:角度)變數,用這個角度值來確定畫多大的一個圓弧。用度做單位,當angle等於360度的時候,arc函式就應當畫出一個整團了。

code:

import turtle
import math
def polygon_arc(t,n,length,angle):
    for i in range(int(n*angle/360)):
        t.fd(length)
        t.lt(360/n)
  
def arc(t,r,angle,n):
    C=2*math.pi*r
    length=C/n
    polygon_arc(t,n,length,angle)
    
bob = turtle.Turtle()
arc(bob,100,180,300)
print(bob)
turtle.mainloop()

answer:

import turtle
import math

def polyline(t, n, length, angle):     
    for i in range(n):         
        t.fd(length)         
        t.lt(angle)
def polygon(t, n, length):     
    angle = 360.0 / n     
    polyline(t, n, length, angle)  
def arc(t, r, angle):     
    arc_length = 2 * math.pi * r * angle / 360     
    n = int(arc_length / 3) + 1     
    step_length = arc_length / n     
    step_angle = float(angle) / n     
    polyline(t, n, step_length, step_angle)
def circle(t, r):     
    arc(t, r, 360)
    
bob = turtle.Turtle()
arc(bob,100,180)
print(bob)
turtle.mainloop()

作用:這個過程中,改進了介面設計,增強了程式碼再利用,這就叫做重構。其中:用多段線polyline來重寫多邊形polygon和圓弧arc;用圓弧arc來重寫circle的實現。

開發計劃:

開發計劃是寫程式的一系列過程。我們本章所用的就是『封裝-泛化』的模式。這一過程的步驟如下:

  1. 開始寫一個特別小的程式,沒有函式定義。

  2. 一旦有你的程式能用了,確定一下實現功能的這部分有練習的語句,封裝成函式,並命名一下。

  3. 通過逐步給這個函式增加引數的方式來泛化。

  4. 重複1-3步驟,一直到你有了一系列能工作的函式為止。把函式複製粘貼出來,避免重複輸入或者修改了。

  5. 看看是不是有通過重構來改進函式的可能。比如,假設你在一些地方看到了相似的程式碼,就可以把這部分程式碼做成一個函式。

這個模式有一些缺點,我們後續會看到一些替代的方式,但這個模式是很有用的,尤其對耐餓實現不值得怎麼去把程式分成多個函式的情況。

除錯:

一個互動介面,就像是函式和呼叫者的一箇中間人。呼叫者提供特定的引數,函式完成特定的任務。

例如,polyline這個多段線函式,需要四個實際引數:t必須是一個Turtle小烏龜;n(邊數)必須是一個整形;length(長度)應該是一個正數;angle(角度)必須是一個以度為單位的角度值。

這些要求叫做『前置條件』,因為要在函式開始執行之前就要實現才行。相應的在函式的結尾那裡的條件叫『後置條件』。後置條件包含函式的預期效果(如畫線段)和其他作用(如移動海龜或進行其他改動)。

前置條件是準備給函式呼叫者的。如果呼叫者違背了(妥當標註的)前置條件,然後函式不能正常工作,這個bug就會反饋在函式呼叫者上,而不是函式本身。

如果前置條件得到了滿足,而後置條件未能滿足,這個bug就是函式的了。所以如果你的前後置條件都弄清晰,對除錯很有幫助。

程式碼:http://greenteapress.com/thinkpython2/code/polygon.py

練習1:

2.4.7小節中的那個版本的arc函式並不太精確,因為對圓進行線性逼近總會超過真實情況。結果就是小烏龜總會距離正確位置偏離一些畫素。我的樣例給出了一種降低這種誤差程度的方法。閱讀一下程式碼,看你能不能理解。如果你畫一個圖示,也許就能明白程式碼是怎麼工作的了。

    # making a slight left turn before starting reduces
    # the error caused by the linear approximation of the arc
    t.lt(step_angle/2)
    polyline(t, n, step_length, step_angle)
    t.rt(step_angle/2)

正是以上這一段,我覺得應該是因為起始方向的提前左轉多邊形外角的一半,使得所擬合的多邊形長度變短了。而造成這樣的原因可能因為如圖:

 1為原來程式碼的起始線段,2為改進程式碼的其實線段,可見改進後多邊形的擬合更加“靠內”了,因為圓是靠多邊形的頂點近似的,所以會導致頂點距離圓心的距離更小了,如此便可以降低誤差。

練習2 3 4 略

練習5:

Wiki百科看一下螺旋線的相關內容;然後寫個程式來畫阿基米德曲線(曲線中的一種)。

即r=a+b*θ。

code:

from __future__ import print_function, division

import turtle


def draw_spiral(t, n, length=3, a=0.1, b=0.0002):
    """Draws an Archimedian spiral starting at the origin.

    Args:
      n: how many line segments to draw
      length: how long each segment is
      a: how loose the initial spiral starts out (larger is looser)
      b: how loosly coiled the spiral is (larger is looser)

    http://en.wikipedia.org/wiki/Spiral
    """
    theta = 0.0

    for i in range(n):
        t.fd(length)
        dtheta = 1 / (a + b * theta)

        t.lt(dtheta)
        theta += dtheta


# create the world and bob
bob = turtle.Turtle()
draw_spiral(bob, n=1000)

turtle.mainloop()

重點說明:

for i in range(n):

t.fd(length)

dtheta = 1 / (a + b * theta)//這個是曲率ρ=1/r,而曲率意義是針對曲線上某個點的切線方向弧長轉動率,通過微分來定義,表明曲線偏離直線的程度。所以用來表示轉過角度。

t.lt(dtheta)

theta += dtheta