Skip to main content
 首页 » 编程设计

python之我应该如何使用 pyparsing 从 bool 表达式生成元素列表

2025年05月04日7zhujiabin

我有一个 bool 表达式列表,这些表达式表示可以组合起来表示更大对象的物理对象。它们看起来像这样:((A 和 B)或 C)。该对象可以由 A 和 B 的组合或单独由 C 表示。我想生成一个可用于创建对象的字符串列表列表。在这种情况下,我想要 [[A,B], [C]]。

Pyparsing 看起来很有趣,所以我决定试一试这个问题。在几次失败的尝试之后,我决定从网站上改编 fourFn.py 示例。这是我目前所拥有的:

from pyparsing import Literal, CaselessLiteral, Word, Combine, \ 
    Group, Optional, ZeroOrMore, Forward, alphanums 
 
exprStack = [] 
 
def myAnd(op1, op2): 
    if isinstance(op1, str): 
        return([op1, op2]) 
    else: 
    return op1.append(op2) 
 
def myOr(op1, op2): 
    if isinstance(op1, str): 
        return([[op1], [op2]]) 
    else: 
        return op1.append([op2]) 
 
def pushFirst(strg, loc, toks): 
    exprStack.append(toks[0]) 
 
bnf = None 
def BNF(): 
    """ 
    boolop  :: 'and' | 'or' 
    gene    :: alphanum 
    atom    :: gene | '(' expr ')' 
    """ 
    global bnf 
    if not bnf: 
        element = Word(alphanums) 
        andop  = Literal( "and" ) 
        orop = Literal( "or" ) 
        lpar  = Literal( "(" ).suppress() 
        rpar  = Literal( ")" ).suppress() 
        boolop  = andop | orop 
 
        expr = Forward() 
        atom = ((element | lpar + expr + rpar).setParseAction(pushFirst) | (lpar + expr.suppress() + rpar)) 
        expr << atom + ZeroOrMore((boolop + expr).setParseAction(pushFirst)) 
 
        bnf = expr 
    return bnf 
 
# map operator symbols to corresponding arithmetic operations 
fn  = {"or": myOr, 
       "and": myAnd} 
 
def evaluateStack( s ): 
    op = s.pop() 
    if op in fn: 
        op2 = evaluateStack(s) 
        op1 = evaluateStack(s) 
        return fn[op](op1, op2) 
    else: 
        return op 
 
if __name__ == "__main__": 
 
    def test(s, expVal): 
        global exprStack 
        exprStack = [] 
        results = BNF().parseString(s) 
        val = evaluateStack(exprStack[:]) 
        if val == expVal: 
            print s, "=", val, results, "=>", exprStack 
        else: 
            print "!!! "+s, val, "!=", expVal, results, "=>", exprStack 
 
    test("((A and B) or C)", [['A','B'], ['C']]) 
    test("(A and B) or C", [['A','B'], ['C']]) 
    test("(A or B) and C", [['A', 'C'], ['B', 'C']]) 
    test("A and B", ['A', 'B']) 
    test("A or B", [['A'], ['B']]) 

前三个测试在这里失败,只返回括号中每个表达式的第一个元素。 A 将被多次插入堆栈。似乎我修改 fourFn.py 的方式破坏了我的脚本处理这些组的能力。有没有更好的方法来解决这个问题?

编辑 一杯咖啡后,我意识到我遇到的问题很容易解决。我的新and和or函数如下:

def myAnd(op1, op2): 
    if isinstance(op1, str) and isinstance(op2, str): 
        newlist = [op1, op2] 
 
    elif isinstance(op1, str): 
        newlist = [op1] 
        newlist.append(op2) 
 
    elif isinstance(op2, str): 
        newlist = op1 
        newlist.append(op2) 
 
    else: 
        newlist = [op1.append(item) for item in op2] 
    return newlist 
 
def myOr(op1, op2): 
    if isinstance(op1, str) and isinstance(op2, str): 
        newlist = [[op1], [op2]] 
        r 
    elif isinstance(op1, str): 
        newlist = [op1] 
        newlist.append([op2]) 
 
    elif isinstance(op2, str): 
        newlist = [op1] 
        newlist.append([op2]) 
 
    else: 
        newlist = [op1, [op2]] 
    return newlist1 

解析器构造如下:

expr = Forward() 
atom = element.setParseAction(pushFirst) | (lpar + expr + rpar) 
expr << atom + ZeroOrMore((boolop + expr).setParseAction(pushFirst)) 

一个新的更有趣的问题涉及如何处理像这样的情况(A 或 B)和 C。结果应该是 [[A, C], [B, C]]。有没有典型的 pyparsing 方法来处理这个问题?

请您参考如下方法:

为了将来引用,这里有一种适用于我的测试用例但与上面建议的 AST 方法不同的方法:

from pyparsing import Literal, Word, Optional, \ 
    Group, ZeroOrMore, Forward, alphanums 
import ffparser, sys 
 
exprStack = [] 
 
def myAnd(op1, op2): 
    if isinstance(op1, str) and isinstance(op2, str): 
        newlist = [[op1, op2]] 
 
    elif isinstance(op1, str): 
        newlist = op2 
        [item.insert(0, op1) for item in newlist] 
 
    elif isinstance(op2, str): 
        newlist = op1 
        [item.append(op2) for item in op1] 
 
    else: 
        newlist = [op1.append(item) for item in op2] 
 
    return newlist 
 
def myOr(op1, op2): 
   if isinstance(op1, str) and isinstance(op2, str): 
        newlist = [[op1], [op2]] 
 
    elif isinstance(op1, str): 
        newlist = op2 
        newlist.insert(0, [op1]) 
 
    elif isinstance(op2, str): 
        newlist = op1 
        newlist.append([op2]) 
 
    else: 
        newlist = [] 
        [newlist.append(item) for item in op1] 
        [newlist.append(item) for item in op2] 
 
    return newlist 
 
def pushFirst(strg, loc, toks): 
    exprStack.append(toks[0]) 
 
bnf = None 
def BNF(): 
    """ 
    boolop  :: 'and' | 'or' 
    gene    :: alphanum 
    atom    :: gene | '(' expr ')' 
    """ 
    global bnf 
    if not bnf: 
        element = Word(alphanums) 
        andop  = Literal( "and" ) 
        orop = Literal( "or" ) 
        lpar  = Literal( "(" ).suppress() 
    rpar  = Literal( ")" ).suppress() 
    boolop  = andop | orop 
 
    expr = Forward() 
    atom = element.setParseAction(pushFirst) | (Optional(lpar) + expr + Optional(rpar)) 
    expr << atom + ZeroOrMore((boolop + expr).setParseAction(pushFirst))