2010-03-18 18 views
14

Tengo algunas expresiones lógicas booleanas generadas de forma dinámica, como:evaluar dinámicamente lógica booleana simple en Python

  • (A o B) y (C o D)
  • A o (A y B)
  • Un
  • vacío - se evalúa como TRUE

Los marcadores de posición son reemplazados con booleanos. En caso de que,

  1. Convertir esta información a una expresión de Python como True or (True or False)eval y que?
  2. Crear un árbol binario donde un nodo es un objeto bool o Conjunction/Disjunction y evaluarlo recursivamente?
  3. ¿Convertirlo en expresiones S anidadas y usar un analizador Lisp?
  4. ¿Algo más?

Sugerencias de bienvenida.

Respuesta

10

No debería ser difícil escribir un evaluador que pueda manejar esto, por ejemplo usando pyparsing. ¿Solo tiene unas pocas operaciones para manejar (y, o, y agrupación?), Por lo que debería poder analizarlas y evaluarlas usted mismo.

No debería necesitar formar explícitamente el árbol binario para evaluar la expresión.

+3

Este ejemplo de pyparsing (http://pyparsing.wikispaces.com/file/view/simpleBool.py) debería ser casi una solución directa. – PaulMcG

+0

Voy a aceptar esta respuesta, ya que no es tan aterradora como 'eval()' y es la más fácil de ampliar. –

8

Si configura los dicts con los lugareños y globales que le interesan, debe poder pasarlos con seguridad junto con la expresión en eval().

+0

No es necesario utilizar 'eval' aquí; solo necesita evaluar un lenguaje muy simple, no Python. (Además, limitar lo que pasas a 'eval' para los locales/globales no lo hace seguro si terminas queriendo pasar mucho, y ciertamente no evita cálculos imposiblemente grandes.) –

+0

+1: Solo crea un diccionario y uso eval. –

17

Así es un pequeño módulo (posiblemente, 74 líneas, incluyendo espacios en blanco) he construido en aproximadamente una hora y media (más casi una hora para refactorización):

str_to_token = {'True':True, 
       'False':False, 
       'and':lambda left, right: left and right, 
       'or':lambda left, right: left or right, 
       '(':'(', 
       ')':')'} 

empty_res = True 


def create_token_lst(s, str_to_token=str_to_token): 
    """create token list: 
    'True or False' -> [True, lambda..., False]""" 
    s = s.replace('(', ' (') 
    s = s.replace(')', ') ') 

    return [str_to_token[it] for it in s.split()] 


def find(lst, what, start=0): 
    return [i for i,it in enumerate(lst) if it == what and i >= start] 


def parens(token_lst): 
    """returns: 
     (bool)parens_exist, left_paren_pos, right_paren_pos 
    """ 
    left_lst = find(token_lst, '(') 

    if not left_lst: 
     return False, -1, -1 

    left = left_lst[-1] 

    #can not occur earlier, hence there are args and op. 
    right = find(token_lst, ')', left + 4)[0] 

    return True, left, right 


def bool_eval(token_lst): 
    """token_lst has length 3 and format: [left_arg, operator, right_arg] 
    operator(left_arg, right_arg) is returned""" 
    return token_lst[1](token_lst[0], token_lst[2]) 


def formatted_bool_eval(token_lst, empty_res=empty_res): 
    """eval a formatted (i.e. of the form 'ToFa(ToF)') string""" 
    if not token_lst: 
     return empty_res 

    if len(token_lst) == 1: 
     return token_lst[0] 

    has_parens, l_paren, r_paren = parens(token_lst) 

    if not has_parens: 
     return bool_eval(token_lst) 

    token_lst[l_paren:r_paren + 1] = [bool_eval(token_lst[l_paren+1:r_paren])] 

    return formatted_bool_eval(token_lst, bool_eval) 


def nested_bool_eval(s): 
    """The actual 'eval' routine, 
    if 's' is empty, 'True' is returned, 
    otherwise 's' is evaluated according to parentheses nesting. 
    The format assumed: 
     [1] 'LEFT OPERATOR RIGHT', 
     where LEFT and RIGHT are either: 
       True or False or '(' [1] ')' (subexpression in parentheses) 
    """ 
    return formatted_bool_eval(create_token_lst(s)) 

Las pruebas simples dan:

>>> print nested_bool_eval('') 
True 
>>> print nested_bool_eval('False') 
False 
>>> print nested_bool_eval('True or False') 
True 
>>> print nested_bool_eval('True and False') 
False 
>>> print nested_bool_eval('(True or False) and (True or False)') 
True 
>>> print nested_bool_eval('(True or False) and (True and False)') 
False 
>>> print nested_bool_eval('(True or False) or (True and False)') 
True 
>>> print nested_bool_eval('(True and False) or (True and False)') 
False 
>>> print nested_bool_eval('(True and False) or (True and (True or False))') 
True 

[parcialmente fuera de tema posiblemente]

Nota, la que puede fácilmente configure los tokens (tanto operandos como operadores) que usa con los medios de inyección de dependencia de personas pobres provistos (token_to_char=token_to_char y amigos) para tener múltiples evaluadores diferentes al mismo tiempo (solo restablecer los valores globales "inyectados por defecto" lo dejará con un solo comportamiento).

Por ejemplo:

def fuzzy_bool_eval(s): 
    """as normal, but: 
    - an argument 'Maybe' may be :)) present 
    - algebra is: 
    [one of 'True', 'False', 'Maybe'] [one of 'or', 'and'] 'Maybe' -> 'Maybe' 
    """ 
    Maybe = 'Maybe' # just an object with nice __str__ 

    def or_op(left, right): 
     return (Maybe if Maybe in [left, right] else (left or right)) 

    def and_op(left, right): 
     args = [left, right] 

     if Maybe in args: 
      if True in args: 
       return Maybe # Maybe and True -> Maybe 
      else: 
       return False # Maybe and False -> False 

     return left and right 

    str_to_token = {'True':True, 
        'False':False, 
        'Maybe':Maybe, 
        'and':and_op, 
        'or':or_op, 
        '(':'(', 
        ')':')'} 

    token_lst = create_token_lst(s, str_to_token=str_to_token) 

    return formatted_bool_eval(token_lst) 

da:

>>> print fuzzy_bool_eval('') 
True 
>>> print fuzzy_bool_eval('Maybe') 
Maybe 
>>> print fuzzy_bool_eval('True or False') 
True 
>>> print fuzzy_bool_eval('True or Maybe') 
Maybe 
>>> print fuzzy_bool_eval('False or (False and Maybe)') 
False 
+0

'nested_bool_eval' fallará si no realiza ninguna operación, es decir,' nested_bool_eval ("True") '(o False). –

+0

@Mike Graham Ah, olvidé ese caso, arreglando, gracias :) – mlvljr

+1

Esto es inquietantemente impresionante. (aplausos) –

Cuestiones relacionadas