很好的尝试!我将建议一种稍微不同的方法,为您的正方形绘图函数添加x 和y 坐标,并使用t.goto(x, y) 重新定位海龟。这些坐标代表应该绘制正方形的左下角,并且省去了手动移动海龟的麻烦(虽然技术上可行,但不太干净)。
绘制一个正方形后,乌龟将始终面向右侧并准备绘制下一个正方形,因此将移动命令保持在最低限度。剩下的就是找出每个角的原点坐标。
对于右上角,很简单:x + size、y + size。对于左上角,类似:仍然是y + size,但使用x - size_of_smaller_square 将x 轴偏移正确的量。如果你好奇的话,我还包括左下角和右下角。
import turtle as t
def draw_square(depth, size, x=0, y=0, shrink_by=3):
if depth <= 0:
return
t.penup()
t.goto(x, y)
t.color(("blue", "orange")[depth%2])
t.pendown()
for _ in range(4):
t.forward(size)
t.left(90)
smaller_size = size / shrink_by
draw_square(depth - 1, smaller_size, x + size, y + size)
draw_square(depth - 1, smaller_size, x - smaller_size, y + size)
#draw_square(depth - 1, smaller_size, x - smaller_size, y - smaller_size)
#draw_square(depth - 1, smaller_size, x + size, y - smaller_size)
if __name__ == "__main__":
t.speed("fastest")
draw_square(depth=4, size=100)
t.exitonclick()
你提到goto是被禁止的。您可以遵循保证有效的机械策略:在每次递归调用结束时始终将海龟准确地放回其开始的位置(相同的位置和方向)。这尊重递归的自相似结构。每帧的高级方法是:
-
绘制当前框
-
对于每个子框:
- 将海龟移动到正确的位置和方向以绘制子框
- 产生递归调用
- 撤消您刚刚在第 3 步中所做的所有动作
这是该策略的正确但冗长且草率的实施:
import turtle as t
def draw_square(depth, size, shrink_by=3):
if depth <= 0:
return
# draw this box
t.color(("blue", "orange")[depth%2])
t.pendown()
for _ in range(4):
t.forward(size)
t.left(90)
t.penup()
smaller_size = size / shrink_by
# put the turtle in the top-right facing east and spawn a child
t.forward(size)
t.left(90)
t.forward(size)
t.right(90)
draw_square(depth - 1, smaller_size)
# undo the moves
t.right(90)
t.forward(size)
t.left(90)
t.backward(size)
# put the turtle in the top-left facing east and spawn a child
t.left(90)
t.forward(size)
t.right(90)
t.backward(smaller_size)
draw_square(depth - 1, smaller_size)
# undo the moves
t.forward(smaller_size)
t.right(90)
t.forward(size)
t.left(90)
if __name__ == "__main__":
t.speed("fastest")
draw_square(depth=4, size=100)
t.exitonclick()
虽然这可行,但您可以看到可以消除一些多余的运动,同时仍然保留海龟将始终以与递归函数开始时相同的位置和方向结束的特性。重写:
import turtle as t
def draw_square(depth, size, shrink_by=3):
if depth <= 0:
return
# draw this box
t.color(("blue", "orange")[depth%2])
t.pendown()
for _ in range(4):
t.forward(size)
t.left(90)
t.penup()
smaller_size = size / shrink_by
# top-right
t.forward(size)
t.left(90)
t.forward(size)
t.right(90)
draw_square(depth - 1, smaller_size)
# top-left
t.backward(size + smaller_size)
draw_square(depth - 1, smaller_size)
# undo all of the moves to reset the turtle state
t.forward(smaller_size)
t.right(90)
t.forward(size)
t.left(90)
if __name__ == "__main__":
t.speed("fastest")
draw_square(depth=4, size=100)
t.exitonclick()
这可以通过尝试找到模式并将它们变成循环来变得更简洁;例如,如果您在绘制父框的过程中不介意绘制子框,则可以跳过中间动作。此代码绘制了所有 4 个角,但您可以尝试仅将其调整为前 2 个:
import turtle as t
def draw_square(depth, size, shrink_by=3):
if depth <= 0:
return
for _ in range(4):
t.color(("blue", "orange")[depth%2])
t.forward(size)
t.right(90)
draw_square(depth - 1, size / shrink_by)
t.right(180)
if __name__ == "__main__":
t.speed("fastest")
t.pendown()
draw_square(depth=4, size=100)
t.exitonclick()