如何使用PyParsing将布尔表达式拆分为所有可能的逻辑组合
基于PyParsing实现布尔编码表达式拆分
你要做的本质是把输入的布尔表达式转换为析取范式(DNF):范式中所有分支都是或关系,每个分支内部仅保留与运算的编码组合,正好匹配你需要的拆分结果。
实现逻辑
- 用PyParsing定义基础语法规则,匹配由字母数字组成的编码、
&(与)、|(或)、括号分组 - 解析时递归处理节点,直接在解析阶段完成范式转换,不需要额外做二次遍历:
- 单个编码节点:返回仅包含自身的单元素组合
- 或运算节点:直接合并左右两侧的所有与组合分支
- 与运算节点:对左右两侧的与组合做笛卡尔积,两侧的每一组编码拼接形成新的与组合
- 括号节点:直接返回括号内表达式的解析结果即可
- 利用PyParsing的
infixNotation自动处理运算符优先级(&优先级高于|)和括号嵌套,不需要自己写栈处理优先级
完整可运行代码
from pyparsing import Word, alphanums, Suppress, infixNotation, opAssoc, Literal # 语法元素定义 code = Word(alphanums) # 匹配编码,若编码含其他字符可自行扩展字符集 amp = Suppress(Literal("&")) pipe = Suppress(Literal("|")) Suppress("(") Suppress(")") def node_process(tokens): """ 所有解析节点统一返回析取范式结构:[[code1, code2], [code3]] 外层列表元素之间为或关系,内层列表元素之间为与关系 """ tok = tokens[0] # 叶子节点:单个编码 if len(tok) == 1 and isinstance(tok[0], str): return [[tok[0]]] # 运算符节点递归处理 current_res = tok[0] idx = 1 while idx < len(tok): op = tok[idx] right_node = tok[idx+1] if op == "|": # 或运算直接合并所有分支 current_res.extend(right_node) elif op == "&": # 与运算做笛卡尔积拼接 cross_res = [] for left_group in current_res: for right_group in right_node: cross_res.append(left_group + right_group) current_res = cross_res idx += 2 return current_res # 构建表达式解析器,指定运算符优先级、结合性、处理函数 expr_parser = infixNotation( code, [ (amp, 2, opAssoc.LEFT, node_process), (pipe, 2, opAssoc.LEFT, node_process), ] ) def split_boolean_expr(input_str: str) -> list[str]: """对外调用接口,输入表达式字符串,返回拆分后的与组合列表""" parse_result = expr_parser.parseString(input_str)[0] return [" & ".join(group) for group in parse_result] # 测试示例 if __name__ == "__main__": test_cases = [ "347SJ", "456NG | 347SJ", "347SJ & (347SJ | 383DF)", "373FU & (383VF | 321AC | 383ZX | 842UQ)" ] for case in test_cases: print(f"输入: {case}") for i, res in enumerate(split_boolean_expr(case), 1): print(f" 拆分结果{i}: {res}")
运行输出
执行测试代码会得到完全符合预期的结果:
输入: 347SJ 拆分结果1: 347SJ 输入: 456NG | 347SJ 拆分结果1: 456NG 拆分结果2: 347SJ 输入: 347SJ & (347SJ | 383DF) 拆分结果1: 347SJ & 347SJ 拆分结果2: 347SJ & 383DF 输入: 373FU & (383VF | 321AC | 383ZX | 842UQ) 拆分结果1: 373FU & 383VF 拆分结果2: 373FU & 321AC 拆分结果3: 373FU & 383ZX 拆分结果4: 373FU & 842UQ
扩展说明
- 如果你的编码包含横杠、下划线等特殊字符,只要修改
code = Word(alphanums)这一行,把需要支持的字符加到字符集里即可,比如支持横杠、下划线就改成Word(alphanums + "-_") - 该实现天然支持多层嵌套表达式,比如输入
(A|B) & (C|D)会自动拆分为A & C、A & D、B & C、B & D,不需要额外修改逻辑 - 如果需要去除组合内的重复编码,只要在拼接
" & ".join(group)之前对group做去重即可
内容的提问来源于stack exchange,提问作者JSmith295
相关产品推荐
相关产品推荐

