Есть ли идиома Python для оценки списка функций / выражений с коротким замыканием? - PullRequest
8 голосов
/ 04 августа 2010

Я написал простой сценарий для решения «логической головоломки», типа головоломки из школы, где вам дают ряд правил, а затем необходимо найти решение для таких проблем, как «Есть пять музыкантов по имени А, 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) and f2(s) and f3(s) ...?

1 Ответ

13 голосов
/ 04 августа 2010

Использовать выражение генератора :

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 будет обрабатывать сохранение состояния функции и так далее.Они классные!

...