是否有一个 Python 习惯用法可以通过短路来评估函数/表达式列表?

发布于 2024-09-13 00:49:21 字数 1145 浏览 1 评论 0原文

我写了一个简单的脚本来解决“逻辑谜题”,这是学校里的谜题类型,给你一些规则,然后必须能够找到诸如“有五个音乐家,名字分别为 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 表达式列表,并且短路,而不需要编写列出一长串返回f1(s)、f2(s)和f3(s) ...

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) ... ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

停顿的约定 2024-09-20 00:49:21

使用 生成器表达式

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

Use a generator expression:

rules = [ rule1, rule2, rule3, rule4, ... ]
rules_generator = ( r( solution ) for r in rules )
return all( rules_generator )

Syntactic sugar: you can omit the extra parentheses:

rules = [ rule1, rule2, rule3, rule4, ... ]
return all( r( solution ) for r in rules )

A generator is (basically) an object with a .next() method, which returns the next item in some iterable. This means they can do useful things like read a file in chunks without loading it all into memory, or iterate up to huge integers. You can iterate over them with for loops transparently; Python handles it behind-the-scenes. For instance, range is a generator in Py3k.

You can roll your own custom generator expressions by using the yield statement instead of return in a function definition:

def integers():
    i = 0
    while True:
        yield i

and Python will handle saving the function's state and so on. They're awesome!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文