我正在尝试通过Common Lisp:符号计算的简单介绍一书来学习 Common Lisp 。此外,我正在使用 SBCL、Emacs 和 Slime。
在第8章的高级部分,作者介绍了labels
特殊功能。实际上,他在顶层(主函数和辅助函数)上定义事物与在label
函数内使用表达式进行了对比。
例如,这将是一个reverse
使用顶级方法的带有尾调用的列表函数:
(defun reverse-top-level-helper (xs-left accu)
(cond ((null xs-left) accu)
(t (reverse-top-level-helper (cdr xs-left)
(cons (car xs-left)
accu)))))
(defun reverse-top-level-main (xs)
(reverse-top-level-helper xs nil))
另一方面,下面的代码将使用以下代码执行相同的操作labels
:
(defun reverse-labels (xs)
(labels ((aux-label (xs-left accu)
(cond ((null xs-left) accu)
(t (aux-label (cdr xs-left)
(cons (car xs-left) accu))))))
(aux-label xs nil)))
因此,标签方法避免了人们搞砸顶层辅助函数的机会。与顶级方法不同,标签方式提供对主函数的局部变量的访问。
不幸的是,根据作者的说法,在大多数 lisp 实现中,无法跟踪标签表达式中的函数。这似乎是我的情况,因为我从 REPL 得到这个:
CL-USER> (trace aux-label)
WARNING: COMMON-LISP-USER::AUX-LABEL is undefined, not tracing.
NIL
让我感兴趣的一点是,作者没有展示在 Racket 中很常见的第三种方法。我将其称为嵌套的 defuns。
同样的问题将被解决为:
(defun reverse-racket-style (xs)
(defun aux (xs-left accu)
(cond ((null xs-left) accu)
(t (aux (cdr xs-left) (cons (car xs-left) accu)))))
(aux xs nil))
在这种方法中,辅助函数可以从主函数访问局部变量。它也可以被 REPL 追踪。
我一整天都在使用它。所以我知道它可以在具有许多使用它的功能的文件中工作。实际上,考虑到我使用了一堆不同的辅助函数并且它们都具有相同的名称,aux
在球拍风格下被调用,我什至不知道 trace 是如何工作的。trace
知道我想看哪个辅助。
最重要的是,这个遗漏真的让我很感兴趣。特别是因为我真的很喜欢这本书。我想我可能遗漏了一些东西。
1 - 为什么没有提到这种方法?这种带有嵌套 defun 的“球拍风格”在 Common Lisp 中被认为是不好的风格吗?
2 - 我是否遗漏了一些重要的细节(例如,这种方法可能是难以发现错误或产生性能问题的根源)?
3 - 这种遗漏是否有一些历史原因?