Python 乌龟在计时器坐标列表中移动
Python Turtle Move Through List of Coordinates on Timer
我正在尝试让 Python Turtle 使用 timer.There 在坐标列表中移动可能有很多方法可以做到这一点,但在我目前的尝试中,程序只是挂起。有人可以解释一下为什么吗?
import turtle
path = [(0, 0), (10, 10), (10, 20), (30, 40)]
bob = turtle.Turtle("square")
def do_path(a_list):
x, y = a_list[0]
bob.goto(x, y)
while len(a_list) > 0:
turtle.ontimer(lambda: do_path(a_list[1:]), 500)
do_path(path)
turtle.done()
使用全局变量似乎也无济于事:
import turtle
path = [(0, 0), (10, 10), (10, 20), (30, 40)]
bob = turtle.Turtle("square")
def do_path():
global path
x, y = path.pop(0)
bob.goto(x, y)
while len(path) > 0:
turtle.ontimer(lambda: do_path(path), 500)
do_path()
turtle.done()
while 循环中的递归调用对我来说看起来很可怕 - while 循环永远不会在 len(a_list) != 0
的所有递归深度结束。也许更像这样?
import turtle
coordinates = [
(0, 0),
(10, 10),
(10, 20),
(30, 40)
]
coordinates_iter = iter(coordinates)
t = turtle.Turtle("square")
def go_to_next_coord():
try:
next_coord = next(coordinates_iter)
except StopIteration:
return
t.goto(next_coord)
turtle.ontimer(go_to_next_coord, 500)
go_to_next_coord()
turtle.done()
So lambda: do_path(a_list[1:]) doesn't modify a_list? In a recursive
function call it would no?
绝对不是!您只是切片 a_list
并将该(完全独立的)列表作为参数传递给 do_path
。 a_list
从第一次递归开始,大小就不会改变,因此 while 循环会在您的 do_path
等待完成时挂起。
编辑 - 关于它是否真的 "recursion":
import turtle
def foo(depth):
print(f"Starting depth {depth}")
if depth != 5:
turtle.ontimer(lambda: foo(depth+1), 1000)
print(f"Ending depth {depth}")
foo(0)
输出:
Starting depth 0
Ending depth 0
Starting depth 1
Ending depth 1
Starting depth 2
Ending depth 2
Starting depth 3
Ending depth 3
Starting depth 4
Ending depth 4
Starting depth 5
Ending depth 5
看起来它在技术上根本不是严格递归的!似乎乌龟有一种安排这些回调的方法。您可以期望在递归设置中看到的输出如下所示:
Starting depth 0
Starting depth 1
Starting depth 2
Starting depth 3
Starting depth 4
Starting depth 5
Ending depth 5
Ending depth 4
Ending depth 3
Ending depth 2
Ending depth 1
Ending depth 0
但是,您遇到的问题通常与递归或海龟无关。准确地说,这与对调用堆栈 and/or 潜在列表切片的误解有关。看看这个示例代码:
def do_it(depth, items):
length = len(items)
print(f"I'm recursion depth {depth}, I see {length} item(s).")
if depth != 5: #arbitrary base case:
new_items = items[1:]
print(f"Depth {depth} - items: {items}")
print(f"Depth {depth} - new_items: {new_items}")
do_it(depth+1, new_items)
print(f"Depth {depth} is ending now, length is {length} and items is {items}")
do_it(0, [1, 2, 3, 4, 5])
输出:
I'm recursion depth 0, I see 5 item(s).
Depth 0 - items: [1, 2, 3, 4, 5]
Depth 0 - new_items: [2, 3, 4, 5]
I'm recursion depth 1, I see 4 item(s).
Depth 1 - items: [2, 3, 4, 5]
Depth 1 - new_items: [3, 4, 5]
I'm recursion depth 2, I see 3 item(s).
Depth 2 - items: [3, 4, 5]
Depth 2 - new_items: [4, 5]
I'm recursion depth 3, I see 2 item(s).
Depth 3 - items: [4, 5]
Depth 3 - new_items: [5]
I'm recursion depth 4, I see 1 item(s).
Depth 4 - items: [5]
Depth 4 - new_items: []
I'm recursion depth 5, I see 0 item(s).
Depth 5 is ending now, length is 0 and items is []
Depth 4 is ending now, length is 1 and items is [5]
Depth 3 is ending now, length is 2 and items is [4, 5]
Depth 2 is ending now, length is 3 and items is [3, 4, 5]
Depth 1 is ending now, length is 4 and items is [2, 3, 4, 5]
Depth 0 is ending now, length is 5 and items is [1, 2, 3, 4, 5]
>>>
我知道输出有点难以理解,但希望它能证明您似乎有一个误解。仅仅因为您在一个函数中调用了一个新函数(或递归情况下的相同函数),并不意味着您所使用的函数 "leaving" 结束或终止。您离开的函数在调用堆栈上等待,直到您去的函数终止,然后执行返回到调用函数。我在这里真正想强调的是,不同的 "depths"(位于调用堆栈上的函数)看到不同的东西。我在这里使用的示例是递归的,但同样适用于您的非递归情况。仅仅因为你在 do_path
内部调用了 do_path
并不意味着旧的 do_path
突然消失了。它正在等待 do_path
的内部最近调用完成,直到它可以完成。
如果我想解决这个问题但保留你原来方法的味道,为了整理它,我会使用 functools 中的 partial()
而不是 lambda
(连同 update_wrapper()
使其更容易被 ontimer()
) 接受。我还将删除路径从 (0, 0)
开始的假设,并从笔向上开始,在移动到第一个坐标后放下它:
from turtle import Screen, Turtle
from functools import partial, update_wrapper
path = [ \
(100, 180), (90, 170), (95, 150), (80, 135), (65, 145), \
(55, 140), (70, 125), (65, 105), (75, 85), (60, 70), \
(70, 60), (80, 75), (100, 65), (120, 75), (130, 60), \
(140, 70), (125, 85), (135, 105), (130, 125), (145, 140), \
(135, 145), (120, 135), (105, 150), (110, 170), (100, 180), \
]
def do_path(a_list):
position, *rest = a_list
bob.setposition(position)
bob.pendown()
if rest:
wrapper = partial(do_path, rest)
update_wrapper(wrapper, do_path)
screen.ontimer(wrapper, 500)
else:
bob.hideturtle()
screen = Screen()
bob = Turtle('square')
bob.penup()
do_path(path)
screen.exitonclick()
我正在尝试让 Python Turtle 使用 timer.There 在坐标列表中移动可能有很多方法可以做到这一点,但在我目前的尝试中,程序只是挂起。有人可以解释一下为什么吗?
import turtle
path = [(0, 0), (10, 10), (10, 20), (30, 40)]
bob = turtle.Turtle("square")
def do_path(a_list):
x, y = a_list[0]
bob.goto(x, y)
while len(a_list) > 0:
turtle.ontimer(lambda: do_path(a_list[1:]), 500)
do_path(path)
turtle.done()
使用全局变量似乎也无济于事:
import turtle
path = [(0, 0), (10, 10), (10, 20), (30, 40)]
bob = turtle.Turtle("square")
def do_path():
global path
x, y = path.pop(0)
bob.goto(x, y)
while len(path) > 0:
turtle.ontimer(lambda: do_path(path), 500)
do_path()
turtle.done()
while 循环中的递归调用对我来说看起来很可怕 - while 循环永远不会在 len(a_list) != 0
的所有递归深度结束。也许更像这样?
import turtle
coordinates = [
(0, 0),
(10, 10),
(10, 20),
(30, 40)
]
coordinates_iter = iter(coordinates)
t = turtle.Turtle("square")
def go_to_next_coord():
try:
next_coord = next(coordinates_iter)
except StopIteration:
return
t.goto(next_coord)
turtle.ontimer(go_to_next_coord, 500)
go_to_next_coord()
turtle.done()
So lambda: do_path(a_list[1:]) doesn't modify a_list? In a recursive function call it would no?
绝对不是!您只是切片 a_list
并将该(完全独立的)列表作为参数传递给 do_path
。 a_list
从第一次递归开始,大小就不会改变,因此 while 循环会在您的 do_path
等待完成时挂起。
编辑 - 关于它是否真的 "recursion":
import turtle
def foo(depth):
print(f"Starting depth {depth}")
if depth != 5:
turtle.ontimer(lambda: foo(depth+1), 1000)
print(f"Ending depth {depth}")
foo(0)
输出:
Starting depth 0
Ending depth 0
Starting depth 1
Ending depth 1
Starting depth 2
Ending depth 2
Starting depth 3
Ending depth 3
Starting depth 4
Ending depth 4
Starting depth 5
Ending depth 5
看起来它在技术上根本不是严格递归的!似乎乌龟有一种安排这些回调的方法。您可以期望在递归设置中看到的输出如下所示:
Starting depth 0
Starting depth 1
Starting depth 2
Starting depth 3
Starting depth 4
Starting depth 5
Ending depth 5
Ending depth 4
Ending depth 3
Ending depth 2
Ending depth 1
Ending depth 0
但是,您遇到的问题通常与递归或海龟无关。准确地说,这与对调用堆栈 and/or 潜在列表切片的误解有关。看看这个示例代码:
def do_it(depth, items):
length = len(items)
print(f"I'm recursion depth {depth}, I see {length} item(s).")
if depth != 5: #arbitrary base case:
new_items = items[1:]
print(f"Depth {depth} - items: {items}")
print(f"Depth {depth} - new_items: {new_items}")
do_it(depth+1, new_items)
print(f"Depth {depth} is ending now, length is {length} and items is {items}")
do_it(0, [1, 2, 3, 4, 5])
输出:
I'm recursion depth 0, I see 5 item(s).
Depth 0 - items: [1, 2, 3, 4, 5]
Depth 0 - new_items: [2, 3, 4, 5]
I'm recursion depth 1, I see 4 item(s).
Depth 1 - items: [2, 3, 4, 5]
Depth 1 - new_items: [3, 4, 5]
I'm recursion depth 2, I see 3 item(s).
Depth 2 - items: [3, 4, 5]
Depth 2 - new_items: [4, 5]
I'm recursion depth 3, I see 2 item(s).
Depth 3 - items: [4, 5]
Depth 3 - new_items: [5]
I'm recursion depth 4, I see 1 item(s).
Depth 4 - items: [5]
Depth 4 - new_items: []
I'm recursion depth 5, I see 0 item(s).
Depth 5 is ending now, length is 0 and items is []
Depth 4 is ending now, length is 1 and items is [5]
Depth 3 is ending now, length is 2 and items is [4, 5]
Depth 2 is ending now, length is 3 and items is [3, 4, 5]
Depth 1 is ending now, length is 4 and items is [2, 3, 4, 5]
Depth 0 is ending now, length is 5 and items is [1, 2, 3, 4, 5]
>>>
我知道输出有点难以理解,但希望它能证明您似乎有一个误解。仅仅因为您在一个函数中调用了一个新函数(或递归情况下的相同函数),并不意味着您所使用的函数 "leaving" 结束或终止。您离开的函数在调用堆栈上等待,直到您去的函数终止,然后执行返回到调用函数。我在这里真正想强调的是,不同的 "depths"(位于调用堆栈上的函数)看到不同的东西。我在这里使用的示例是递归的,但同样适用于您的非递归情况。仅仅因为你在 do_path
内部调用了 do_path
并不意味着旧的 do_path
突然消失了。它正在等待 do_path
的内部最近调用完成,直到它可以完成。
如果我想解决这个问题但保留你原来方法的味道,为了整理它,我会使用 functools 中的 partial()
而不是 lambda
(连同 update_wrapper()
使其更容易被 ontimer()
) 接受。我还将删除路径从 (0, 0)
开始的假设,并从笔向上开始,在移动到第一个坐标后放下它:
from turtle import Screen, Turtle
from functools import partial, update_wrapper
path = [ \
(100, 180), (90, 170), (95, 150), (80, 135), (65, 145), \
(55, 140), (70, 125), (65, 105), (75, 85), (60, 70), \
(70, 60), (80, 75), (100, 65), (120, 75), (130, 60), \
(140, 70), (125, 85), (135, 105), (130, 125), (145, 140), \
(135, 145), (120, 135), (105, 150), (110, 170), (100, 180), \
]
def do_path(a_list):
position, *rest = a_list
bob.setposition(position)
bob.pendown()
if rest:
wrapper = partial(do_path, rest)
update_wrapper(wrapper, do_path)
screen.ontimer(wrapper, 500)
else:
bob.hideturtle()
screen = Screen()
bob = Turtle('square')
bob.penup()
do_path(path)
screen.exitonclick()