【问题标题】:Is there any advantage to using De Morgan's laws in python?在 python 中使用德摩根定律有什么好处吗?
【发布时间】:2014-01-23 22:49:22
【问题描述】:

我使用 pycharm 并且有几次在使用 if 语句时我看到了使用德摩根定律更改语句的建议,例如使用以下if 语句:

if new_odds > 10 and new_odds <= 30:

if not (not (new_odds > 10) or not (new_odds <= 20)):

对我来说,它的可读性降低了,那么使用德摩根定律有什么好处,还是完全是个人选择?

【问题讨论】:

  • 为什么不建议10 &lt; new_odds &lt;= 30
  • 正如我在下面的评论中所说的那样,我最终使用它只是想发布我的原始代码来展示我为获得建议而写的内容。我只是好奇如果使用德摩根定律有什么优势。
  • 我认为 PyCharm 并不是建议您更改代码,而是如果您想更改代码,只需单击建议即可。

标签: python demorgans-law


【解决方案1】:

De Morgan's laws 声明:

"not (A and B)" 等同于"(not A) or (not B)"

还有,

“非(A或B)”与“(非A)和(非B)”相同

用于在替代形式之间转换逻辑。因此,虽然您所做的转换符合德摩根定律,但它变得更加难以阅读。正如其他人所建议的那样,更简单的10 &lt; new_odds &lt;= 30 将更具可读性,然而了解这是 10 &lt; new_odds and new_odds &lt;= 30 的缩写非常重要,因为从这里您可以执行以下逻辑:

10 < new_odds <= 30 != max_odds | default_condition

扩展为:

10 < new_odds and new_odds <= 30 and 30 != max_odds and max_odds | default_condition

考虑到语法糖,让我们看另一个例子:

我们将在简单的角色扮演游戏中考虑一个人为的示例,我们将在其中查看我们称之为“荷兰勇气”的技能。此攻击的前提是,如果您处于最大健康状态,并且您的护甲或攻击等级不足以攻击敌人,您可以获得奖励。我需要知道这条规则何时不适用。

写出来我们有 4 个条件:

A = health == max_health
B = armor > enemy.attack
C = attack > enemy.defense
no_bonus = not(A and not(B and C))

使用德摩根定律,我可以这样分解:

not(A and not(B and C))
not(A) or not(B and C)
not(A) or not(B) or not(C)
not(health == max_health) or not(armor > enemy.attack) or (attack > enemy.defense)

好吧,现在我可以进一步分解了……

health < max_meath or armor < enemy.attack < attack > enemy.defense

这里我们假设== max_health 的反义词是&lt; max_health,否则它不是最大值。

虽然是人为的,但这向我们展示了德摩根定律是使我们能够重写逻辑的工具。是否改进此逻辑取决于程序员,但其目的是能够产生更简单的构造,首先更易读,其次希望需要更少的指令,从而更快。

【讨论】:

  • 冲锋队,谢谢,终于明白了。 Pycharm 的编辑称其为 Demoorgan Law,所以我会责怪 Jetbrain 的术语不正确。
  • 这里有一些错误;首先也是主要的,Python 中的运算符链接仅适用于比较运算符,即。 30 != max_odds | default_condition 不会像您指出的那样扩展到 30 != max_odds and max_odds | default_condition。其次,您在示例中存在错误:分解not(A and not(B and C)) 产生not(A) or (B and C) 而不是not(A) or not(B and C)。后来,最后一个not也神秘消失了,最后一个or变成了不相关的比较……
【解决方案2】:

在 Python 中,几乎总是清晰比稍微快一点要好(如果那样的话,我对此表示怀疑)。

我更喜欢这个更简单的语句:

if 10 < new_odds <= 30:

【讨论】:

  • 我最后实际上使用了它,它也是由 pycharm 建议的,我只是想知道为什么有人会使用 DeMorgan Law,因为它使一个简单的语句更难阅读,我认为一定有一些优势。
  • 虽然是一个更好的灵魂,但这并不能解释德摩根定律是什么以及它们如何有用。
  • @LegoStormtroopr,我同意我的回答并不像其他人那样广泛,但对于 OP 与预计算变量 (new_odds) 进行比较的情况,零 i> 应用德摩根定律可能带来的好处。
【解决方案3】:

在某些情况下,它使事情变得更加冗长和可读。但是,如果您已经有一堆 nots 散布在周围,或者您正在以不太自然的顺序比较事物,则分解可以减少 nots 的数量或颠倒顺序不平等的比较。例如:

if not foo() and not bar():
if not(foo() or bar()):

if new_score <= high_score and new_level <= high_level:
if not (new_score > high_score or new_level > high_level)

(第二个值得商榷……但这正是您对可读性和风格问题的期望。)

所以,如果它使您的代码更具可读性,那就去做吧;否则,不要。


少数语言(逻辑、约束满足、关系等)并非如此,因为将 not 应用于值不仅仅是翻转 True 和错误,但会生成相反的,可能慢得多,甚至可能不确定的查询。

但 Python 或大多数其他“通用”语言并非如此。

【讨论】:

  • 所以使用 DeMorgan Law,如果第一个条件为假,则它不会继续,而不是使用 not foo() 和 not bar() 无论如何都会检查两个函数,我理解你正确吗?
  • @PadraicCunningham:不,你会在同一个地方短路:如果解体版本可以短路or,因为foo() 是True,那么原始版本可以短路-循环and,因为not foo() 是真的。两种方式都没有显着的性能或正确性优势,因此可读性是唯一的问题。
  • @PadraicCunningham:(嗯, 实际上,无论哪一个具有较少的总运算符,都会带来一点性能优势,但是 (a) 这几乎无关紧要,并且(b) 无论如何,它几乎总是与可读性相同……)
【解决方案4】:

鉴于下面的结果,似乎更复杂/可读性较差的表达式也是最慢的。在任何情况下,可读性在大多数时候在 python 中更有价值。

In [1]: new_odds = 0  
In [2]: %timeit if new_odds > 10 and new_odds <= 30: pass  
10000000 loops, best of 3: 24.3 ns per loop

In [3]: %timeit if not (not (new_odds > 10) or not (new_odds <= 20)): pass  
10000000 loops, best of 3: 48.6 ns per loop  

In [4]: %timeit if 10 < new_odds <= 30:pass  
10000000 loops, best of 3: 43.4 ns per loop  

In [5]: new_odds = 20  
In [6]: %timeit if new_odds > 10 and new_odds <= 30: pass  
10000000 loops, best of 3: 57.7 ns per loop  

In [7]: %timeit if not (not (new_odds > 10) or not (new_odds <= 20)): pass  
10000000 loops, best of 3: 102 ns per loop  

In [8]: %timeit if 10 < new_odds <= 30:pass  
10000000 loops, best of 3: 52.7 ns per loop

【讨论】:

    【解决方案5】:

    它有时对可读性很有用,但在执行时是一样的。例如:

    not(A AND B) === not(A) OR not(B)
    
    if not a() and not b():
    if not(a() or b()):
    

    执行将与a() True 或 False 相同。

    对于您的示例,最好的解决方案仍然是使用 Python 语法的强大功能并编写:

    if 10 < new_odds <= 30:
    

    这种语法对于检查数值是否在特定范围内非常有用。

    【讨论】:

    • 不,b()不会在第二种情况下每次都被调用;如果a() 为True,or 将短路——就像在第一种情况下,如果not a() 为False,and 将短路。这里没有区别。
    猜你喜欢
    • 1970-01-01
    • 2016-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-16
    • 2016-08-29
    • 2017-05-29
    相关资源
    最近更新 更多