【问题标题】:Best way to combine a permutation of conditional statements组合条件语句排列的最佳方法
【发布时间】:2019-08-20 17:05:08
【问题描述】:

所以,我有一系列动作要执行,基于 4 个条件变量 - 比如说 x、y、z 和 t。这些变量中的每一个都有一个可能的 True 或 False 值。因此,总共有 16 种可能的排列。而且我需要为每个排列执行不同的操作。

什么是最好的方法而不是制作一个巨大的 if-else 结构。

让我们看一个简化的例子。如果我尝试将所有不同的排列包含到一个大型 if-else 构造中,这就是我的代码的外观。

if (x == True):
    if (y == True):
        if (z == True):
            if (t == True):
                print ("Case 1")
            else:
                print ("Case 2")
        else:
            if (t == True):
                print ("Case 3")
            else:
                print ("Case 4")
    else:
        if (z == True):
            if (t == True):
                print ("Case 5")
            else:
                print ("Case 6")
        else:
            if (t == True):
                print ("Case 7")
            else:
                print ("Case 8")
else:
    if (y == True):
        if (z == True):
            if (t == True):
                print ("Case 9")
            else:
                print ("Case 10")
        else:
            if (t == True):
                print ("Case 11")
            else:
                print ("Case 12")
    else:
        if (z == True):
            if (t == True):
                print ("Case 13")
            else:
                print ("Case 14")
        else:
            if (t == True):
                print ("Case 15")
            else:
                print ("Case 16")

有什么办法可以简化吗?显然,我对每个案例的目标都比打印“案例 1”要复杂。

【问题讨论】:

  • 二进制数从0到15?你的代码相当于print("Case " + str(8*(x==True) + 4*(y==True) + 2*(z==True) + 1*(t == True)))
  • 在一个 if-else 语句中不嵌套并检查所有 4 个值 16 次可能更容易。
  • 通常使用if x:而不是if (x == True):更合适
  • @JohnColeman 不错的解决方案。不过,您不需要在每个变量上都添加 '==True' 部分。
  • @JohnKugelman 当然——但情况 确实 对应于 0-15 中的数字。可以计算该数字,然后分支到案例。

标签: python if-statement conditional combinations


【解决方案1】:

您可以使用案例映射到结果:

cases = { (True,  True,  True,  True):  "Case 1",
      (True,  True,  True,  False): "Case 2",
      (True,  True,  False, True): "Case 3",
      (True,  True,  False, False):"Case 4",
      (True,  False, True,  True): "Case 5",
      (True,  False, True,  False):"Case 6",
      (True,  False, False, True): "Case 7",
      (True,  False, False, False):"Case 8",
      (False, True,  True,  True): "Case 9",
      (False, True,  True,  False):"Case 10",
      (False, True,  False, True): "Case 11",
      (False, True,  False, False):"Case 12",
      (False, False, True,  True): "Case 13",
      (False, False, True,  False):"Case 14",
      (False, False, False, True): "Case 15",
      (False, False, False, False):"Case 16"}

print(cases[(x,y,z,t])

如果您想为每种情况做一些其他/不同的事情,您可以向该地图添加一个函数。

cases = { (True,  True,  True,  True):  foo_func,
      (True,  True,  True,  False): bar_func,
         ...}

result = cases[(x,y,x,t)](*args)

您也可以使用其中一种掩码解决方案来缩短代码,或者如果您有太多案例要写出来,但对于较小的案例集,这种显式表示会更清晰且更易于维护。

【讨论】:

  • 方括号和括号应该在代码的最后一行交换:result = cases[(x,y,x,t)](*args)
【解决方案2】:

您可以直接从布尔值的二进制操作中获取您的案例编号:

case = (x^1) << 3 | (y^1) << 2 | (z^1) << 1 | (t^1) + 1
print(f'Case {case}')

如果您查看John Kugelman's answer,您会发现x, y, z, t 只是您的案例编号的“位”(其中True=0False=1)...所以我构造了一个int 设置这些位(然后添加1,因为您从1开始计数。

如果编号是任意的,您可以将其简化为 x &lt;&lt; 3 | y &lt;&lt; 2 | z &lt;&lt; 1 | t 并从那里获取。

这很容易扩展到更多的布尔变量。

然后根据案例编号做一些事情,我建议您创建一个字典,其中包含 case 作为键和函数或数据或任何值作为值。类似:

case_functions = {1: func_1, 2: func_2, ...}
res = case_functions(case)(some argument)

【讨论】:

    【解决方案3】:

    您可以将所有值放入一个元组并使用 16 个元组比较。

    if   (x, y, z, t) == (True,  True,  True,  True):  print("Case 1")
    elif (x, y, z, t) == (True,  True,  True,  False): print("Case 2")
    elif (x, y, z, t) == (True,  True,  False, True):  print("Case 3")
    elif (x, y, z, t) == (True,  True,  False, False): print("Case 4")
    elif (x, y, z, t) == (True,  False, True,  True):  print("Case 5")
    elif (x, y, z, t) == (True,  False, True,  False): print("Case 6")
    elif (x, y, z, t) == (True,  False, False, True):  print("Case 7")
    elif (x, y, z, t) == (True,  False, False, False): print("Case 8")
    elif (x, y, z, t) == (False, True,  True,  True):  print("Case 9")
    elif (x, y, z, t) == (False, True,  True,  False): print("Case 10")
    elif (x, y, z, t) == (False, True,  False, True):  print("Case 11")
    elif (x, y, z, t) == (False, True,  False, False): print("Case 12")
    elif (x, y, z, t) == (False, False, True,  True):  print("Case 13")
    elif (x, y, z, t) == (False, False, True,  False): print("Case 14")
    elif (x, y, z, t) == (False, False, False, True):  print("Case 15")
    elif (x, y, z, t) == (False, False, False, False): print("Case 16")
    

    这可以转换为 dict 查找或使用巧妙的二进制打包技巧,但这里的优点是 (a) 它简单易读; (b) 不需要lambdas 或函数; (c) 你可以在这 16 个箱子里放任何东西。

    【讨论】:

    • 你可以先将元组保存到一个变量中,以避免多次重复 lhs...
    【解决方案4】:

    这是一个灵活的解决方案,提供可扩展性和一定程度的简单性。

    首先,您需要创建按输出运行的方法。这些是您的 print("case X") 语句的“复杂”版本

    #Define your method outcomes here...
    #Note that this follows a binary layout starting with 
    # a + b + c + d = false
    def action1():      #binary 0 (a'b'c'd')
        print("case 1")
    def action2():      #binary 1 (a'b'c'd)
        print("case 2")
    def action3():      #binary 2 (a'b'cd')
       print("case 3")
    def action4():      #binary 3 (a'b'cd)
        print("case 4")
    def action5():      #binary 4 (a'bc'd')
        print("case 5") #etc...
    def action6():
        print("case 6")
    def action7():
        print("case 7")
    def action8():
        print("case 8")
    def action9():
        print("case 9")
    def action10():
        print("case 10")
    def action11():
        print("case 11")
    def action12():
        print("case 12")
    def action13():
        print("case 13")
    def action14():
        print("case 14")
    def action15():
        print("case 15")
    def action16():
        print("case 16")
    def actionDefault():
        print("Error!")
    

    然后,您可以在以后轻松引用这些特定的操作方法,方法是创建一个方法名称列表,然后创建一个方法以在调用时引用该方法列表。

    import itertools #Generates all permutations
    import sys       #Allows us to get the current module
    
    #Returns the index of the actionList we should execute
    def evaluateActionIndex(varList): 
        allcombinations = itertools.product([False, True], repeat=len(varList))
        i = 0
        for subset in allcombinations: #for each of the possible combinations...
            if list(subset) == varList: #Check to see if we want to execute this index.
                return i
            i = i + 1                  #Increment the target index
        return -1                      #Execute default method (-1 index)
    
    def performAction(index):
        actionList = [action1.__name__, action2.__name__, action3.__name__, action4.__name__, 
                      action5.__name__, action6.__name__, action7.__name__, action8.__name__,
                      action9.__name__, action10.__name__, action11.__name__, action12.__name__,
                      action13.__name__, action14.__name__, action15.__name__, action16.__name__,
                      actionDefault.__name__]
        method = getattr(sys.modules[__name__], actionList[index])  #Get method by name
        method()                                                    #Execute Method
    

    我们可以通过以下方式执行一些操作:

    #Mock up some control inputs
    a = False
    b = True
    c = False
    d = False
    controlVariables = [a, b, c, d] #All Your Control Variables
    
    #Execute control sequence
    performAction(evaluateActionIndex(controlVariables))
    

    我已经对此进行了测试,并且效果很好。您可以根据需要向controlVariables 列表中添加任意数量的控制变量。

    【讨论】:

      【解决方案5】:

      这真是太棒了。位!很干净。

      我一直在寻找解决这个问题的方法。

      这是一个 javascript 版本:

      //assuming you have your variables in an array
      let q = evaluatedQuery = ["wd:Q82955", "wd:Q212238", "", "wd:Q116"]
      
      //lenght of the binary string
      let possibleCases = evaluatedQuery.length
      let binaryCase = ""
      
      
      for (let i = 0; i < possibleCases; i++) {
      
          // this "!!" makes a value truthy or falsy,
          // and converts that to an integer "!!q[i] ^ 0"
      
          binaryCase = `${binaryCase}${!!q[i] ^ 0}`
      
      }
      
      //this finds out which of (q*q = 16) cases its gonna be
      let parsedBinaryCase = parseInt(binaryCase, 2) + 1
      
      //this converts it to an array for easy handling
      let binaryCaseArr = binaryCase.split("")
      
      //this filers out falsy values by taking falsy values index
      let boundQueryElements = evaluatedQuery.filter((el, i) => {
          return !binaryCaseArr[i] != !!el ^ 0 
      })
      
      console.log(binaryCase) //output: 1101
      console.log(parsedBinaryCase) //output: 14
      console.log(boundQueryElements) //output: ['wd:Q82955','wd:Q212238','wd:Q116']
      
      //and this is a clean way to handle those 16 cases
      //in this example it would go to case 14
      switch (parsedBinaryCase) {
          case 1:
              break
          case 2:
              break
          case 3:
              break
          case 4:
              break
          case 5:
              break
          case 6:
              break
          case 7:
              break
          case 8:
              break
          case 9:
              break
          case 10:
              break
          case 11:
              break
          case 12:
              break
          case 13:
              break
          case 14:
           // for (let el in boundQueryElements) {
           // }
              break
          case 15:
              break
          case 16:
              break
          default:
      }
      
      

      它就像“扁平化”树结构。

      【讨论】:

        【解决方案6】:

        只需使用 TrueFalse 值的二进制性:

        x = True
        y = True
        z = True
        t = True
        total = bin(x + 2 * y + 4 * z + 8 * t)
        print(total)
        print(int(total, 2))
        

        输出:

        0b1111

        15

        x = False
        y = True
        z = False
        t = True
        total = bin(x + 2 * y + 4 * z + 8 * t)
        print(total)
        print(int(total, 2))
        

        产量:

        0b1010

        10

        现在您可以轻松地使用int(total, 2) 值来确定您正在处理的情况

        因此您可以将代码转换为单级缩进:

        case = int(total, 2)
        if case == 0:
            print('case 0')
        elif case == 1:
            print('case 1')
        elif case == 2:
            print('case 2')
        ...
        

        【讨论】:

          【解决方案7】:

          当有这么多情况时,我通常更喜欢编写帮助函数,使代码更易于维护,例如:

          def compare(xyzt, binaryval):
              boolval = tuple(digit == '1' for digit in binaryval)
              return all(a == b for a, b in zip(xyzt, boolval))
          

          那么你的 if 语句可以写成:

          xyzt = (x, y, z, t)
          if   compare(xyzt, '1111'): ...
          elif compare(xyzt, '1110'): ...
          elif compare(xyzt, '1100'): ...
          etc.
          

          这样更容易验证您是否考虑了所有情况。

          【讨论】:

            【解决方案8】:

            我认为这是一个注册处理程序的好地方。这不会为您提供 最短 代码,但我认为它为您提供更易于阅读和更易于维护的代码,这是对“更简单”的一种解释。我会做这样的事情:

            registry.py

            ​​>
            handlers = dict()
            def register(x, y, z, t):
                if (x, y, z, t) in handlers:
                    raise ValueError("Handler already registered for {}/{}/{}/{}".format(
                            x, y, z, t))
                def insert(f):
                    handlers[(x, y, z, t)] = f
                return insert
            
            def handle(x, y, z, t):
                if (x, y, z, t) not in handlers:
                    raise KeyError("No handler registered for {}/{}/{}/{}".format(
                            x, y, z, t))
                return handlers[(x, y, z, t)]()
            

            handlers.py

            ​​>
            from delegation import register, handle
            
            @register(x=True, y=True, z=False, t=True)
            def some_descriptive_name():
                print("hi!")
            
            @register(x=True, y=False, z=True, t=False)
            def another_good_name():
                print("Yes hello.")
            
            # etc.
            

            main.py

            ​​>
            from handlers import handle
            
            x, y, z, t = True, False, False, True
            handle(x, y, z, t)
            

            这可以让您准确地看到每个处理程序将被激活的条件。将您的处理程序分成它们自己的函数也可以进行更清洁的测试。我添加了一项检查,以确保您不会多次尝试处理相同的条件,并在未处理一组条件时显示错误消息。添加检查以确保所有案例也得到处理是很容易的。

            如果您的操作需要使用变量(除了四个条件),您也可以这样做;只需像这样更改handle 的签名和返回值:

            def handle(x, y, z, t, *args, **kwargs):
                ...
                return handlers[(x, y, z, t)](*args, **kwargs)
            

            当然,还要为处理程序添加参数。

            【讨论】:

              【解决方案9】:

              延续@Reedinationer 的回答:

              # your functions
              def f0(): print('case 1')
              def f1(): print('case 2')
              def f2(): print('case 3')
              #.
              #.
              def f15(): print('case 16')
              
              list_of_functions = [f0, f1, f2] # assuming there are 16 functions in total
              
              x = False
              y = False
              z = False
              t = False
              total = bin(x + 2 * y + 4 * z + 8 * t)
              index = int(total, 2)
              
              list_of_functions[index]() # will print('case 1')
              
              

              在 python 2.7 和 3.7 上测试

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2013-09-10
                • 2018-06-06
                • 1970-01-01
                • 2011-11-28
                • 1970-01-01
                相关资源
                最近更新 更多