8

我写了一个简单的脚本来解决一个“逻辑谜题”,这是一种来自学校的谜题类型,您可以在其中获得许多规则,然后必须能够找到问题的解决方案,例如“有五个音乐家名为A,B ,C,D和E在一场音乐会中演奏,每一个都依次演奏......如果A在B之前,而D不是最后......谁在什么时候演奏?等是否有Python语言用于评估短路的函数/表达式列表?

评估可能的解决方案,我写的每一个“规则”作为一个独立的功能,如果可能的解决方案(简单地表示为一个字符串列表)是有效的,其将评估,例如

#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,也将评估每个规则。

所以我的问题是:是否有一个更Python /功能性的方式才能够评估True/False表情列表,以及短路,而不需要写出来的return f1(s) and f2(s) and f3(s) ...一个长长的清单?

回答

13

使用generator expression

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将处理保存功能的状态等。他们真棒!

+1

所以这里的基本区别是省略了'return all([r(solution)for r in rules])'中的括号,因此在'all()'被评估之前不会创建所有结果的列表? – 2010-08-04 13:16:10

+2

是的。在这两种情况下,“all”的参数在传递之前进行评估,但评估列表理解会在内存中创建整个列表,而评估生成器表达式会创建一个生成器对象,该对象根据需要加载元素。 – katrielalex 2010-08-04 13:19:21

+0

完美,这使得很多感 - 感谢 – 2010-08-04 13:23:47