catch 和 throw 的示例 ¶使用 catch 和 throw 的一种场景是退出双层嵌套循环。(在大多数编程语言中,这类需求会通过 goto 实现。)以下示例中,我们会计算当 i 和 j 从 0 遍历到 9 时 (foo i j) 的值:
(defun search-foo ()
(catch 'loop
(let ((i 0))
(while (< i 10)
(let ((j 0))
(while (< j 10)
(if (foo i j)
(throw 'loop (list i j)))
(setq j (1+ j))))
(setq i (1+ i))))))
若 foo 某次返回值为非 nil,则会立即终止执行,并返回一个包含 i 和 j 的列表。若 foo 始终返回 nil,则 catch 会正常返回,其返回值为 nil—— 因为这正是 while 结构的执行结果。
以下是两个稍有差异的进阶示例,展示了同时存在两个返回点的场景。首先是两个返回点使用相同标记 hack 的情况:
(defun catch2 (tag)
(catch tag
(throw 'hack 'yes)))
⇒ catch2
(catch 'hack (print (catch2 'hack)) 'no) ⊣ yes ⇒ no
由于两个返回点的标记均与该 throw 匹配,因此 throw 会跳转到内层的那个返回点 —— 也就是在 catch2 中建立的返回点。因此,catch2 会正常返回,返回值为 yes,且该值会被打印输出。最后,外层 catch 代码体中的第二个形式(即 'no)会被求值,并作为外层 catch 的返回值。
接下来我们修改传给 catch2 的参数:
(catch 'hack (print (catch2 'quux)) 'no) ⇒ yes
我们仍然有两个返回点,但这一次只有外层返回点的标记是 hack;内层返回点的标记改为了 quux。因此,throw 会让外层的 catch 直接返回 yes。函数 print 不会被调用,代码体 'no 也不会被求值。