Generating Evalable Python Code: All Combinations Of Functions In Disjunctive Normal Form
Solution 1:
This is equivalent to taking the outer/cartesian product, "summing" across the "var" dimension, and interspersing those with the outer product of logic operators. You can use itertools.product
or just the normal list comprehensions. The following will work for any number of variables, functions, comparators, logic operators, and numeric thresholds. It is also easily extensible if you choose to make more complicated expressions:
#!/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)
Result:
['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']
Solution 2:
Maybe this can summaries what you are trying to do (using python2 syntax):
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
...
In this method i just cheated by replacing vars_
with ('A', 'B', 'C'). beside that i think it should work.
If you don't like my way of cheating by hard coding the the vars_
list and the funcs_
name you can get the name of your variable from the globals dictionary something like this:
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
Solution 3:
First of all, I would start by saying eval is a bad idea.There is always another way to do it.
Answer to your question:
Question 1:
Function name,
You can get using f.func_name.
Variable name,
Not so simple,but this should work,
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]
It doesn't matter if it returns the wrong name since their value will be equal.
The operator and vol_limit is easily generated.
2nd Question.
No obvious solution out of mind. Iterate through all of them?
3rd Question.
Yes it is possible. Check out Decorators.
Post a Comment for "Generating Evalable Python Code: All Combinations Of Functions In Disjunctive Normal Form"