2
我有一個布爾表達式列表,表示可以組合以表示較大對象的物理對象。他們看起來像這樣:((A和B)或C)。這個對象可以用A和B的組合來表示,也可以用C單獨來表示。我想生成可用於創建對象的字符串列表的列表。在這種情況下,我想[[A,B],[C]]。如何pyparsing從布爾表達式生成元素列表?
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的方式已經打破了我的腳本處理這些組的能力。有沒有更好的方法來解決這個問題?
編輯 喝了一杯咖啡後,我意識到我遇到的問題很容易解決。是我的新的和或功能如下:
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]]。有沒有一種處理這個問題的典型方法?
如果我是你,我會剛剛創建了一個簡單的經典AST與操作爲節點,操作數作爲葉子,然後遍歷它來構建我喜歡的構造。在你的情況下,它只是用它們的操作數列表替換'和'節點。 – 9000 2012-02-13 21:54:29
查看pyparsing wiki上的SimpleBool.py示例。它使用了一個pyparsing helper方法'operatorPrecedence',它爲你做了大量的原子/因子/項。 SimpleBool.py將爲您提供Bool對象的結構,類似於@ 9000提出的AST,但具有更豐富的對象API,因此您可以直接評估結果。 – PaulMcG 2012-02-13 22:48:01
感謝您的意見。我喜歡AST的建議,並且一定會看看SimpleBool.py示例。我確實設法使上述方法適用於我的測試用例。 – 2012-02-13 23:07:33