3

我喜欢 Lisp,但我觉得它令人讨厌的一件事是它嵌套太多。

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

int x = someFunctionCall() ? someOtherFunctionCall() : 42;
int y = myUnterminableNameFunction(x);

代替

int x = myUnterminableNameFunction(someFunctionCall() ? someOtherFunctionCall() : 42);

这也可以在 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))

如果它不存在,是否可以通过一个聪明的宏来实现,而不会对性能产生不利影响?

4

4 回答 4

8

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

(let* ((x (if (some-function-call)
             (some-other-call)
             42))
       (y (my-unterminable-name-function x)))
   (bla-bla-bla)
   ...)

它确实筑巢,但不是那么多。

从 开始declare-local,它必须由一些外部宏处理。例如,您可以在其主体中编写my-defun检查declare-local并对其进行转换。 Upd但它有点反 Lisp。Lisp 形式通常只影响嵌套形式。 cl:declare是我能想到的唯一例外。

于 2013-07-01T06:26:48.940 回答
5

是的,在 Common Lisp 中使用&aux参数:

(defun foo (a b &aux x y z)
  (setq x ...)
  (setq y ...)
  .... )

或使用prog功能”

(defun bar (a b)
  (prog (x y)
    (setq x a y b) 
    ....
    (return 42)
    ))
于 2013-06-30T19:50:41.963 回答
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)

比如说,这段代码:

(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)))))

这种事情在模式匹配库的帮助下可以更好地表达。

于 2013-07-19T19:47:13.943 回答
4

这是一个概念证明宏,它将变量声明从平面列表中提取到标准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
于 2013-07-02T16:22:31.700 回答