2010-03-18 9 views
14

Ho alcune espressioni logiche booleane generate dinamicamente, come:dinamicamente valutando semplice logica booleana in Python

  • (A o B) e (C o D)
  • A o (A e B)
  • Un
  • vuoto - restituisce true

I segnaposto vengono sostituiti con booleani. Dovrei,

  1. Converti queste informazioni per un'espressione Python come True or (True or False) e eval esso?
  2. Creare un albero binario in cui un nodo è un oggetto bool o Conjunction/Disjunction e valutarlo in modo ricorsivo?
  3. Converti in espressioni S nidificate e usa un parser Lisp?
  4. Qualcos'altro?

suggerimenti benvenuto.

risposta

10

Non dovrebbe essere affatto difficile scrivere un valutatore in grado di gestirlo, ad esempio utilizzando pyparsing. Hai solo poche operazioni da gestire (e, o, e il raggruppamento?), Quindi dovresti essere in grado di analizzarle e valutarle tu stesso.

Non è necessario creare in modo esplicito l'albero binario per valutare l'espressione.

+3

Questo esempio di pyparsing (http://pyparsing.wikispaces.com/file/view/simpleBool.py) dovrebbe essere quasi una soluzione drop-in. – PaulMcG

+0

Accetterò questa risposta poiché non è così terrificante come 'eval()' ed è la più facilmente estendibile. –

8

Se imposti i termini con le persone locali e globali a cui tieni, dovresti essere in grado di passarli in modo sicuro insieme all'espressione in eval().

+0

Non è necessario utilizzare 'eval' qui; hai solo bisogno di valutare un linguaggio molto semplice, non Python. (Inoltre, limitare ciò che si passa a "eval" per locali/globali non lo rende sicuro se si finisce col voler passare molto e, certamente, non si evitano calcoli incredibilmente grandi.) –

+0

+1: basta creare un dizionario e usa eval. –

17

Ecco un piccolo modulo (possibilmente, 74 linee, tra cui gli spazi bianchi) che ho costruito in circa un'ora e mezza (più quasi un'ora per refactoring):

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

I test semplici danno:

>>> 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 

[parzialmente fuori tema possibilmente]

nota, la si può facilmente configura i token (sia gli operandi che gli operatori) che usi con i mezzi di iniezione delle dipendenze di poor-mans forniti (token_to_char=token_to_char e amici) per avere più valutatori diversi allo stesso tempo (basta resettare i globali "iniettati per default" ti lasceranno con un singolo comportamento).

Ad esempio:

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) 

dà:

>>> 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' non funzionerà se non si esegue alcuna operazione, ad esempio' nested_bool_eval ("True") '(o False). –

+0

@Mike Graham Ah, ho dimenticato quel caso, risolvendo, grazie :) – mlvljr

+1

Questo è inquietantemente impressionante. (applausi) –