有没有一种方法可以在Lisp(避免let)中声明局部变量?
我喜欢口齿不清,但其中一件让我感到厌烦的事是它嵌套太多 在命令式编程语言中,我可以通过使用中间值来中断长表达式,例如:有没有一种方法可以在Lisp(避免let)中声明局部变量?,lisp,let,Lisp,Let,我喜欢口齿不清,但其中一件让我感到厌烦的事是它嵌套太多 在命令式编程语言中,我可以通过使用中间值来中断长表达式,例如: int x=someFunctionCall()?someOtherFunctionCall():42; int y=myUnterminableNameFunction(x); 而不是 int x=myUnterminableNameFunction(someFunctionCall()?someOtherFunctionCall():42); 这也可以在Lisp中完成,
int x=someFunctionCall()?someOtherFunctionCall():42;
int y=myUnterminableNameFunction(x);
而不是
int x=myUnterminableNameFunction(someFunctionCall()?someOtherFunctionCall():42);
这也可以在Lisp中完成,但据我所知,只能使用let
let
引入了一个额外的嵌套级别,我宁愿避免这种情况
我不是想反驳这种观点,而是想找到一种在单个非嵌套函数/宏调用中声明局部变量的方法。类似以下内容的declare\u local
:
(defun my_function (a b)
(declare_local x (if (some_function_call) (some_other_function_call) 42))
(my_unterminable_name_function x))
如果它不存在,是否可以通过一个巧妙的宏来实现,而不影响性能?是的,在常见的Lisp中使用
&aux
参数:
(defun foo (a b &aux x y z)
(setq x ...)
(setq y ...)
.... )
或使用:
您可以使用
let*
表单进行顺序绑定
(let* ((x (if (some-function-call)
(some-other-call)
42))
(y (my-unterminable-name-function x)))
(bla-bla-bla)
...)
它确实会筑巢,但不是很多
从
声明本地
开始,它必须由一些外部宏处理。例如,您可以编写my defun
,检查其主体中的declare local
,并对其进行转换Upd但它有点反Lisp。Lisp表单通常只影响嵌套表单cl:declare
是我能想到的唯一例外。这里有一个概念证明宏,它将变量声明从平面列表拉到标准的let*
表单中
(defun my/vardecl-p (x)
"Return true if X is a (VAR NAME VALUE) form."
(and (listp x)
(> (length x) 1)
(eq 'var (car x))))
(defmacro my/defun (name args &rest body)
"Special form of DEFUN with a flatter format for LET vars"
(let ((vardecls (mapcar #'cdr
(remove-if-not #'my/vardecl-p body)))
(realbody (remove-if #'my/vardecl-p body)))
`(defun ,name ,args
(let* ,vardecls
,@realbody))))
例如:
(my/defun foo (a b)
(var x 2)
(var y 3)
(* x y a b))
(foo 4 5)
; => 120
不,在类
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…
表单。然后根据模式将这两个元素折叠起来((新的A…(声明B…)C…->(让(A…(声明B…)C…
),在这里我们递归处理(C…
)以获得更多出现的(新…
)
当然,如果您有这个begin
宏,就必须显式地使用它。没有任何简单的方法可以重定具有“隐式progn”的现有Lisp结构的目标,从而使其具有“隐式begin”
当然,您始终可以实现一个非常复杂的宏,如下所示:
(my-dialect-of-lisp
;; file full of code in your customized dialect of Lisp goes here
)
lisp的my dialogue
宏解析方言(即为该方言实现完整的代码遍历器),并将其翻译成标准lisp
附录:实施
(开始…
(无声明支持):
在模式匹配库的帮助下,这种东西可以更好地表达。我觉得奇怪的是,你喜欢lisp,但不喜欢括号嵌套!这是野兽的天性,不是吗?我最喜欢Lisp的是宏。括号使宏变得更容易,并且是可以容忍的,原因如下:)但我仍在寻找保持括号稀疏的方法。我不清楚
declare\u local
对您有什么好处。只是函数末尾少了一个括号吗?基本上,它避免了函数内部的一堆嵌套的let
。因此,它节省了缩进,是的,也节省了那些令人困惑的结束参数。但这意味着我必须提前声明所有变量,就像以前在C中所做的那样。另外,您的建议基本上等同于(让(x y)…
(我并不需要返回和转到,正如prog
所提供的那样)。@Norswap这是您想要的,不是吗?--好的,所以prog并没有添加太多内容,只是“不让”——您还可以在预先声明一些变量的情况下使用let,以便更接近于使用站点声明,并且总的let数量更小(比完全“嵌套”的编码样式)。或者通过提前完成所有声明来完全消除let。找到适合你的平衡点。:)@Norswap如果您希望声明是本地的,而不是嵌套的,那么它必须是一个宏,比如def<
(my-dialect-of-lisp
;; file full of code in your customized dialect of Lisp goes here
)
(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)))))