有没有办法在Lisp中声明局部变量(避免让)?

Nor*_*wap 2 lisp let

我喜欢Lisp,但我发现其中一件令人讨厌的事情就是它太过分了.

在命令式编程语言中,我可以通过使用中间值来打破长表达式,例如:

int x = someFunctionCall() ? someOtherFunctionCall() : 42;
int y = myUnterminableNameFunction(x);
Run Code Online (Sandbox Code Playgroud)

代替

int x = myUnterminableNameFunction(someFunctionCall() ? someOtherFunctionCall() : 42);
Run Code Online (Sandbox Code Playgroud)

这也可以在Lisp中完成,但据我所知,只能使用let.let引入了额外的嵌套级别,我宁愿避免.

我不打算争论这个意见,而是想找到一种方法在单个非嵌套函数/宏调用中声明局部变量.类似declare_local下面的内容:

(defun my_function (a b)
   (declare_local x (if (some_function_call) (some_other_function_call) 42))
   (my_unterminable_name_function x))
Run Code Online (Sandbox Code Playgroud)

如果它不存在,它可以通过一个聪明的宏实现,而不会对性能有害吗?

mon*_*oid 8

您可以使用let*表单进行顺序绑定.

(let* ((x (if (some-function-call)
             (some-other-call)
             42))
       (y (my-unterminable-name-function x)))
   (bla-bla-bla)
   ...)
Run Code Online (Sandbox Code Playgroud)

它确实嵌套,但不是那么多.

截至declare-local,它必须由一些外部宏处理.例如,您可以在其正文中编写my-defun该检查declare-local并对其进行转换. 更新,但它有点反Lisp.Lisp表单通常只影响嵌套表单. cl:declare是我能想到的唯一例外.


Wil*_*ess 5

Yes, in Common Lisp use &aux arguments:

(defun foo (a b &aux x y z)
  (setq x ...)
  (setq y ...)
  .... )
Run Code Online (Sandbox Code Playgroud)

Or use the "prog feature":

(defun bar (a b)
  (prog (x y)
    (setq x a y b) 
    ....
    (return 42)
    ))
Run Code Online (Sandbox Code Playgroud)

  • @Norswap如果你希望你的声明是本地的,只是没有嵌套,那么它必须是一个宏,比如`def`,它会识别,比如说,`(deflocal ......)`形式并将事物重新组合成嵌套的let. (3认同)

Kaz*_*Kaz 5

不,没有任何形式可以出现在类似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)
Run Code Online (Sandbox Code Playgroud)

比如这段代码:

(progn
  a b c
  (let ((x 42))
    d e
    (let ((y 'foo) (z))
      f g)))
Run Code Online (Sandbox Code Playgroud)

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
  )
Run Code Online (Sandbox Code Playgroud)

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)))))
Run Code Online (Sandbox Code Playgroud)

借助模式匹配库可以更好地表达这种事物.