Я написал простой сценарий для решения« логической головоломки », тип головоломки из школы, где вам дают ряд правил, а затем вы должны быть в состоянии найти решение для таких проблем, как «Есть пять музыкантов по имени A, B, C, D и E, играющих на концерте, каждый играет по одному после другого ... если A идет перед B, а D не последний ... каков порядок того, кто когда играет? " и т. д.
Чтобы оценить возможные решения, я написал каждое «правило» как отдельную функцию, которая оценивала бы, если возможное решение (представленное просто в виде списка строк), например,
#Fifth slot must be B or D
def rule1(solution):
return solution[4] == 'B' or solution[4] == 'D'
#There must be at least two spots between A and B
def rule2(solution):
returns abs(solution.index('A') - solution.index('B')) >= 2
#etc...
Я заинтересован в поиске Pythonic способ проверить, соответствует ли возможное решение всем таким правилам, с возможностью прекратить оценивать правила после неудачи первого.
Сначала я написал простейшую возможную вещь:
def is_valid(solution):
return rule1(solution) and rule2(solution) and rule3(solution) and ...
Но это казалось довольно уродливым. Я подумал, что, возможно, смогу сделать это чтение более элегантным с помощью чего-то вроде понимания списка ...
def is_valid(solution)
rules = [rule1, rule2, rule3, rule4, ... ]
return all([r(solution) for f in rules])
... но потом я понял, что, поскольку понимание списка генерируется до функции all ()
оценивается, что это имеет побочный эффект - отсутствие короткого замыкания вообще - каждое правило будет оцениваться, даже если первое вернет False
.
Итак, мой вопрос: есть ли более Pythonic / функциональный способ оценки списка выражений True
/ False
с коротким замыканием без необходимости выписывать длинный список return f1 (s) и f2 (s) и f3 (s) ...
?
Используйте выражение генератор:
rules = [ rule1, rule2, rule3, rule4, ... ]
rules_generator = ( r( solution ) for r in rules )
return all( rules_generator )
Синтаксический сахар: можно опустить лишние круглые скобки:
rules = [ rule1, rule2, rule3, rule4, ... ]
return all( r( solution ) for r in rules )
Генератор - это (в основном) объект с методом .next()
, который возвращает следующий элемент в некоторой итерационной таблице. Это означает, что они могут делать такие полезные вещи, как чтение файла по частям, не загружая его целиком в память, или итерация до огромных целых чисел. Вы можете выполнять итерации с помощью циклов for
прозрачно; Python обрабатывает это за кадром. Например, range
- это генератор в Py3k.
Вы можете создавать свои собственные выражения-генераторы, используя оператор yield
вместо return
в определении функции:
def integers():
i = 0
while True:
yield i
и Python позаботится о сохранении состояния функции и так далее. Они великолепны!