如何编写所有2 ^ N布尔条件的if语句(python)

时间:2012-03-01 23:02:07

标签: python algorithm

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

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个语句)..我担心这个数字将来会增加。 这是检查这些条件的更好方法吗?

6 个答案:

答案 0 :(得分:9)

你是否需要总是写出所有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

答案 1 :(得分:7)

你应该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()

然而,请注意,大量不同的案例是确定的代码味道。实际上,在任何情况下,您实际上并不需要这么多情况,并且实际上可以直接调用函数。您可能认为您的案例是例外,但可能不是。你为什么需要这么多案件?

答案 2 :(得分:5)

您正在寻找一种写出真值表的简洁方法。 (你唯一的另一种选择是找到一种使用布尔代数简化表达式的方法;可能不存在简化形式。)除了节省输入外,没有办法使这更简单:

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

答案 3 :(得分: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)]()

答案 4 :(得分:4)

如果你的目标是避免编写大量的“ands”和布尔表达式,你可以使用素数而只有一个这样的条件(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

这个hack可以用于3个条件,使用3个素数等等

答案 5 :(得分:3)

您可以采用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,...每个都乘以真值(如果True则为1, 0如果False)。

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

>>> 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

如果你使用的是这样的话,你可能想彻底评论这个。