Как создать функцию с произвольными параметрами на основе произвольной строки - PullRequest
0 голосов
/ 20 июня 2019

Моя конечная цель: я хочу создать набор таблиц истинности, где каждая таблица истинности соответствует произвольно определенному логическому выражению, которое изначально хранится в виде строки (например: «(var_1 и не var_2) или var_3» ). Строка может иметь любое количество операторов.

Это легко достижимо, если я имею в виду конкретное логическое выражение:

def evaluator(var_1,var_2,var_3):
    return  (var_1 and not var_2) or var_3

def truth_table(f):
    values = [list(x) + [f(*x)] for x in product([False,True], repeat=f.__code__.co_argcount)]
    return pd.DataFrame(values,columns=(list(f.__code__.co_varnames) + [f.__name__]))

one_truth_table = truth_table(evaluator)

Но я хочу сделать это для любой функции с любым количеством параметров, с любым типом логического выражения. Я буду перебирать логические выражения в виде строк, чтобы создать серию таблиц истинности.

Я боролся с этим весь день. Если бы я мог заставить этот фрагмент кода вести себя так, как я хочу, то моя проблема была бы решена.

def temp_func(boolean_expression_string,variable_names_list):
    return eval(boolean_expression_string)

# i have two strings: '(var_1 and var_2) and (var_3 or not var_4) or var_etc'
# and also: 'var_1,var_2,var_3,var_4,var_etc'

temp_func('(var_1 and var_2) and (var_3 or not var_4) or var_etc', input(list(eval('var_1,var_2,var_3,var_4,var_etc'))))

Запуск этого результата:

NameError: name 'var_1' is not defined

Я включил всю предысторию на случай, если тупо подойду ко всей проблеме. Хотя вы можете догадаться, что я просто пытаюсь заставить его работать, элегантность не мой главный приоритет на данный момент.

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

1 Ответ

0 голосов
/ 20 июня 2019

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

Новый ответ

Проще попросить прощения, чем получить разрешение.

Идея состоит в том, чтобы найти все имена переменных, пытаясь вычислить выражение и поймав NameError.Обратите внимание, что нам нужно сгенерировать все присвоения переменных для списка переменных, потому что python будет короткое замыкание оценка or и and.Например, в var_1 or var_2 мы не найдем var_2, если var_1 инициализирован в True.

def variable_names(expression):
    # list of found variables
    variables = list()
    while True:
        try:
            # generate all assignments for current variable names
            assignments = [
                {variables[i]: v for i, v in enumerate(vs)}
                for vs in itertools.product(
                    [True, False], repeat=len(variables)
                )
            ]
            # try to evaluate them all
            for assignment in assignments:
                eval(expression, None, assignment)
            # all of them work, can return
            return variables
        except NameError as e:
            # get next variable
            variables.append(
                re.match("name '(.+)' is not defined", str(e)).group(1)
            )

Затем мы создадим список словарей назначений - точно так же, как в предыдущем методе- и передайте это eval, добавив результат в словарь.Затем можно создать DataFrame из записей.

def truth_table(expression):
    # get variable names
    variables = variable_names(expression)
    # make list of assignments
    assignments = [
        {variables[i]: v for i, v in enumerate(vs)}
        for vs in itertools.product([True, False], repeat=len(variables))
    ]
    # get truthy values
    values = [
        {**assignment, **{"value": eval(expression, None, assignment)}}
        for assignment in assignments
    ]
    # make dataframe from records and supply column order
    return pd.DataFrame.from_records(values, columns=variables + ["value"])

Старый ответ - фиксированное именование переменных.

Если все ваши переменные имеют имена var_1, var_2, ... youможет дать список присваиваний их значению evaluator и проанализировать их в словарь

def evaluator(expression, values):
    return eval(
        expression,
        None,
        {"var_{}".format(i + 1): v for i, v in enumerate(values)},
    )

и запустить его следующим образом

evaluator( 
    "(var_1 and var_2) and (var_3 or not var_4)", 
    [True, False, True, False] 
)                                                                                        

, который возвращает False.

Полный код для вычисления таблицы истинности из выражения равен

def truth_table(expression):
    # get variable names
    varnames = set(re.findall(r"(var_\d+)", expression))
    # sort by index
    varnames = sorted(varnames, key=lambda x: int(x.split("_")[1]))
    # get truthy values
    values = [
        list(x) + [evaluator(expression, x)]
        for x in itertools.product([True, False], repeat=len(varnames))
    ]
    return pd.DataFrame(values, columns=varnames + ["T/F"])

Если у вас есть дыр в списке переменных - например, (var_1 and var_3) - вам понадобитсялибо переименовать их перед вызовом оценщика, либо изменить оценщик на словарь.

...