Python中的自调用函数
假设我正在用Python制作一个战舰游戏。我有一个叫做board的列表,里面包含了多个列表。board中的每个列表代表棋盘的一行,在这个例子中,棋盘是5x5的:
[['clear', 'clear', 'clear', 'clear', 'clear'],
['clear', 'clear', 'clear', 'clear', 'clear'],
['clear', 'clear', 'clear', 'clear', 'clear'],
['clear', 'clear', 'clear', 'clear', 'clear'],
['clear', 'clear', 'clear', 'clear', 'clear']]
我想创建一个函数,能够返回棋盘上一个随机的空位。
def pl_point(board):
place = [randrange(0,5),randrange(0,5)] #heh, found out these aren't inclusive.
if board[place[0]][place[1]] == "clear": return place
else:
return pl_point() #calls itself until a clear place is found
我有几个问题:让一个函数自己调用自己,直到得到想要的值,这样做效率低吗?特别是当棋盘几乎被填满的时候?有没有更好的方法可以做到这一点?
我不知道怎么用while语句来实现这个,因为这个语句总是会在'place'被赋值之前就引用它,我也想不出一个不会引用超出范围或者未赋值的'board'值的'place'的值。
如果这个话题重复了,我很抱歉,我试着找过类似的,但没找到。这也是我第一次在这个网站上提问,而不是回答问题。
3 个回答
生成器是一个非常实用的Python工具,可以帮助你解决问题。简单来说,你想要在一组随机的空白空间中逐个返回一个项目,对吧?
你首先需要创建一个函数,这个函数会生成一个包含所有空白空间的列表,就像一些已有的答案所提到的那样,然后从这个列表中随机选择一个项目返回。不过,之后你可以通过调用你创建的函数的next()来继续获取需要的选择。
import random
def board_randomizer():
clear_spaces = [...] # list comprehension or your method of choosing
while clear_spaces:
coord = random.choice(clear_spaces)
clear_spaces.remove(coord)
yield coord # returns a random location, one item at a time
clear_positions = board_randomizer() # initialize a generator
# keeps picking random locations until a StopIteration error is raised
clear_positions.next()
clear_positions.next()
当然有更好的方法。试试这个:
def pl_point(board):
for y,row in enumerate(board):
for x,col in row:
if col == 'clear':
return (x,y)
有没有什么特别的原因需要它是随机的?如果有的话……
def pl_point_random(board):
places = {(x,y) for x in range(len(board)) for y in range(len(board[0]))}
# the above is a set comprehension of every spot on the board
# (assuming the board is rectangular...)
while True:
point = random.choice(places)
# choose a random spot in places
x,y = point
if board[x][y] != 'clear':
return point
else:
places.remove(point)
或者更好的方法是:
def pl_point_random_v2(board):
places = [(x,y) for y,row in enumerate(board) for x,col in rows if col=='clear']
return random.choice(places)
让一个函数自己调用自己,直到得到想要的值,这样做效率低吗?(即使棋盘几乎填满了)有没有更好的方法可以做到这一点?
是的,确实如此。不过,问题不在于效率,而是如果你运气不好,需要尝试1000次,你的程序就会因为递归错误而崩溃。
我不知道怎么用while语句来处理这个,因为这个语句总是会在'place'被赋值之前引用它,我想不出一个不会引用超出范围或未赋值的'board'值的'place'值。
你可以直接用 while True:
,然后用 break
或 return
来跳出循环:
while True:
place = [randrange(0,4),randrange(0,4)] #randrange is inclusive
if board[place[0]][place[1]] == "clear":
return place
顺便提一下,正如inspectorG4dget在评论中指出的,randrange
是 不包含 的;这只会返回数字 0
、1
、2
和 3
。
另外,把x和y坐标放到一个列表里,只是为了能重复使用 [0]
和 [1]
,这样会让代码看起来更难懂。你可以直接用 x, y = [randrange(5), randrange(5)]
(这样也解决了之前的问题),然后用 board[x][y]
,最后 return x, y
。
如果这样做太慢了,那确实有更好的方法。首先,列出所有空位,然后随机选择一个:
clearslots = [(x, y) for x in range(5) for y in range(5) if board[x][y] == "clear"]
return random.choice(clearslots)
当棋盘大部分是空的时候,这种方法可能会慢一点,但随着棋盘的填满,它的速度不会变得更慢。而且,与你的方法不同,这种方法在最坏情况下是有保证的常量时间;也就是说,程序花费数年时间的可能性是不存在的。
如果你不理解那个列表推导式,我可以更明确地写出来:
clearslots = []
for x in range(5):
for y in range(5):
if board[x][y] == "clear":
clearslots.append((x, y))