【问题标题】:How to write If statements for all 2^N boolean conditions (python)如何为所有 2^N 布尔条件编写 If 语句(python)
【发布时间】:2012-03-01 23:02:07
【问题描述】:

我有一个函数需要根据输入的查询实例执行查询。 但随着条件的增加,我列出所有这些变得乏味。 例如: 假设我最初有两个条件:

if (cond_1 == True and cond_2 == False):
    do something
elif  cond_1 == True and cond_2 == True:
    do something else
elif cond_1 == False and cond_2 == True:
    do this

....

所以我猜如果条件采用二进制值,那么我必须编写 2^n 条语句:(

所以现在我有 3 个条件变量(8 个语句).. 我担心这个数字将来可能会增加。 是否有更好的方法来检查这些条件??

【问题讨论】:

  • 如果您想在所有这些情况下做其他事情,那么现在可以为每种情况编写一份声明,对吧?
  • == True== False 几乎总是一个(风格上的)错误。使用cond_1 and not cond_2)
  • @SvenMarnach:实际上循环内执行的内容略有不同..因为它通常是一个查询..所以如果条件为真..那么它包括查询中的条件值else 将其从查询中排除..(SQL 查询.. Select * from this If cond = this and cond_2 = that.. 所以基本结构有点常见。但基于循环,这些条件被附加
  • 您应该举例说明您在个别情况下所做的事情 - 这样做可能会更容易。

标签: python algorithm


【解决方案1】:

您是否需要始终写出所有 2^n 种可能性?

你必须做的所有事情都是不同的吗(2^n 动作也是如此?)

不过我可以给出一些提示:

不要使用 '== True' 或 '== False'

你写的等于:

if cond_1 and not cond_2:
    do something 
elif cond_1 and cond_2:
    do something else 
elif not cond_1 and cond_2:
    do this 

也考虑写作:

if cond_1:
    if cond_2:
        Do something
    else:
        Do something 
else:
    if cond_2:
        Do something
    else:
        Do something 

您还可以根据值定义函数:

def __init__(self):
    self.actions = {
      (False, False): action_1,
      (False, True ): action_2,
      ...
    }

def action_1(self):
    some action

def action_2(self):
    some action

....

并调用它:

self.actions[(cond_1, cond_2)]()

如果你想优化选项的数量,以防你有不同的条件和类似的动作,看看卡诺图(它比看起来更容易):

http://en.wikipedia.org/wiki/Karnaugh_map

【讨论】:

  • 关于 K-maps 的好提示,从未想过它们(我已经很久没有看到它们了)。
  • 卡诺图 = 第一年 CS 噩梦
  • 其实我喜欢那些地图...非常简单有趣,可以简化复杂的布尔表达式。
【解决方案2】:

你应该never test a boolean variable with == True or == False。相反,仅使用布尔值就足够了。如果您想覆盖几乎所有真值组合,您可能还需要嵌套,如下所示:

if cond_1:
    if cond_2:
        do something
    else:
        do something else
elif cond_2:
    do this

如果数字进一步增加,我会建议一张地图,如下所示:

call_map = {
  (True, True, True): func1,
  (True, False, True): func2,
  (True, False, False): func3,
}
try:
  func = call_map[(cond_1, cond_2, cond_3)]
except KeyError:
  pass
else:
  func()

但是,请注意,大量不同的情况肯定是代码异味。几乎在任何情况下,您实际上都不需要这么多情况,并且实际上可以直接调用函数。您可能认为您的案例是一个例外,但很可能不是。为什么需要这么多箱子?

【讨论】:

  • “你永远不应该使用 == True 或 == False 来测试布尔变量”——除了可读性/样式问题之外,在比较中省略对 True 和 False 的明确提及也更安全。说 if x == True 是在测试 x 的值是否等于名为 True 的对象引用的值(可以重新定义,尽管 非常 这样做是不好的做法所以)。说if x: 是在测试在布尔上下文中评估的 x 是否评估为布尔值 True(这完全是预期的)。
【解决方案3】:

您正在寻找一种写出真值表的紧凑方法。 (您唯一的另一种选择是找到一种使用布尔代数简化表达式的方法;可能不存在简化的形式。)除了节省打字之外,没有其他方法可以简化:

def TruthTable(text):
    table = {}

    for line in text.splitlines():
        line = line.strip()
        if line:
            inputs,output = line.split()
            table[tuple(bool(int(x)) for x in inputs)] = bool(int(output))

    return lambda *inputs:table[inputs]

演示:

myFunc = TruthTable('''
    000 1
    001 0
    010 0
    011 1
    100 1
    101 0
    110 0
    111 1
''')

输出:

>>> myFunc(False, False, True)
False

如果您需要的不仅仅是布尔输出,您可以调整它以通过使用例如字典来引用任意表达式,并将键后处理为布尔元组:

{
    (0,0,0): <expr0>,
    (0,0,1): <expr1>,
    (0,1,0): <expr2>,
    (0,1,1): <expr3>,
    ...
}

您也可以使用二进制表示法(例如0b110 == 6)执行以下操作,但我觉得它不太干净:

{
    0b000: <expr0>,
    0b001: <expr1>,
    ...
}

您甚至可以只使用一个列表,然后将其转换为字典以进行快速查找(通过 dict((intToBinarytuple(i),expr) for i,expr enumerate(myList))):

[
             # ABC
    <expr0>, # 000
    <expr1>, # 001
    <expr2>, # 010
    <expr3>, # 011
    ...
]

旁注:万一您需要任意 python 命令,您可以像这样调度:

conditions = (True, False, True)

c = lambda *args: conditions==toBooleanTuple(args)
if c(0,0,0):
    ...
elif c(0,0,1):
    ...
elif c(0,1,0):
    ...
elif c(0,1,1):
    ...

【讨论】:

    【解决方案4】:

    我会使用 dict 以更清晰的方式将条件映射到操作。如果您真的需要为每种情况做一些不同的事情,那么可能没有比仅列举可能性更好的方法了。

    def do_something():
      pass
    
    def do_something_else():
      pass
    
    def do_this():
      pass
    
    
    do_dict = {(True, False): do_something, 
               (True, True): do_something_else,
               (False, True): do_this}
    
    # call it
    do_dict[(cond_1, cond_2)]()
    

    【讨论】:

      【解决方案5】:

      如果您的目标是避免编写大量“与”和布尔表达式,您可以使用素数和这样的一个条件(例如 2 个条件)

       cond = (2**cond_1)*(3**cond_2)
      

      所以

      cond == 1 #means cond_1 and cond_2 are False
      cond == 2 #means cond_1 is True and con_2 is False
      cond == 3 #means cond_1 is False and con_2 is True
      cond == 6 #means con_1 and Con_2 are True
      

      这个技巧可以用于使用 3 个素数的 3 个条件,依此类推

      【讨论】:

      • 你是对的,我错了,我的错 :( 猜猜这意味着我今晚应该停止审核。抱歉,这两个问题都已恢复并且(现在)误导性 cmets 已删除。
      • 对不起,我的意思是,您可以毫不费力地将素数概括为任意数量的条件,如果有什么抱歉,请告诉我您的决定以及我是否错了。
      • 不,我错了,我混淆了乘法和加法的使用,我是按照二进制值示例的思路思考的。你的几乎一样,但也不同,所以它有优点。忘记我曾经对你的两个答案嗤之以鼻:)
      【解决方案6】:

      您可以获取一个 n 大小的条件列表,并将其转换为单个值以进行比较,方法是将条件转换为 2 的倍数之和。

      >>> conditions = [True, False, True, True, False]
      >>> condition = sum(2**i * cond for i, cond in enumerate(conditions))
      >>> condition
      13
      

      条件的第一个值是2^0 = 1,第二个是2^1 = 2,第三个是2^2 = 4,...每个都乘以真值(1如果True,0如果False)。

      这还允许您查看条件的子集是否为真。如果您需要知道至少一部分条件是否为真,您可以使用二进制&amp; 来查找:

      >>> subset = lambda c, x: (c & x) == x
      >>> conditions = [True, False, True, True, False, True, True, True, True]
      >>> condition = sum(2**i * cond for i, cond in enumerate(conditions))
      >>> subset(condition, 13)
      True
      >>> subset(condition, 2)
      False
      

      要获得前面一些答案提供的功能,您还可以使用列表和字典来将条件映射到操作,而不是使用字典。您还可以使用列表实现类似defaultdict 的功能(尽管这使用 2^n 内存,并且可能不是最好的主意):

      def do_something():
          print("Hello World!")
      condition_action_map = [None] * (2 ** len(conditions))
      condition_action_map[13] = do_something
      

      如果您正在使用类似的内容,您可能希望对此进行彻底的评论。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-08
        • 2013-03-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多