Python 內建的 turtle
模組,提供了海龜繪圖的實作,例如,以下這個簡單的程式,可以畫出星狀圖:
from turtle import *
leng = 200
a = 170
n = 37
speed(0)
for _ in range(n):
forward(leng) # 前進
left(a) # 轉向(角度)
done() # 繪圖完成(不關閉視窗)
執行結果如下:
這又跟 NumPy 有什麼關係呢?使用 NumPy 其實是種重新看待需求的過程,將一切都看成是資料的處理,使用 NumPy 來實現海龜繪圖,可以很好地示範出這個看待需求的過程。
首先,試著用 Matplotlib 來製出相同的圖案吧!這只要線圖就可以了,當然,你需要提供線的每個座標點,這可以使用 turtle
模組的 pos
函式,用來取得海龜的位置:
from turtle import *
import matplotlib.pyplot as plt
leng = 200
a = 170
n = 37
speed(0)
coords = []
for _ in range(n):
coords.append(pos())
forward(leng)
left(a)
x = [coord[0] for coord in coords]
y = [coord[1] for coord in coords]
plt.gca().set_aspect(1)
plt.plot(x, y)
plt.show()
程式中使用 coords
收集了海龜繪圖迭代過程的座標點,這可以畫出以下的圖案:
接著來用 NumPy 實現,基本出發點就是,可以不用迴圈就不用,不使用迴圈的意義其實在於,儘量透過 Universal 函式來處理資料,為了能透過 Universal 函式,你得有一筆資料,這該是什麼資料呢?
觀察上面的程式,你迭代了 n
次,每次的迭代會取得一次海龜座標,也就是說,你是將 0 到 n - 1
轉換為海龜座標,因此一開始的資料就只是:
step = np.arange(n)
有了初始的資料,接下來是將 0 到 n - 1
轉換為海龜座標:
def forward_left(_, leng, a):
forward(leng)
left(a)
return np.array(pos())
forward_left = np.frompyfunc(forward_left, 3, 1)
coord = np.array(forward_left(step, leng, a).tolist())
x = np_pos[:,0]
y = np_pos[:,1]
forward_left
取 step
中的每個元素,將之轉換為 pos
取得的座標,最後得到的結果,重組為 NumPy 陣列,這是為了後續可以透過索引範圍取出 x
與 y
,將以上片段組合起來:
from turtle import *
import numpy as np
import matplotlib.pyplot as plt
def forward_left(_, leng, a):
forward(leng)
left(a)
return np.array(pos())
forward_left = np.frompyfunc(forward_left, 3, 1)
leng = 200
a = 170
n = 37
speed(0)
step = np.arange(n)
coord = forward_left(step, leng, a)
np_pos = np.array(coord.tolist())
x = np_pos[:,0]
y = np_pos[:,1]
plt.gca().set_aspect(1)
plt.plot(x, y)
plt.show()
執行結果與上一張圖是相同的,如果 forward_left
不是你實作的,你只要看成是個黑箱就可以了,當它是個傳入引數就傳回值的函式就可以了。
只不過,forward_left
中使用了 forward
、left
、pos
,其實是有點作弊的方式,前兩者是有副作用的函式,改變了內部海龜的狀態,而且實際上也沒用到 step
每次傳入的元素值,這些看來都…很不 NumPy!
很不 NumPy 的意思是,forward
、left
、pos
看來只是指令,不像是在做資料處理與轉換,如果想更 NumPy 一些,就得知道海龜繪圖的原理,進一步將原理看待為資料處理的過程,這下篇文件再來談…