【练习题】第四章--交互设计(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

猜你喜欢

转载自blog.csdn.net/qq_29567851/article/details/82997457