Python中简单布尔逻辑的动态求值

Python中简单布尔逻辑的动态求值,python,tree,logic,boolean,boolean-logic,Python,Tree,Logic,Boolean,Boolean Logic,我有一些动态生成的布尔逻辑表达式,如: (A或B)及(C或D) A或(A和B) A empty-计算结果为True 占位符将替换为布尔值。我应该 将此信息转换为Python表达式,如True或(True或False)和evalit 创建一个二叉树,其中一个节点是bool或Conjunction/Disjunction对象,并递归计算它 将其转换为嵌套的S表达式并使用Lisp解析器 还有别的吗 欢迎您的建议。如果您与您关心的本地人和全局人一起设置dict,那么您应该能够安全地将它们与表达式一起

我有一些动态生成的布尔逻辑表达式,如:

  • (A或B)及(C或D)
  • A或(A和B)
  • A
  • empty-计算结果为True
占位符将替换为布尔值。我应该

  • 将此信息转换为Python表达式,如
    True或(True或False)
    eval
    it
  • 创建一个二叉树,其中一个节点是
    bool
    Conjunction
    /
    Disjunction
    对象,并递归计算它
  • 将其转换为嵌套的S表达式并使用Lisp解析器
  • 还有别的吗

  • 欢迎您的建议。

    如果您与您关心的本地人和全局人一起设置dict,那么您应该能够安全地将它们与表达式一起传递到中。

    编写一个能够处理此问题的计算器应该一点也不困难,例如使用。您只需要处理几个操作(and、or和分组),所以您应该能够自己解析和评估它

    您不需要显式地形成二叉树来计算表达式。

    这里有一个小模块(可能是74行,包括空格),我花了大约一个半小时(再加上几乎一个小时的重构时间)构建:

    简单的测试给出:

    >>> 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
    
    [可能部分脱离主题]

    注意,您可以很容易地将您使用的令牌(操作数和运算符)配置为同时具有多个不同的求值器(只需重置“默认注入”全局变量,您就会有一个单一的行为)

    例如:

    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)
    
    给出:

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

    使用Symphy逻辑模块听起来像小菜一碟。他们甚至在文档上有这样一个例子:

    我写这篇文章是因为我今天有一个解决类似问题的方法,我在这里寻找线索。(具有任意字符串标记的布尔解析器,这些标记稍后将转换为布尔值)

    在考虑了不同的选择(自己实现一个解决方案或使用一些软件包)后,我决定使用Lark

    如果使用LALR(1),它很容易使用,而且速度也很快

    下面是一个与您的语法相匹配的示例

    from lark import Lark, Tree, Transformer
    
    base_parser = Lark("""
        expr: and_expr
            | or_expr
        and_expr: token
                | "(" expr ")"
                | and_expr " " and " " and_expr
        or_expr: token
                | "(" expr ")"
                | or_expr " " or " " or_expr
        token: LETTER
        and: "and"
        or: "or"
        LETTER: /[A-Z]+/
    """, start="expr")
    
    
    class Cleaner(Transformer):
        def expr(self, children):
            num_children = len(children)
            if num_children == 1:
                return children[0]
            else:
                raise RuntimeError()
    
        def and_expr(self, children):
            num_children = len(children)
            if num_children == 1:
                return children[0]
            elif num_children == 3:
                first, middle, last = children
                return Tree(data="and_expr", children=[first, last])
            else:
                raise RuntimeError()
    
        def or_expr(self, children):
            num_children = len(children)
            if num_children == 1:
                return children[0]
            elif num_children == 3:
                first, middle, last = children
                return Tree(data="or_expr", children=[first, last])
            else:
                raise RuntimeError()
    
    
    def get_syntax_tree(expression):
        return Cleaner().transform(base_parser.parse(expression))
    
    print(get_syntax_tree("A and (B or C)").pretty())
    

    注意:我选择的正则表达式故意与空字符串不匹配(Lark出于某种原因不允许它)。

    您可以使用Lark语法库执行该操作


    这里不需要使用
    eval
    ;您只需要计算一种非常简单的语言,而不是Python。(另外,如果你最终想要通过更多的计算,那么限制你传递给本地人/全局人的
    eval
    并不能保证安全,当然也不能阻止不可能的大计算。)
    nested\u bool\u eval
    如果你没有实际执行任何操作,即
    nested\u bool\u eval(“True”)
    (或False).这令人不安。(掌声)@mlvljr它在使用
    False或False或True
    时失败,因为它没有父项,如果没有父项,则在
    中返回:return self.bool_eval(token_list)
    python正确计算该表达式:
    >False或False或True-->True
    @Besnik事实是,这是在假设中编写的,排序是由paren显式给出的(就像在OP的文本中),但是是的,最好检查这是否为真,或者可能只是假设左或右运算符的关联性和所有必要的转换这应该是一个简单的解决方案。我将接受这个答案,因为它没有
    eval()
    那么可怕,而且它是最容易扩展的。答案现在无效,
    wikispaces.com
    已经死了。仍然可以使用Internet Archive/Wayback Machine Live版本在
    from lark import Lark, Tree, Transformer
    
    base_parser = Lark("""
        expr: and_expr
            | or_expr
        and_expr: token
                | "(" expr ")"
                | and_expr " " and " " and_expr
        or_expr: token
                | "(" expr ")"
                | or_expr " " or " " or_expr
        token: LETTER
        and: "and"
        or: "or"
        LETTER: /[A-Z]+/
    """, start="expr")
    
    
    class Cleaner(Transformer):
        def expr(self, children):
            num_children = len(children)
            if num_children == 1:
                return children[0]
            else:
                raise RuntimeError()
    
        def and_expr(self, children):
            num_children = len(children)
            if num_children == 1:
                return children[0]
            elif num_children == 3:
                first, middle, last = children
                return Tree(data="and_expr", children=[first, last])
            else:
                raise RuntimeError()
    
        def or_expr(self, children):
            num_children = len(children)
            if num_children == 1:
                return children[0]
            elif num_children == 3:
                first, middle, last = children
                return Tree(data="or_expr", children=[first, last])
            else:
                raise RuntimeError()
    
    
    def get_syntax_tree(expression):
        return Cleaner().transform(base_parser.parse(expression))
    
    print(get_syntax_tree("A and (B or C)").pretty())
    
    from lark import Lark, Transformer, v_args, Token, Tree
    from operator import or_, and_, not_
    
    calc_grammar = f"""
        ?start: disjunction
        ?disjunction: conjunction
            | disjunction "or" conjunction   -> {or_.__name__}
        ?conjunction: atom
            | conjunction "and" atom  -> {and_.__name__}
        ?atom: BOOLEAN_LITTERAL           -> bool_lit
             | "not" atom         -> {not_.__name__}
             | "(" disjunction ")"
        BOOLEAN_LITTERAL: TRUE | FALSE
        TRUE: "True"
        FALSE: "False"
        %import common.WS_INLINE
        %ignore WS_INLINE
    """
    
    
    @v_args(inline=True)
    class CalculateBoolTree(Transformer):
        or_ = or_
        not_ = not_
        and_ = and_
    
        allowed_value = {"True": True, "False": False}
    
        def bool_lit(self, val: Token) -> bool:
            return self.allowed_value[val]
    
    
    calc_parser = Lark(calc_grammar, parser="lalr", transformer=CalculateBoolTree())
    calc = calc_parser.parse
    
    
    def eval_bool_expression(bool_expression: str) -> bool:
        return calc(bool_expression)
    
    
    print(eval_bool_expression("(True or False) and (False and True)"))
    print(eval_bool_expression("not (False and True)"))
    print(eval_bool_expression("not True or False and True and True"))