编程中的控制结构与变量使用
立即解锁
发布时间: 2025-10-26 00:26:08 阅读量: 4 订阅数: 25 AIGC 

### 编程中的控制结构与变量使用
在编程中,控制结构和变量是非常重要的概念。下面将详细介绍控制结构中的非局部退出清理机制,以及变量的不同类型和使用方法。
#### 1. 非局部退出清理
在编程过程中,有时会临时将数据结构置于不一致的状态。为了确保在出现错误或抛出异常时能使数据恢复一致,`unwind-protect` 结构就显得至关重要。
`unwind-protect` 的语法形式为:
```lisp
unwind-protect body-form cleanup-forms...
```
它的工作原理是:执行 `body-form`,并保证无论 `body-form` 是正常完成、通过 `throw` 退出,还是引发错误,`cleanup-forms` 都会被执行。如果 `body-form` 正常完成,`unwind-protect` 会在执行完 `cleanup-forms` 后返回 `body-form` 的值;若 `body-form` 未完成,`unwind-protect` 通常不会返回值。
需要注意的是,只有 `body-form` 受 `unwind-protect` 保护。如果 `cleanup-forms` 自身通过 `throw` 或错误非局部退出,`unwind-protect` 不能保证会执行剩余的 `cleanup-forms`。若某个 `cleanup-form` 的失败可能引发问题,可在该形式周围再使用一个 `unwind-protect` 进行保护。
当前活动的 `unwind-protect` 形式的数量,会与局部变量绑定的数量一起,受 `max-specpdl-size` 限制。
以下是一个使用 `unwind-protect` 创建临时缓冲区并确保在结束前将其删除的示例:
```lisp
(let ((buffer (get-buffer-create " *temp*")))
(with-current-buffer buffer
(unwind-protect
body-form
(kill-buffer buffer))))
```
虽然也可以写成 `(kill-buffer (current-buffer))` 并省略变量 `buffer`,但上述方式在 `body-form` 切换到不同缓冲区后出现错误时更安全。也可以在 `body-form` 周围使用 `save-current-buffer`,以确保临时缓冲区在需要删除时恢复为当前缓冲区。
Emacs 包含一个标准宏 `with-temp-buffer`,其展开形式与上述代码类似。许多宏也会以这种方式使用 `unwind-protect`。
下面是一个来自 FTP 包的实际示例:
```lisp
(let ((win nil))
(unwind-protect
(progn
(setq process (ftp-setup-buffer host file))
(if (setq win (ftp-login process host user password))
(message "Logged in")
(error "Ftp login failed")))
(or win (and process (delete-process process)))))
```
不过,这个示例存在一个小问题:如果用户在 `ftp-setup-buffer` 函数返回后但变量 `process` 赋值前按下 `C-g` 退出,进程将不会被终止。虽然这个问题很难修复,但发生的可能性很小。
#### 2. 变量的基本概念
在编程中,变量是用于代表值的名称。在 Lisp 里,每个变量由一个 Lisp 符号表示,变量名就是符号的名称,变量的值存储在符号的值单元中。在 Emacs Lisp 中,符号作为变量的使用与作为函数名的使用是相互独立的。
#### 3. 变量的类型及使用
##### 3.1 全局变量
全局变量是最简单的变量使用方式。它在同一时间只有一个值,并且该值在整个 Lisp 系统中(至少在当前时刻)都有效,直到指定新的值。使用 `setq` 可以为符号指定值,例如:
```lisp
(setq x '(a b))
```
这会将变量 `x` 的值设置为 `(a b)`。`setq` 是一种特殊形式,它不会计算第一个参数(变量名),但会计算第二个参数(新值)。一旦变量有了值,就可以直接使用符号来引用它,如:
```lisp
x ⇒(a b)
```
如果再次设置同一变量,新值将替换旧值:
```lisp
x ⇒(a b)
(setq x 4) ⇒4
x ⇒4
```
##### 3.2 不可变变量
在 Emacs Lisp 中,某些符号通常会计算为它们自身,如 `nil` 和 `t`,以及名称以 `:` 开头的符号(称为关键字)。这些符号不能被重新绑定,也不能改变其值。任何尝试设置或绑定 `nil` 或 `t` 的操作都会引发 `setting-constant` 错误。对于关键字,如果它被插入到标准对象数组中,除了将其设置为自身外,其他设置操作也会出错。
可以使用 `keywordp` 函数来判断一个对象是否为关键字:
```lisp
[Function]
keywordp object
```
该函数在对象是名称以 `:` 开头且插入到标准对象数组中的符号时返回 `t`,否则返回 `nil`。
需要注意的是,这些常量与使用 `defconst` 特殊形式定义的“常量”有本质区别。`defconst` 形式只是告知人类
0
0
复制全文


