【问题标题】:Is there a Python idiom for evaluating a list of functions/expressions with short-circuiting?是否有用于评估具有短路功能的函数/表达式列表的 Python 习语?
【发布时间】:2011-03-25 06:19:17
【问题描述】:

我写了一个简单的脚本来解决“逻辑谜题”,这是一种来自学校的谜题类型,你被赋予了许多规则,然后必须能够找到诸如“有五个名叫 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 表达式列表,并进行短路,而无需写出一长串 @987654328 @?

【问题讨论】:

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


    【解决方案1】:

    使用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 将处理保存函数的状态等等。他们太棒了!

    【讨论】:

    • 那么这里的基本区别是省略return all([r(solution) for r in rules]) 中的括号,因此在评估all() 之前不创建所有结果的列表?
    • 是的。在这两种情况下,all 的参数在传递之前都会被评估,但是评估列表推导式会在内存中创建整个列表,而评估生成器表达式会创建一个生成器对象,该对象会按需加载元素。
    • 我一直在寻找类似的东西——但只要有True而不是False,我就想要短路。我将 all() 更改为 any() 并且成功了。
    猜你喜欢
    • 2013-01-23
    • 2021-09-23
    • 2013-08-25
    • 2011-08-08
    • 2012-01-28
    相关资源
    最近更新 更多