Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using any() and all() to check if a list contains one set of values or another

Tags:

python

list

My code is for a Tic Tac Toe game and checking for a draw state but I think this question could be more useful in a general sense.

I have a list that represents the board, it looks like this:

board = [1,2,3,4,5,6,7,8,9] 

When a player makes a move the int they moved on is replaced with their marker ('x' or 'o'), I already have checks in place to look for a winning state, what I can't do is check for a draw state, where none of the list values are ints but a winning state has not been set.

The code I have so far:

if any(board) != playerOne or any(board) != playerTwo:     print 'continue' elif all(board) == playerOne or playerTwo:     print 'Draw' 

The if statement works, the elif does not, I think the problem is my 'or' operator, what I want to check for is: if the every item on the board is either playerOne marker or playerTwo marker, if I where to make the code:

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

I would be checking to see if every place on the board was playerOne or every place on the board is playerTwo, which it won't be.

So how do I check if the board is taken up by a combination of playerOne markers and playerTwo markers?

like image 754
DasSnipez Avatar asked Oct 06 '13 17:10

DasSnipez


1 Answers

Generally speaking:

all and any are functions that take some iterable and return True, if

  • in the case of all(), no values in the iterable are falsy;
  • in the case of any(), at least one value is truthy.

A value x is falsy iff bool(x) == False. A value x is truthy iff bool(x) == True.

Any non-booleans in the iterable will be fine — bool(x) will map (or coerce, if you prefer) any x according to these rules: 0, 0.0, None, [], (), [], set(), and other empty collections get mapped to False, anything else to True. The docstring for bool uses the terms 'true'/'false' for 'truthy'/'falsy', and True/False for the concrete boolean values.


In your specific code samples:

You misunderstood a little bit how these functions work. Hence, the following does something completely not what you thought:

if any(foobars) == big_foobar: 

...because any(foobars) would first be evaluated to either True or False, and then that boolean value would be compared to big_foobar, which generally always gives you False (unless big_foobar coincidentally happened to be the same boolean value).

Note: the iterable can be a list, but it can also be a generator/generator expression (≈ lazily evaluated/generated list) or any other iterator.

What you want instead is:

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

which basically first constructs an iterable that yields a sequence of booleans—for each item in foobars, it compares the item to big_foobar and emits the resulting boolean into the resulting sequence:

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

then any walks over all items in tmp and returns True as soon as it finds the first truthy element. It's as if you did the following:

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 

Note: As DSM pointed out, any(x == y for x in xs) is equivalent to y in xs but the latter is more readable, quicker to write and runs faster.

Some examples:

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 

See also: http://docs.python.org/2/library/functions.html#all

like image 62
Erik Kaplun Avatar answered Oct 02 '22 07:10

Erik Kaplun