Генерация корректного кода Python: все комбинации функций в дизъюнктивной нормальной форме - PullRequest
3 голосов
/ 03 июня 2011
(A,B,C) = (100, 200, 300)

def f1(p): return p+50 
def f2(p): return p*1.5 
def f3(p): return p*p 

vars_ = (A,B,C)
funcs_ = [f1, f2, f3]
logic_ = ["and","or"]
vol_lmt_ = [200, 300]
op_ = [">","<","="] 

Я хочу сгенерировать строку кода подтверждения для eval (), чтобы проверить правильность, например, ниже 1:

"f1(A)>200 and f1(B)>200 and f1(C)>200"                             # True
 -^-------------^-------------^------------: funcs_
 ----^-------------^-------------^---------: vars_
 ------^-------------^-------------^-------: op_
 --------^-------------^-------------^-----: vol_lmt_        
 ------------^-------------^---------------: logic_

Мои вопросы:

  1. как создать строку кода, которую я хотел, основываясь на этих переменных выше?

  2. как перечислить все возможности тестовой логики для выше (A,B,C)? Например:

    "f1(A)>200 and f1(B)>200 and f1(C)>200"
    "f1(A)<300 and f2(B)=200 or f3(C)>200"

  3. возможно ли заменить имя функции на запись списка при генерации кода?

    "f(A)>200 and f1(B)>200 and f1(C)>200"

    К

    "funcs_[0](A)>200 and funcs_[0](B)>200 and funcs_[0](C)>200"

Ответы [ 3 ]

1 голос
/ 03 июня 2011

Это эквивалентно взятию внешнего / декартового произведения, «суммированию» по измерению «var» и перемежению с внешним произведением логических операторов.Вы можете использовать itertools.product или просто обычные списки. Следующее будет работать для любого числа переменных, функций, компараторов, логических операторов и числовых порогов.Это также легко расширяется, если вы решите сделать более сложные выражения :

#!/usr/bin/python3

from pprint import pprint as pp
from itertools import *

VARS = 'XYZ'
FUNCS = range(2)
COMPARE = '><='
LOGIC = ['and', 'or']
NUMS = [200, 300]

def listJoin(iter):
    return sum(map(list,iter), [])

terms = [
    [
         'func[{func}]({var}){compare}{num}'.format(func=func, var=var, compare=compare, num=num)
         for var in VARS
    ]
    for func in FUNCS
    for compare in COMPARE
    for num in NUMS
]

def intersperse(iter, joiners):
    iter = list(iter)
    for tokens in product(*(joiners for _ in iter[:-1])):
        yield ' '.join(listJoin(zip(iter,tokens))+[iter[-1]])

formulas = listJoin(intersperse(t, LOGIC) for t in terms)

pp(formulas)

Результат:

['func[0](X)>200 and func[0](Y)>200 and func[0](Z)>200',                                                                                               
 'func[0](X)>200 and func[0](Y)>200 or func[0](Z)>200',
 'func[0](X)>200 or func[0](Y)>200 and func[0](Z)>200',
 'func[0](X)>200 or func[0](Y)>200 or func[0](Z)>200',
 'func[0](X)>300 and func[0](Y)>300 and func[0](Z)>300',
 'func[0](X)>300 and func[0](Y)>300 or func[0](Z)>300',
 'func[0](X)>300 or func[0](Y)>300 and func[0](Z)>300',
 'func[0](X)>300 or func[0](Y)>300 or func[0](Z)>300',
 'func[0](X)<200 and func[0](Y)<200 and func[0](Z)<200',
 'func[0](X)<200 and func[0](Y)<200 or func[0](Z)<200',
 'func[0](X)<200 or func[0](Y)<200 and func[0](Z)<200',
 'func[0](X)<200 or func[0](Y)<200 or func[0](Z)<200',
 'func[0](X)<300 and func[0](Y)<300 and func[0](Z)<300',
 'func[0](X)<300 and func[0](Y)<300 or func[0](Z)<300',
 'func[0](X)<300 or func[0](Y)<300 and func[0](Z)<300',
 'func[0](X)<300 or func[0](Y)<300 or func[0](Z)<300',
 'func[0](X)=200 and func[0](Y)=200 and func[0](Z)=200',
 'func[0](X)=200 and func[0](Y)=200 or func[0](Z)=200',
 'func[0](X)=200 or func[0](Y)=200 and func[0](Z)=200',
 'func[0](X)=200 or func[0](Y)=200 or func[0](Z)=200',
 'func[0](X)=300 and func[0](Y)=300 and func[0](Z)=300',
 'func[0](X)=300 and func[0](Y)=300 or func[0](Z)=300',
 'func[0](X)=300 or func[0](Y)=300 and func[0](Z)=300',
 'func[0](X)=300 or func[0](Y)=300 or func[0](Z)=300',
 'func[1](X)>200 and func[1](Y)>200 and func[1](Z)>200',
 'func[1](X)>200 and func[1](Y)>200 or func[1](Z)>200',
 'func[1](X)>200 or func[1](Y)>200 and func[1](Z)>200',
 'func[1](X)>200 or func[1](Y)>200 or func[1](Z)>200',
 'func[1](X)>300 and func[1](Y)>300 and func[1](Z)>300',
 'func[1](X)>300 and func[1](Y)>300 or func[1](Z)>300',
 'func[1](X)>300 or func[1](Y)>300 and func[1](Z)>300',
 'func[1](X)>300 or func[1](Y)>300 or func[1](Z)>300',
 'func[1](X)<200 and func[1](Y)<200 and func[1](Z)<200',
 'func[1](X)<200 and func[1](Y)<200 or func[1](Z)<200',
 'func[1](X)<200 or func[1](Y)<200 and func[1](Z)<200',
 'func[1](X)<200 or func[1](Y)<200 or func[1](Z)<200',
 'func[1](X)<300 and func[1](Y)<300 and func[1](Z)<300',
 'func[1](X)<300 and func[1](Y)<300 or func[1](Z)<300',
 'func[1](X)<300 or func[1](Y)<300 and func[1](Z)<300',
 'func[1](X)<300 or func[1](Y)<300 or func[1](Z)<300',
 'func[1](X)=200 and func[1](Y)=200 and func[1](Z)=200',
 'func[1](X)=200 and func[1](Y)=200 or func[1](Z)=200',
 'func[1](X)=200 or func[1](Y)=200 and func[1](Z)=200',
 'func[1](X)=200 or func[1](Y)=200 or func[1](Z)=200',
 'func[1](X)=300 and func[1](Y)=300 and func[1](Z)=300',
 'func[1](X)=300 and func[1](Y)=300 or func[1](Z)=300',
 'func[1](X)=300 or func[1](Y)=300 and func[1](Z)=300',
 'func[1](X)=300 or func[1](Y)=300 or func[1](Z)=300']
0 голосов
/ 03 июня 2011

Может быть, это может резюмировать, что вы пытаетесь сделать (используя синтаксис python2):

import itertools

arguments = ('A', 'B', 'C', 'D')
funcs_ = [f1, f2, f3, f4]
logic_ = ["and","or"]
op_ = [">","<","="]
vol_lmt_ = [200, 300]

num_func = len(funcs_)

assert num_func == len(arguments), ("The number of argument should be the same as "
                                    "the number of function.")

operands = itertools.product(["funcs_[%d]" % i for i in range(num_func)],
                             arguments,
                             op_,
                             vol_lmt_)

def comp(operands):
    templ = "{func}({arg}){op}{val}"
    for operand in operands:
        yield templ.format(func=operand[0], arg=operand[1],
                           op=operand[2], val=operand[3])

new_operands = map(comp, itertools.tee(operands, num_func))

# construct the argument to pass to itertools.product.
args = []
for operand in new_operands:
    args.append(operand)
    args.append(logic_)

args.pop() # Remove the last logic operator.

res = itertools.product(*args)

print " ".join(res.next())
# funcs_[0](A)>200 and funcs_[0](A)>200 and funcs_[0](A)>200 and funcs_[0](A)>200

...

В этом методе я просто обманул, заменив vars_ на ('A', 'B', 'C'). кроме того, я думаю, что это должно работать.


Если вам не нравится мой способ мошенничества путем жесткого кодирования списка vars_ и имени funcs_, вы можете получить имя вашей переменной из словаря globals примерно так:

def get_name(obj):
    """Get the name of an object (variable) from the globals dict.

    Argument:
       - obj : The variable that we want to get the name of. 

    Return:
       - A string representing the name of the object if it was found else return None.

    """

    for name, value in globals().items():
         if value is obj:
             return name
0 голосов
/ 03 июня 2011

Прежде всего, я бы сказал, что eval - плохая идея. Всегда есть другой способ сделать это.

Ответ на ваш вопрос:

Вопрос 1:

Имя функции,

Вы можете использовать f.func_name.

Имя переменной,

Не так просто, но это должно работать,

import gc, sys

def find_names(obj):
    frame = sys._getframe()
    for frame in iter(lambda: frame.f_back, None):
        frame.f_locals
    result = []
    for referrer in gc.get_referrers(obj):
        if isinstance(referrer, dict):
            for k, v in referrer.iteritems():
                if v is obj:
                    result.append(k)
    return result

a = 97
print find_names(a)[0]

Не имеет значения, возвращает ли оно неправильное имя, поскольку их значение будет равно.

Оператор и vol_limit легко генерируются.

2-й вопрос.

Нет очевидного решения из головы. Перебрать все из них?

3-ий вопрос.

Да, это возможно. Выезд Декораторы .

...