我写了一个简单的脚本来解决一个“逻辑难题”,这类难题来自学校,在那里你会得到一些规则,然后必须能够找到解决问题的方法,比如“有五个音乐家,分别是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
all()
False
所以,我的问题是:有没有一种更具Pythonic/功能性的方法来评估 True / 假 表达式,具有短路功能,无需写出一长串 return f1(s) and f2(s) and f3(s) ... ?
True
假
return f1(s) and f2(s) and f3(s) ...
使用 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() 方法,该方法返回某个iterable中的下一项。这意味着他们可以做一些有用的事情,比如分块读取文件而不将其全部加载到内存中,或者迭代到大整数。你可以用 for range 是Py3k的发电机。
.next()
for
range
您可以使用 yield 语句而不是 return 在函数定义中:
yield
return
def integers(): i = 0 while True: yield i
Python将处理保存函数的状态等等。他们太棒了!