Python: имеет ли смысл преобразовать эту проверку в свой собственный метод? - PullRequest
2 голосов
/ 28 апреля 2010

Я все еще изучаю 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

... что, откровенно говоря, не кажется значительным улучшением. Вы видите лучший способ реорганизовать это? Или вообще более питонский вариант? Я хотел бы услышать это!

Ответы [ 7 ]

5 голосов
/ 28 апреля 2010

Я мог бы использовать

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

В конце концов, я думаю, что я буду придерживаться вашего выбора - более читабельного и просто чуть более многословного, если вообще - но, мне кажется, приятно осознавать альтернативы - здесь, перечислите понимание и enumerate для создания списков для проверки в качестве альтернативы «ручному кодированию» трех возможностей.

2 голосов
/ 28 апреля 2010

Вы можете использовать лучшее имя вместо 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)
2 голосов
/ 28 апреля 2010

Просто создайте пользовательский итератор для 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:)

1 голос
/ 29 апреля 2010

А теперь для чего-то совершенно другого:

Представляет доску списком из девяти элементов. Каждый элемент может быть -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
1 голос
/ 28 апреля 2010

Лично я считаю, что для удобства чтения лучше всего разбивать функции, чтобы они представляли строки (), columns () и diags () доски в виде списков списков. Затем вы можете пройтись по ним и проверять равномерно. Вы даже можете определить allTriples (), который добавляет выходные данные row (), columns () и diags (), чтобы вы могли выполнять проверку в одном кратком цикле. Вероятно, я бы также сделал доску объектом, чтобы эти функции могли стать объектными методами.

0 голосов
/ 28 апреля 2010

Ваше решение в порядке - правильно, читабельно и понятно.

Тем не менее, если вы хотите оптимизировать скорость, я бы использовал одномерный массив цифр, а не строк, и попытался бы найти каждое число как можно меньше раз. Конечно, будет крайне неудобное решение, в котором вы проверяете каждое поле только один раз. Я не хочу строить это сейчас. :) Подобные вещи могут иметь значение, если вы хотите реализовать ИИ, играющего против вас, при изучении всего дерева поиска возможных ходов. Там будет необходима эффективная проверка на выигрыш / проигрыш.

0 голосов
/ 28 апреля 2010

Просто идея

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
...