Лучший способ объединить перестановку условных операторов - PullRequest
4 голосов
/ 29 марта 2019

Итак, мне нужно выполнить ряд действий, основанных на 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».

Ответы [ 8 ]

5 голосов
/ 29 марта 2019

Вы можете использовать карту наблюдений для результатов:

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)

Вы также можете использовать одно из решений для маскировки, чтобы сделать код короче, или если у вас слишком много дел для записи, но для небольших наборов случаев это явное представление будет более понятным и проще в обслуживании.

1 голос
/ 29 марта 2019

Это гибкое решение, которое предлагает масштабируемость и определенный уровень простоты.

Во-первых, вам нужно создать методы, которые будут запускаться для каждого выхода. Это «сложные» версии ваших 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.

1 голос
/ 29 марта 2019

вы можете получить номер вашего дела напрямую из бинарных манипуляций с вашими логическими значениями:

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

если вы посмотрите на ответ Джона Кугельмана , вы увидите, что x, y, z, t - это просто "биты" вашего номера дела (где True=0 и False=1) ... поэтому я создаю int установка этих битов (а затем добавьте 1, потому что вы начинаете считать с 1).

если нумерация произвольная, вы можете упростить ее до x << 3 | y << 2 | z << 1 | t и взять ее оттуда.

это легко расширяется на большее число логических переменных.

затем, чтобы сделать что-то на основе номера дела, я предлагаю вам создать словарь, содержащий case в качестве ключа и функцию или данные или что-либо в качестве значения. что-то вроде:

case_functions = {1: func_1, 2: func_2, ...}
res = case_functions(case)(some argument)
1 голос
/ 29 марта 2019

Вы можете засунуть все значения в кортеж и использовать 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 или использовать хитрые двоичные приемы упаковки, но преимущества здесь (а)это просто и читаемо;(б) нет необходимости в lambda с или функциях;и (c) вы можете положить что-нибудь в 16 случаев.

0 голосов
/ 29 марта 2019

Распространение ответа @ 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

0 голосов
/ 29 марта 2019

Я думаю, что это хорошее место для реестра обработчиков.Это не даст вам самый короткий код, но я думаю, что это даст вам код, который легче читать и легче обслуживать, что является одной из интерпретаций «более простого».Я бы сделал что-то вроде этого:

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)

и, конечно, добавьте аргументы в обработчики.

0 голосов
/ 29 марта 2019

Когда таких случаев много, я обычно предпочитаю писать вспомогательные функции, облегчающие поддержку кода, например ::10000

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.

, что значительно упрощает проверку того, что вы рассмотрели все дела.

0 голосов
/ 29 марта 2019

Просто используйте двоичные значения True и False:

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