是否有用于评估短路函数/表达式列表的Python习惯用法?

时间:2010-08-04 13:08:32

标签: functional-programming python list-comprehension short-circuiting

我写了一个简单的脚本来解决一个“逻辑谜题”,即从学校获得许多规则的拼图类型,然后必须能够找到问题的解决方案,例如“有五位音乐家名为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) and f2(s) and f3(s) ...的列表?

1 个答案:

答案 0 :(得分: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将处理保存函数的状态等等。他们太棒了!