使用 any() 和 all() 函数检测列表是否包含一组值或另一组值

90

我的代码是为了实现井字棋游戏并检查平局状态,但我认为这个问题在一般情况下可能更有用。

我有一个代表棋盘的列表,它看起来像这样:

board = [1,2,3,4,5,6,7,8,9]
当玩家进行移动操作时,他们所移动到的整数位置将被替换为其标记('x'或'o')。我已经设置了检查来确定胜利状态。但是我无法检查平局状态,当列表中的所有值都不是整数并且没有设置胜利状态时。 到目前为止,我拥有的代码:
if any(board) != playerOne or any(board) != playerTwo:
    print 'continue'
elif all(board) == playerOne or playerTwo:
    print 'Draw'

if语句可以工作,但elif不能。我认为问题在于我的“或”运算符。我想要检查的是:如果棋盘上的每个项目都是playerOne标记或playerTwo标记,则执行某些操作。如果我修改代码如下:

elif all(board) == playerOne or all(board) == playerTwo:

我会检查棋盘上的每个位置是否都是playerOne,或者是否所有位置都是playerTwo,但实际不可能这样。

那么我该如何检查棋盘上是由playerOne标记和playerTwo标记组合而成的呢?


3
这真的不是 any()all() 的工作原理:当“可迭代对象(iterable)”中有元素为真时,any() 返回 Trueall() 返回 True;如果可迭代对象为空,则 any()all() 都返回 False - Matt Ball
2个回答

170

一般而言:

allany是函数,它们接受某些可迭代对象并在以下情况下返回True

  • all的情况下,可迭代对象中没有值为falsy;
  • any的情况下,至少有一个值为truthy。

当且仅当bool(x) == False时,值x为falsy。 当且仅当bool(x) == True时,值x为truthy。

可迭代对象中的任何非布尔元素都是完全可以接受的 - 根据以下规则,bool(x)将任何x映射或强制转换:

  • 00.0None[]()[]set()和其他空集合映射到False
  • 所有其他值都映射到True

对于'truthy'/'falsy',bool的文档字符串使用术语“true”/“false”,对于具体的布尔值,则使用True/False


在您的特定代码示例中:

您稍微误解了这些函数的工作方式。以下内容与您的想法完全不同:

if any(foobars) == big_foobar:

因为 any(foobars) 首先会被计算为 True 或者 False,然后这个布尔值会被与 big_foobar 进行比较,通常情况下都会得到 False(除非 big_foobar 恰好是相同的布尔值)。

注意:可迭代对象可以是列表,也可以是生成器或生成器表达式(≈ 延迟计算/生成的列表),或任何其他迭代器。

你需要使用以下代码:

if any(x == big_foobar for x in foobars):

这基本上首先构造了一个可迭代对象,该对象生成一系列布尔值——对于foobars中的每个项目,它将该项目与big_foobar所保存的值进行比较,并(惰性地)将结果布尔值发出到生成的布尔序列中:

tmp = (x == big_foobar for x in foobars)

然后any会遍历tmp中的所有元素,并在找到第一个真值元素时立即返回True。这就好像你执行了以下操作:

any(elem for elem in tmp)

In [1]: foobars = ['big', 'small', 'medium', 'nice', 'ugly']                                        

In [2]: big_foobar = 'big'                                                                          

In [3]: any(['big' == big_foobar, 'small' == big_foobar, 'medium' == big_foobar, 'nice' == big_foobar, 'ugly' == big_foobar])        
Out[3]: True

注意:正如DSM所指出的,any(x == y for x in xs) 等同于 y in xs 但后者更易读、更快速地书写和运行更快。

一些例子:

In [1]: any(x > 5 for x in range(4))
Out[1]: False

In [2]: all(isinstance(x, int) for x in range(10))
Out[2]: True

In [3]: any(x == 'Erik' for x in ['Erik', 'John', 'Jane', 'Jim'])
Out[3]: True

In [4]: all([True, True, True, False, True])
Out[4]: False

参见: http://docs.python.org/2/library/functions.html#all


9
我认为写成big_foobar in foobars会更符合惯用方式,而不是any(x == big_foobar for x in foobars) - DSM

0

针对标题中的问题:

如果一个列表包含一组值或另一组值

使用集合操作可能更自然。换句话说,不是使用

if any(x==playerOne for x in board) or any(x==playerTwo for x in board):

# or 
if playerOne in board or playerTwo in board:

使用 set.issubset(或者 set.intersection1):

if {playerOne, playerTwo}.issubset(board):

# or
if {playerOne, playerTwo} & set(board):

如果playerOneplayerTwo是一组值的集合/列表/元组,则计算它们的并集并测试其是否为board的子集:
if {*playerOne,*playerTwo}.issubset(board):

还有,如果问题是:

如果棋盘上的每个项目都是playerOne标记或playerTwo标记

那么不是

if all(x == playerOne or x == playerTwo for x in board):

测试集相等性:1

if {playerOne, playerTwo} == set(board):

1显然,您可以事先将set(board)分配给某个变量,以便每次需要测试此条件时不必将board转换为集合。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接