I wrote a simple script to solve a "logic puzzle", the type of puzzle from school where you are given a number of rules and then must be able to find the solution for problems like "There are five musicians named A, B, C, D, and E playing in a concert, each plays one after the other... if A goes before B, and D is not last ... what is the order of who plays when?" etc.
To evaluate possible solutions, I wrote each "rule" as a separate function which would evaluate if a possible solution (represented simply as a list of strings) is valid, for example
#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...
I'm interested in finding the Pythonic way to test if a possible solution passes all such rules, with the ability to stop evaluating rules after the first has failed.
At first I wrote the simplest possible thing:
def is_valid(solution):
return rule1(solution) and rule2(solution) and rule3(solution) and ...
But this seemed rather ugly. I thought perhaps I could make this read a bit more elegant with something like a list comprehension...
def is_valid(solution)
rules = [rule1, rule2, rule3, rule4, ... ]
return all([r(solution) for f in rules])
... but then I realized that since the list comprehension is generated before the all()
function is evaluated, that this has the side effect of not being short-circuited at all - every rule will be evaluated even if the first returns False
.
So my question is: is there a more Pythonic/functional way to be able to evaluate a list of True
/False
expressions, with short-circuiting, without the need to write out a long list of return f1(s) and f2(s) and f3(s) ...
?