不,没有任何形式可以出现在类似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)))))
这种东西最好借助模式匹配库来表达。