我有这种方法可以从棋盘上获取所有有效的举动。
def get_all_moves(self) -> list:
moves = []
pieces = self.__get_all_turn_pieces()
for p in pieces:
self.__jump_moves(p.coord, moves)
self.__b.wipe()
self.__simple_moves(p, moves)
moves.sort(key=len, reverse=True)
for i in range(len(moves)):
for j in range(len(moves)):
if moves[i][:len(moves[j])] == moves[j] and len(moves[i]) > len(moves[j]):
moves.remove(moves[j])
return moves
问题是:根据跳棋规则,玩家必须在移动中执行最多次数的捕获。因此,我需要删除无效的举动。
让我们分析此输出:
[[3, 0, 5, 2, 3, 4], [5, 0, 3, 2, 5, 4], [1, 0, 2, 1], [1, 2, 2, 3], [1, 2, 2, 1], [1, 4, 2, 3], [2, 5, 3, 6], [2, 5, 3, 4], [2, 7, 3, 6], [3, 0, 5, 2], [5, 0, 3, 2]]
输出列表的第一项和第二项分别包含最后两项。所以我需要删除它们,因为它们是第一个的“子列表”。
问题是我的嵌套循环无法解决此问题。程序溢出此错误:
Traceback (most recent call last):
File "damas.py", line 435, in <module>
print(m.get_all_moves())
File "damas.py", line 418, in get_all_moves
if moves[i][:len(moves[j])] == moves[j] and len(moves[i]) > len(moves[j]):
IndexError: list index out of range
过了一会儿,我想知道解决这个问题的最 pythonic 方法是什么。
答案 0 :(得分:0)
您可能已经解决过,问题是您只在开始时检查了moves
的{{1}}的长度。从其中删除项目时,将其缩短,这将导致循环变量i
最终超出列表的大小。
有多种方法可以解决此问题。对于这种情况,我通常的处理方法是必须从列表的 end 开始,然后再重新开始。
i
请注意,我正在评估for i in reversed(range(len(moves))):
for j in range(len(moves)):
if i != j and moves[i] == moves[j][:len(moves[i])]:
del moves[i]
break
而不是moves[i]
。如果发现要删除它,请这样做,然后立即退出内部moves[j]
循环。这只会影响列表{em> 位置for
之后的列表的结构(我们已经考虑并决定保留的项目),而不是之前的列表(我们尚未考虑的项目) )。因此,我们不会遇到任何讨厌的IndexErrors。
答案 1 :(得分:0)
我不知道这是否是最Python化的处理方式,但这是我用于检查子字符串的一种变体,非常简洁:
def check_for_sublist(sub_list,longer_list):
return any(sub_list == longer_list[offset:offset+len(sub_list)] for offset in range(len(longer_list)))
或者是lambda版本,建议不要在注释中使用。我从评论中学到了一些东西,就把它留在这里作为学习工具!
x_sublist_y = lambda x, y: any(x == y[offset:offset+len(x)] for offset in range(len(y)))
然后我认为这可以解决问题。它会创建条目列表,其中arraycheck
的返回值不为True(请确保排除对自己的条件进行检查)。
moves = [[3, 0, 5, 2, 3, 4], [5, 0, 3, 2, 5, 4], [1, 0, 2, 1], [1, 2, 2, 3], [1, 2, 2, 1], [1, 4, 2, 3], [2, 5, 3, 6], [2, 5, 3, 4], [2, 7, 3, 6], [3, 0, 5, 2], [5, 0, 3, 2]]
filtered_moves = [move1 for move1 in moves if all([not check_for_sublist(move1, move2) for move2 in moves if move1 != move2])]
答案 2 :(得分:0)
这是一个使用列表理解的解决方案,当您想使事情变得尽可能Pythonic时,这通常是一种有用的方法
def is_sublist( seq, lists ):
for candidate in lists:
if len( candidate ) > len( seq ) and candidate[ :len( seq ) ] == seq:
return True
return False
moves = [[3, 0, 5, 2, 3, 4], [5, 0, 3, 2, 5, 4], [1, 0, 2, 1], [1, 2, 2, 3], [1, 2, 2, 1], [1, 4, 2, 3], [2, 5, 3, 6], [2, 5, 3, 4], [2, 7, 3, 6], [3, 0, 5, 2], [5, 0, 3, 2]]
# here comes the list comprehension:
pruned = [ eachMove for eachMove in moves if not is_sublist( eachMove, moves ) ]
要就地修改序列moves
,您应该分配给moves[:]
而不是新变量pruned
。
但是,上面的解决方案并不是最有效的,如果潜在动作的数量很大,则可能会令人担忧。建议采用如下所示的不太优雅的方法,因为随着候选举动被拒绝,我们减少了必须检查每个潜在潜在子列表的潜在超级列表的数量:
accepted = []
while moves:
eachMove = moves.pop( 0 )
if not is_sublist( eachMove, moves ) and not is_sublist( eachMove, accepted ):
accepted.append( eachMove )
moves[:] = accepted
如果移动列表的顺序并不重要,则在此例程的顶部执行moves.sort()
可以使其效率更高(实际上,我们可以进一步优化代码,因为每个移动只有一次必须与列表中的下一个移动进行比较。