【練習題】第四章--互動設計(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-3步驟,一直到你有了一系列能工作的函式為止。把函式複製粘貼出來,避免重複輸入或者修改了。
-
看看是不是有通過重構來改進函式的可能。比如,假設你在一些地方看到了相似的程式碼,就可以把這部分程式碼做成一個函式。
這個模式有一些缺點,我們後續會看到一些替代的方式,但這個模式是很有用的,尤其對耐餓實現不值得怎麼去把程式分成多個函式的情況。
除錯:
一個互動介面,就像是函式和呼叫者的一箇中間人。呼叫者提供特定的引數,函式完成特定的任務。
例如,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