闭包介绍
为什么闭包能共享变量 pre
?
在提供的代码中,闭包能够共享变量 pre
的关键在于 词法作用域(Lexical Scoping) 和 闭包捕获变量 的特性:
1 | func isValidBST(root *TreeNode) bool { |
工作原理:
词法作用域:
- Go 中函数可以访问定义时所在作用域的所有变量
dfs
匿名函数定义在isValidBST
函数体内,自然能访问pre
闭包捕获变量:
- 闭包不会复制变量值,而是捕获变量的引用
- 所有递归调用的
dfs
都指向同一个pre
变量 - 修改
pre
会影响所有闭包实例
递归共享状态:
1
2
3
4
5
6
7graph LR
A[pre=MinInt64] --> B[dfs-根节点]
B --> C[dfs-左子节点]
C --> D[dfs-左孙节点]
D -->|更新pre| C
C -->|更新pre| B
B -->|更新pre| E[dfs-右子节点]
闭包名称的含义与起源
名称来源
“闭包”(Closure)一词来源于 函数”闭合”(close over)其定义环境 的概念:
- 最早由 Peter J. Landin 在 1964 年提出
- 源自数学中的 λ演算(Lambda Calculus) 和 闭包属性(Closure Property)
- 在编译原理中称为 词法闭包(Lexical Closure)
名称含义
术语 | 解释 |
---|---|
闭(Close) | 函数”封闭”地携带其定义时的环境 |
包(-ure) | 将函数与其环境”打包”在一起 |
核心特性
- 环境捕获:函数记住并访问定义时的作用域
- 状态持久化:捕获的变量生命周期与闭包相同
- 私密状态:只有闭包能访问捕获的变量
闭包在 BST 验证中的优势
1 | pre := math.MinInt64 // 共享状态 |
- 状态管理简化:无需指针传递
- 代码更简洁:减少参数传递
- 逻辑更直观:直接读写共享状态
- 避免值复制:高效处理递归状态
闭包的本质
闭包本质上是一个 (函数 + 绑定环境) 的组合体:
1 | type Closure struct { |
每次递归调用时,都使用同一个 Env
环境,这正是 BST 验证需要的中序遍历状态一致性保证。
闭包 vs 指针参数
特性 | 闭包 | 指针参数 |
---|---|---|
状态共享 | 自动捕获 | 需显式传递 |
可读性 | ★★★☆☆ | ★★☆☆☆ |
安全性 | 变量封装 | 暴露内存地址 |
递归适用性 | 非常适合 | 需要小心指针传递 |
在 BST 验证场景中,闭包方案更简洁安全,避免了指针传递的复杂性,是 Go 语言处理递归状态的首选模式。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Lei Qi's Blog!
评论