讲师中心 微信公众号

工具推荐

视频效率加速
updream

一键生成高质量推广成片

立刻MV
立刻MV

低成本歌曲 MV、视频生成

剧云
剧云

中文短剧剧本制作平台

Laper
Laper

剧本策划到成片协作

构建支持任意深度嵌套的 n 叉表达式树(含递归解析与动态节点访问)

冬明酱_6716

冬明酱_6716

发布时间:2026-01-06 10:32:39

|

305人浏览过

|

来源于php中文网

原创

构建支持任意深度嵌套的 n 叉表达式树(含递归解析与动态节点访问)

本文介绍如何通过递归下降解析器将括号嵌套表达式(如 `["(", "a", "&", "b", ")", "|", "c"]`)构建成 n 叉树,并支持在任意深度动态定位、修改或添加子节点,避免硬编码 `.nodes[x].nodes[y]...`。

在处理多层嵌套逻辑表达式(如 ["(", "MORE", "&", "(", "COMPLICATED", "|", "(", "EXPRESSION", "&", "PRESENTING", ")", "|", "MANY", ")", ")", "|", "(", "DEEPER", "&", "(", "LEVELS", "|", "FOR", "|", "TREE", ")", ")"])时,传统“逐级索引”方式(如 root.nodes[0].nodes[1].nodes[2].add_node(...))既不可扩展也不可维护——因为嵌套深度动态变化(2 层到 5 层不等),无法预知路径长度。

根本解法是用递归替代路径拼接:不构造字符串形式的 .nodes[0].nodes[2],而是设计一个能接受“路径坐标列表”的通用访问函数,并配合递归建树逻辑实现自顶向下的结构化构建。

✅ 推荐方案:递归建树 + 路径导航 API

我们重构树类,增强其可操作性,并提供 get_node_by_path() 和 set_child_at_path() 等工具方法:

logos-node
logos-node

在 Linux x86_64 上安装、更新或检查 Logos 区块链测试网验证节点。用于用户需要搭建 Logos 节点时使用。

下载
class NonBinTree:
    def __init__(self, val):
        self.val = val
        self.nodes = []  # 子节点列表,支持任意数量子节点

    def add_node(self, val):
        """添加新子节点(值为 val 的新 Node)"""
        self.nodes.append(NonBinTree(val))
        return self.nodes[-1]

    def get_node_by_path(self, path):
        """
        根据路径列表访问节点,例如 path=[0, 1, 2] → root.nodes[0].nodes[1].nodes[2]
        若路径越界或非整数,抛出 IndexError 或 TypeError
        """
        node = self
        for idx in path:
            if not isinstance(idx, int):
                raise TypeError(f"Path index must be int, got {type(idx).__name__}")
            if not (0 <= idx < len(node.nodes)):
                raise IndexError(f"Index {idx} out of range for node with {len(node.nodes)} children")
            node = node.nodes[idx]
        return node

    def set_child_at_path(self, path, new_child):
        """
        在指定路径位置替换子节点(path[:-1] 定位父节点,path[-1] 指定下标)
        例如 path=[0, 2] 表示:root.nodes[0] 的第 2 个子节点被 new_child 替换
        """
        if not path:
            raise ValueError("Path cannot be empty for set_child_at_path")
        parent_path, child_idx = path[:-1], path[-1]
        parent = self if not parent_path else self.get_node_by_path(parent_path)
        if not isinstance(child_idx, int) or not (0 <= child_idx < len(parent.nodes)):
            raise IndexError(f"Invalid child index {child_idx} for parent with {len(parent.nodes)} children")
        parent.nodes[child_idx] = new_child

    def __repr__(self):
        children_repr = ", ".join(repr(n) for n in self.nodes)
        return f"NonBinTree({self.val}): [{children_repr}]"

? 递归解析器:自动处理任意嵌套层级

核心思想是迭代器 + 递归下降:用 iter(expr) 创建消耗式遍历器,每遇到 "(" 就递归调用解析子表达式,遇到 ")" 或运算符时回溯。该方法天然支持无限嵌套,无需手动管理栈或深度计数。

def expr_to_tree(expr):
    it = iter(expr)

    def parse_operand():
        token = next(it, None)
        if token is None:
            raise ValueError("Unexpected end of expression")
        if token == "(":
            return parse_expr()
        elif token in ("&", "|", ")"):
            raise ValueError(f"Expected operand, got {repr(token)}")
        else:
            return NonBinTree(token)

    def parse_expr():
        # 解析首个操作数
        left = parse_operand()
        # 查看下一个 token:是否为顶层运算符?
        op = next(it, None)
        if op not in ("&", "|"):
            # 无运算符 → 单节点表达式,直接返回
            if op == ")":
                return left
            elif op is None:
                return left
            else:
                raise ValueError(f"Unexpected token after operand: {repr(op)}")

        # 构建以 op 为根的子树
        root = NonBinTree(op)
        root.add_node(left)  # 左操作数作为第一个子节点

        # 继续读取后续操作数(直到遇到 ')' 或结束)
        while True:
            token = next(it, None)
            if token is None or token == ")":
                break
            elif token in ("&", "|"):
                # 新运算符出现:说明当前 op 是更高层的运算符,应停止本层解析
                # 将 token “推回”,由上层处理
                it = iter([token] + list(it))  # 简单模拟 peek + pushback(生产环境建议用更健壮方式)
                break
            else:
                # 普通操作数或 '('
                if token == "(":
                    operand = parse_expr()
                else:
                    operand = NonBinTree(token)
                root.add_node(operand)

        return root

    # 启动解析(顶层无括号约束)
    result = parse_expr()
    # 检查是否还有未消费 token(防多余内容)
    leftover = list(it)
    if leftover:
        raise ValueError(f"Unconsumed tokens: {leftover}")
    return result
? 注意:上述 parse_expr 中的 it = iter([token] + list(it)) 是教学简化写法;实际项目中推荐使用 itertools.chain([token], it) 或封装带 peek() 的迭代器类,避免多次 list(it) 导致性能损耗。

? 使用示例

complicated_expr = ["(", "MORE", "&", "(", "COMPLICATED", "|","(","EXPRESSION","&","PRESENTING",")","|", "MANY", ")", ")", "|", "(", "DEEPER", "&", "(", "LEVELS", "|", "FOR", "|", "TREE", ")",")"]

tree = expr_to_tree(complicated_expr)
print(tree)
# 输出结构化树(省略部分细节):
# NonBinTree(|): [NonBinTree(&): [...], NonBinTree(&): [...]]

# ✅ 动态访问第 0 层子节点的第 1 个子节点的第 2 个子节点(假设存在)
try:
    target = tree.get_node_by_path([0, 1, 2])
    print("Found node:", target.val)
except (IndexError, TypeError) as e:
    print("Path invalid:", e)

# ✅ 在 [0, 1, 0] 处插入新子节点
new_node = NonBinTree("DYNAMICALLY_ADDED")
tree.set_child_at_path([0, 1, 0], new_node)

⚠️ 关键注意事项

  • 不要手动拼接属性链:eval("tree.nodes[0].nodes[1].add_node(...)") 或字符串格式化路径不仅危险(代码注入风险),且无法做静态类型检查和 IDE 自动补全。
  • 路径有效性必须校验:get_node_by_path 应始终检查索引范围,避免 IndexError 影响主流程。
  • 递归深度限制:Python 默认递归限制约 1000 层,若表达式极端嵌套(>500 层),需调用 sys.setrecursionlimit() —— 但更优解是改用显式栈的迭代版本(本文未展开)。
  • 运算符优先级未建模:当前实现按出现顺序左结合(A & B | C 视为 (A & B) | C)。如需支持优先级(如 & 优先于 |),需引入算符优先分析(Shunting Yard)或增加 precedence 参数。

✅ 总结

面对动态深度的 n 叉表达式树,破局点在于:

  1. 放弃硬编码路径,转而使用 path: List[int] + 递归/循环导航;
  2. 用迭代器驱动的递归下降解析器替代状态机或多层嵌套 for 循环,让语法结构自然映射到树结构;
  3. 增强树类 API(如 get_node_by_path, set_child_at_path),使任意层级的操作统一、安全、可测试。

这套模式不仅适用于布尔表达式,还可扩展至 JSON Schema 解析、AST 构建、配置树加载等场景——只要输入具备嵌套文法,即可复用该递归+路径范式。

热门AI工具

更多
DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

SoundView
SoundView Hot

SoundView是一款面向跨境电商的视频翻译、配音和本地化 AI 创作平台。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

超级麦吉

一款通过数字员工协作完成调研、数据分析与文档制作的AI办公平台。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

2355

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

3882

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

1296

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

4519

2025.09.10

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

6404

2023.10.24

Go语言中的运算符有哪些
Go语言中的运算符有哪些

Go语言中的运算符有:1、加法运算符;2、减法运算符;3、乘法运算符;4、除法运算符;5、取余运算符;6、比较运算符;7、位运算符;8、按位与运算符;9、按位或运算符;10、按位异或运算符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3564

2024.02.23

php三元运算符用法
php三元运算符用法

本专题整合了php三元运算符相关教程,阅读专题下面的文章了解更多详细内容。

1952

2025.10.17

登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

15586

2023.09.14

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

100

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn