Функции all и any. Примеры их использования

Курс по Python: https://stepik.org/course/100707

На этом занятии мы поговорим о функциях all() и any(). Первым делом посмотрим, что они делают и зачем нужны? Начнем с функции all() и очень простого примера. Предположим, что есть список булевых значений:

a = [True, True, True, True]

И мы хотим узнать, принимают ли все они значение True? Для этого, как раз и используется функция all(), которая на вход принимает итерируемый объект и все его значения приводит к булевым величинам:

all(a)

Если все значения равны True, то на выходе получаем True. Если же, хотя бы одно значение принимает False, то на выходе будет False:

all([True, True, False, True])

Но, как вы понимаете, значения в списках могут быть любыми, например, такими:

a = [0, 1, 2.5, "", "python", [], [1, 2], {}]

И к нему тоже можно применить функцию all():

all(a)

Как она работает? Помните, когда мы говорили о типе bool, то говорили, что любое значение можно привести к этому типу? В частности:

bool(0)

даст False, так как 0 воспринимается, как пустое значение. По тем же причинам False получается и для:

bool("")
bool([])
bool({})

А вот все, что не пустое, превращается в True:

bool(1)
bool("python")

Именно так вначале происходит преобразование списка, а затем, применяется функция all(). И, так как список содержит значения False, то и результат равен False.

Работу этой функции можно повторить с помощью обычных булевых операций. Если вначале объявить некую переменную со значением True:

all_res = True
for x in a:
    all_res = all_res and bool(x)
 
print(all_res)

А, затем, в цикле для каждого элемента выполнять оператор and, то эта переменная сохранит начальное значение True только в том случае, если не встретится ни один False. Я вам показал этот способ, чтобы вы знали как действовать в других языках программирования, где нет уже готовой функции all(). В Python, конечно, следует использовать эту функцию – это и удобнее и быстрее.

Вторая функция any() работает похожим образом, но возвращает True, если встретилось хотя бы одно такое значение. Например, для списка:

any(a)

мы увидим значение True. А вот если передать список со всеми False:

any([False, False, False, False])

то только в этом случае она вернет False.

Повторим также работу этой функции через булевы операции. Начальное значение переменной здесь нужно присвоить False:

any_res = False
for x in a:
    any_res = any_res or bool(x)
 
print(any_res)

А, затем, в цикле с помощью оператора or (или) корректировать это значение текущей булевой величиной. Если встретится хотя бы один True, то оператор or вернет True и оно сохранится в переменной any_res, что бы в дальнейшем не появлялось.

Вот принцип работы этих двух функций. Ну и, наверное, самый главный вопрос, где это имеет смысл применять? Давайте представим, что мы делаем игру «Крестики-нолики» и хотели бы на каждом шаге определять, есть ли выигрышная позиция, например, у крестиков? Для простоты сделаем это следующим образом. Все поле из девяти клеток я представлю одномерным списком (сейчас вы узнаете, зачем):

P = ['x', 'x', 'o', 'o', 'x', 'o', 'x', 'x', 'x']

Тогда, чтобы проверить выигрышные ситуации по строкам, можно воспользоваться функцией all(), следующим образом:

row_1 = all(map(lambda x: x == 'x', P[:3]))
row_2 = all(map(lambda x: x == 'x', P[3:6]))
row_3 = all(map(lambda x: x == 'x', P[6:]))
 
print(row_1, row_2, row_3)

Смотрите, мы здесь используем вложенный вызов функции map(), чтобы правильно преобразовать крестики в True, а нолики – в False, иначе бы все преобразовалось в True. Далее, срез для каждой строки на выходе функции map() обрабатывается функцией all() и получаем результат: True – есть выигрышная комбинация; False – нет выигрышной комбинации.

Кстати, внимательный и грамотный зритель здесь сразу должен возмутиться из-за дублирования кода – три раза записана одна и та же анонимная функция. Действительно, это лучше поправить и использовать свою собственную:

def true_x(a):
    return a == 'x'

И, затем, указать ее в функции map():

row_1 = all(map(true_x, P[:3]))
row_2 = all(map(true_x, P[3:6]))
row_3 = all(map(true_x, P[6:]))

Теперь известный принцип DRY – сохраняется. По аналогии можно сделать проверку выигрыша по столбцам:

col_1 = all(map(true_x, P[::3]))
col_2 = all(map(true_x, P[1::3]))
col_3 = all(map(true_x, P[2::3]))
 
print(col_1, col_2, col_3)

А по диагоналям сделайте самостоятельно – это несложно.

Для второй функции я приведу такой короткий пример. Предположим, мы делаем игру «Сапер» и игровое поле также представлено в виде одномерного списка длиной NxN элементов:

N = 10
P = [0] * (N*N)

Далее, если в этом списке появляется хотя бы одна мина (отметим ее звездочкой):

P[4] = '*'

то игрок проигрывает. Чтобы узнать, наступил ли игрок на мину, удобно воспользоваться функцией any():

loss = any(map(lambda x: x == '*', P))
print(loss)

После запуска программы увидим значение True, то есть, игрок проиграл. А если мину поставить в комментарий и снова запустить, то увидим значение False.

Вот два простых примера, где удобно применять эти две функции any() и all().

Курс по Python: https://stepik.org/course/100707

Видео по теме