【问题标题】:Confused by Lisp Quoting对 Lisp 引用感到困惑
【发布时间】:2012-04-08 05:42:44
【问题描述】:

我有一个关于在 lisp 中评估列表的问题。

为什么没有评估 (a)(+ a 1)

(defun test (a) (+ a 1))

就像(print 4) 不在这里评估

(if (< 1 2) (print 3) (print 4))

(print (+ 2 3)) 在这里被评估

(test (print (+ 2 3)))

它们是标准库函数吗?我可以在我的 lisp 程序中定义类似的函数吗?

【问题讨论】:

标签: lisp eval quote


【解决方案1】:

您可能知道,Lisp 复合表单通常是从外向内处理的。您必须查看最外层嵌套的第一个位置的符号才能理解表单。那个符号完全决定了形式的意义。以下表达式都包含(b c),含义完全不同;因此,我们无法通过首先分析(b c) 部分来理解它们:

;; Common Lisp: define a class A derived from B and C
(defclass a (b c) ())

;; Common Lisp: define a function of two arguments
(defun a (b c) ())

;; add A to the result of calling function B on variable C:
(+ a (b c))

传统上,Lisp 方言将表单分为运算符表单和函数调用表单。运算符形式具有完全任意的含义,由编译或解释该函数的代码段确定(例如,评估简单地递归所有函数调用的参数形式,并将结果值传递给函数)。

从早期的历史开始,Lisp 就允许用户编写自己的运算符。存在两种方法:解释运算符(历史上称为fexprs)和编译运算符称为宏。两者都围绕着接收未评估形式作为参数的函数的想法,以便它可以实现自定义策略,从而用新的行为扩展评估模型。

fexpr 类型操作符在运行时被简单地传递给表单,以及一个环境对象,它可以使用它来查找变量的值等。然后该运算符遍历表单并实现行为。

宏操作符在宏扩展时被传递给表单(这通常发生在读取顶级表单时,就在它们被评估或编译之前)。它的工作不是解释表单的行为,而是通过生成代码来翻译它。 IE。宏是一个迷你编译器。 (生成的代码可以包含更多的宏调用;宏扩展器会处理这些,确保所有宏调用都被抽取。)

fexpr 方法失宠,很可能是因为它效率低下。它基本上使编译变得不可能,而 Lisp 黑客则重视编译。 (Lisp 早在 1960 年左右就已经是一种编译语言了。)fexpr 方法也对词汇环境充满敌意。它需要fexpr,它是一个函数,能够窥视到它被调用的表单的变量绑定环境,这是一种词法范围不允许的封装违规。

宏编写稍微困难一些,并且在某些方面不如 fexprs 灵活,但是从 1960 到 70 年代,Lisp 对宏编写的支持得到了改进,使其尽可能容易。宏最初接收整个表单,然后必须自己解析它。宏定义系统发展成为一种为宏函数提供参数的东西,这些参数以易于理解的部分接收分解的语法,包括语法的一些嵌套方面。还开发了用于编写代码模板的反引号语法,使得代码生成更容易表达。

所以回答你的问题,我自己怎么写这样的表格?例如,如果:

;; Imitation of old-fashioned technique: receive the whole form,
;; extract parts from it and return the translation.
;; Common Lisp defmacro supports this via the &whole keyword
;; in macro lambda lists which lets us have access to the whole form.
;;
;; (Because we are using defmacro, we need to declare arguments "an co &optional al",
;; to make this a three argument macro with an optional third argument, but
;; we don't use those arguments. In ancient lisps, they would not appear:
;; a macro would be a one-argument function, and would have to check the number
;; of arguments itself, to flag bad syntax like (my-if 42) or (my-if).)
;;
(defmacro my-if (&whole if-form an co &optional al)
  (let ((antecedent (second if-form))   ;; extract pieces ourselves
        (consequent (third if-form))    ;; from whole (my-if ...) form
        (alternative (fourth if-form)))
    (list 'cond (list antecedent consequent) (list t alternative))))

;; "Modern" version. Use the parsed arguments, and also take advantage of
;; backquote syntax to write the COND with a syntax that looks like the code.
(defmacro my-if (antecedent consequent &optional alternative)
   `(cond (,antecedent ,consequent) (t ,alternative))))

这是一个合适的例子,因为最初 Lisp 只有cond。 McCarthy 的 Lisp 中没有 if。那个“语法糖”是后来发明的,可能是一个扩展为cond的宏,就像上面的my-if一样。

【讨论】:

    【解决方案2】:

    ifdefun 是宏。宏将表单扩展为一段较长的代码。在展开时,不会评估宏的任何参数。

    当您尝试编写一个函数,但因为需要实现自定义评估策略而遇到困难时,这是一个强烈的信号,表明您应该改为编写宏。

    免责声明:根据您使用的 lisp 类型,ifdefun 在技术上可能被称为“特殊形式”而不是宏,但延迟评估的概念仍然适用。

    【讨论】:

      【解决方案3】:

      Lisp 包含一个表单评估模型。不同的 Lisp 方言有不同的规则。

      让我们看看 Common Lisp。

      • 数据自行评估
      • 通过在评估的参数上调用函数来评估函数形式
      • 根据为每个特殊运算符定义的规则评估特殊形式。 Common Lisp 标准列出了所有这些,以非正式的方式定义了它们的作用,并且没有办法由用户定义新的特殊运算符。
      • 宏形式被转换,结果被评估

      IF、DEFUN 等如何工作以及它们评估的内容、何时执行以及未评估的内容在 Common Lisp 标准中定义。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-10-19
        • 1970-01-01
        • 1970-01-01
        • 2018-06-18
        • 1970-01-01
        • 2012-07-12
        相关资源
        最近更新 更多