不,没有任何形式可以出现在类似progn
- 的主体中并突然声明新变量的范围超过progn
.
虽然这在某些方面会很方便(例如在版本控制中减少缩进和更少的空白差异),但有一个很大的缺点:编写分析代码的代码要困难得多。
Lisp 的结构使得当我们查看复合形式的最左边的符号时,我们知道它是什么,并且在该符号旁边有一些严格的、易于解析的语法,它告诉我们在该构造的范围。(如果它做了这样的事情,它被称为绑定构造)。
可以将变量定义分散在整个主体中的绑定构造需要额外的工作才能找到所有这些位置。
如果你真的错过了其他语言的这个特性,你可以自己编写一个宏来为你实现它。
这是一个可能的开始。
让我们调用宏(begin ...)
,在里面(begin ...)
让我们支持使用语法(new (var [initform])*)
引入一个或多个变量的形式的let
语法,除了它没有主体。这些变量的范围是begin
表单的其余部分。
因此,任务是制作这种形式的宏转换语法:
(begin
a b c
(new (x 42))
d e
(new (y 'foo) (z))
f g)
比如说,这段代码:
(progn
a b c
(let ((x 42))
d e
(let ((y 'foo) (z))
f g)))
begin
宏必须查看它的所有参数形式,并将它们与其他任何形式区分开来,(new ...)
并生成嵌套let
结构。
上述转换问题的结构暗示了一个简单的递归解决方案。
现代工业实力begin
将不得不提供声明。也许我们可以允许一个(new ...)
表格紧跟一个(declare ...)
表格。然后根据模式折叠这两个((new A ...) (declare B ...) C ...)
-> (let (A ...) (declare B ...) C ...)
,在这里我们递归处理(C ...)
更多出现的(new ...)
.
当然,如果你有这个begin
宏,你必须明确地使用它。没有任何简单的方法可以重新定位具有“隐式预测”的现有 Lisp 结构,以便它们具有“隐式开始”。
当然,你总能做的是实现一个非常复杂的宏,如下所示:
(my-dialect-of-lisp
;; file full of code in your customized dialect of Lisp goes here
)
my-dialect-of-lisp
宏解析方言(即为该方言实现完整的代码漫游器)并翻译成标准的 Lisp 。
附录:实施(begin ...)
(无声明支持):
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun begin-expander (forms)
(if (null forms)
nil
(destructuring-bind (first &rest rest) forms
(if (and (consp first)
(eq (first first) 'new))
`((let (,@(rest first)) ,@(begin-expander rest)))
`(,first ,@(begin-expander rest)))))))
(defmacro begin (&rest forms)
(let ((expansion (begin-expander forms)))
(cond
;; (begin) -> nil
((null expansion) nil)
;; (begin (new ...) ...) -> ((let (...) ...)) -> (let (...) ...)
((and (consp (first expansion))
(eq (first (first expansion)) 'let))
(first expansion))
;; (begin ...) -> (...) -> (progn ...)
(t `(progn ,@expansion)))))
这种事情在模式匹配库的帮助下可以更好地表达。