有没有一种方法可以在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)))))