Python:将此检查重构为自己的方法是否有意义?

时间:2010-04-28 14:12:33

标签: refactoring python tic-tac-toe

我还在学习python。我刚刚写了这个方法,以确定一个玩家是否赢得了一个井字游戏,给出了一个董事会状态:'[['o','x','x'],['x','o','-'],['x','o','o']]'

def hasWon(board):
  players = ['x', 'o']
  for player in players:
    for row in board:
      if row.count(player) == 3:
        return player
    top, mid, low = board
    for i in range(3):
      if [ top[i],mid[i],low[i] ].count(player) == 3:
        return player
    if [top[0],mid[1],low[2]].count(player) == 3:
        return player
    if [top[2],mid[1],low[0]].count(player) == 3:
        return player
  return None

在我看来,我多次检查了3个字符的列表,并且可以将检查重构为自己的方法,如下所示:

def check(list, player):
  if list.count(player) == 3:
    return player

......但后来意识到所有真正做的就是更改行:

 if [ top[i],mid[i],low[i] ].count(player) == 3:
    return player

为:

  if check( [top[i],mid[i],low[i]], player ):
    return player

......坦率地说,似乎并没有多大的改进。你有没有看到更好的重构方法?或者一般来说更多的Pythonic选项?我很乐意听到它!

7 个答案:

答案 0 :(得分:5)

我可以使用

def check(somelist, player):
  return somelist.count(player) == 3

编辑:正如@Andrew在评论(tx @Andrew!)中建议的那样,你可以做得更好,例如:

def check(somelist, player):
  return somelist.count(player) == len(somelist)

没有硬编码3 - 这也暗示了另一个不错的选择:

def check(somelist, player):
  return all(x==player for x in somelist)

非常直接地读取“列表中的所有项目等于player”。一般的观点是,通过重构到一个单独的方法,你可以使用该方法的实现 - 现在当然这里的代码非常简单,所以优势同样适中,但是当你转移到时,这是一个很好的注意事项。更复杂的代码。

正如你已经注意到你只需要一个bool,所以这允许一个更简单的方法 - 只需返回bool表达式而不是对它进行if。重要的是永远不要使用像list这样的内置名称作为您自己的标识符 - 这种语言的“有吸引力的麻烦”......; - )。

我的意思是,Python为其内置函数使用了许多漂亮,有吸引力的名称,如list,bool,sum等等,因此很容易发现自己不小心使用其中一个名称作为自己的变量似乎没有什么坏事......直到你需要把一个元组转换成一个列表,使用明显最好的解决方案,x = list(thetuple) ...并最终花费我们的努力来理解和解决由于您使用list表示除该名称的内置类型之外的任何其他内容而导致的错误。

所以,只要养成的习惯,使用这些漂亮的内置名称除了指示各自的内置之外的目的,你将为自己节省很多未来的恶化! - )

回到你的代码,你可能会考虑而不是解包board所提供的简洁性(一个艰难的决定,因为你的代码 非常易读......但可能看起来有点冗长):

for i in range(3):
  if check([row[i] for row in board], player):
    return player
if check([row[i] for i, row in enumerate(board)], player):
    return player
if check([row[2-i] for i, row in enumerate(board)], player):
    return player

最后,我认为我会坚持你的选择 - 更具可读性,只是略微更冗长,如果有的话 - 但我很高兴知道替代品,我认为 - 在这里,列出理解和{ {1}}生成要检查的列表,作为“手动编码”三种可能性的替代方法。

答案 1 :(得分:2)

只需在board上创建自定义迭代器。

def get_lines(board):
  nums = range(3)
  for i in nums: 
    yield (board[i][j] for j in nums) #cols
  for j in nums: 
    yield (board[i][j] for i in nums) #rows
  yield (board[i][i] for i in nums) #diag \
  yield (board[i][2-i] for i in nums) #diag /

def get_winner(board): #a bit too indented
  for line in get_lines(board): #more expensive, so go through it only once
    for player in 'x', 'o':
      if line == player, player, player: #other way to check victory condition
        return player
  return None

显然这些应该是board类的方法:)

答案 2 :(得分:2)

您可以使用更好的名称,而不是check,但这并不能说明问题。经验法则是:如果你能想出一个代码和平的好名字,那么将它移动到单独的函数中可能是有益的,即使它只是一行代码。 allsame可能是此处的替代选择之一。

def winner(board):
    main_diag = [row[i] for i, row in enumerate(board)]
    aux_diag = [row[len(board) - i - 1] for i, row in enumerate(board)]   
    for triple in board + zip(*board) + [main_diag, aux_diag]: 
        if allsame(triple):         
           return triple[0]

def allsame(lst):    
    return all(x == lst[0] for x in lst)

答案 3 :(得分:1)

就我个人而言,我认为最好的选择是将函数冒出来,为你提供董事会的行(),列()和diags(),作为列表的列表。然后你可以迭代这些并统一检查。您甚至可以定义allTriples(),它附加rows(),columns()和diags()的输出,以便您可以在一个简洁的循环中进行检查。我可能也会把电路板作为一个对象,这样这些函数就可以成为对象方法。

答案 4 :(得分:1)

现在有一些完全不同的东西:

通过九个要素列表代表董事会。每个元素可以是-1(X),1(O)或0(空):

WIN_LINES = (
    (0, 1, 2),
    (3, 4, 5),
    (6, 7, 8),
    (0, 3, 6),
    (1, 4, 7),
    (2, 5, 8),
    (2, 4, 6),
    (0, 4, 8),
    )

def test_for_win(board):
    for line in WIN_LINES:
        total = sum(board[point] for point in line)
        if abs(total) == 3:
            return total // 3
    return None

精修:

WIN_LINES = (
    0, 1, 2,
    3, 4, 5,
    6, 7, 8,
    0, 3, 6,
    1, 4, 7,
    2, 5, 8,
    2, 4, 6,
    0, 4, 8,
    )

def test_for_win(board):
    wpos = 0
    for _unused in xrange(8):
        total  = board[WIN_LINES[wpos]]; wpos += 1
        total += board[WIN_LINES[wpos]]; wpos += 1
        total += board[WIN_LINES[wpos]]; wpos += 1
        if total ==  3: return  1
        if total == -3: return -1
    return None

答案 5 :(得分:0)

只是一个想法

def hasWon(board):
  players = ['x', 'o']
  for player in players:
    top, mid, low = board
    game = board + [[ top[i],mid[i],low[i]] for i in range(3)] + [top[0],mid[1],low[2]] +[top[2],mid[1],low[0]]
    if 3 in [l.count(player) for l in game] :
      return player
  return None

答案 6 :(得分:0)

您的解决方案很好 - 正确,易读且易于理解。

但是,如果你想优化速度,我会使用一维数字而不是字符串数组,并尝试尽可能少地查找每个数字。肯定会有一个非常尴尬的解决方案,您只需检查一次每个字段。我现在不想构建它。 :)如果您想要在探索可能移动的整个搜索树时实施与您对战的AI,那么这样的事情可能很重要。那里有必要进行有效的赢/输检查。