自定义自我引用形式:有用吗?

时间:2014-11-07 00:14:27

标签: lisp elisp

Lisps经常声明,某些类型是自我评估的。例如。在emacs-lisp数字,"字符串",:关键字 - 符号和一些更多评估自己。

或者,更具体地说:评估表单并再次评估结果会得到相同的结果。

还可以创建自定义自我评估表单,例如

(defun my-list (&rest args) 
  (cons 'my-list (mapcar (lambda (e) (list 'quote e)) args)))

(my-list (+ 1 1) 'hello)
  => (my-list '2 'hello)

(eval (my-list (+ 1 1) 'hello))
  => (my-list '2 'hello)

是否有任何实际用途来定义此类表格,或者这更像是一个深奥的概念?

我想过创建"自定义类型"作为自我评估表单,其中评估可以例如对参数执行类型检查。当我在我的代码中尝试使用这些类型时,我通常发现它与简单地工作相比是不方便的。但是还有。但

*编辑* 我再次检查了,似乎我混淆了#34;自我评估"和#34;自我引用"。 In emacs lisp the later term was applied to the lambda form,至少在没有词汇绑定的情况下。请注意,即使结果为eq,lambda表单也不会自行计算(equal)。

(setq form '(lambda () 1))              ;; => (lambda () 1)
(equal form (eval form))                ;; => t
(equal (eval form) (eval (eval form)))  ;; => t
(eq form (eval form))                   ;; => nil
(eq (eval form) (eval (eval form)))     ;; => nil

正如约书亚在答案中所说的那样:eval函数的固定点(关于equal)。

2 个答案:

答案 0 :(得分:5)

您提供的代码并未定义一种自我评估表单。一个自我评估表单,当eval作为参数传递时它将返回。让我们仔细看看。首先,有一个函数需要一些参数并返回一个新列表:

(defun my-list (&rest args) 
  (cons 'my-list (mapcar (lambda (e) (list 'quote e)) args)))

新列表的符号为 my-list 作为第一个元素。其余元素是包含符号 quote 的两元素列表以及传递给函数的元素:

(my-list (+ 1 1) 'hello)
;=> (my-list '2 'hello)

现在,对于相等,这确实为 eval 提供fixed point,因为

(eval (my-list (+ 1 1) 'hello))
;=> (my-list '2 'hello)

(eval (eval (my-list (+ 1 1) 'hello)))
;=> (my-list '2 'hello)

自我评估形式也是等于的固定点,但在Common Lisp中,自我评估形式是一个固定点< strong> eval 关于 eq (或者 eql )。

指定自我评估表单的语言的关键在于定义评估者与表单有什么关系。从概念上讲, eval 的定义如下:

(defun self-evaluating-p (form)
  (or (numberp form)
      (stringp form)
      (and (listp form)
           (eql 2 (length form))
           (eq 'quote (first form)))
      ; ...
      ))

(defun eval (form)
  (cond
    ((self-evaluating-p form) form)
    ((symbolp form) (symbol-value-in-environment form))
    ;...
    ))

关键不在于自我评估表单是评估等价(对于某些等价关系)值的表单,而是 eval 不必执行任何操作的表单。工作

编译器宏

虽然对于自我评估(模数等价)关系的表单通常没有很多用途,但有一个非常重要的地方使用了非常相似的东西Common Lisp:compiler macros(重点补充):

  

3.2.2.1 Compiler Macros

     

compiler-macro-function返回的函数是2的函数   参数,称为扩展函数。要扩展编译器宏,   通过调用macroexpand hook调用扩展函数   扩展函数作为它的第一个参数,整个编译器   宏形式作为其第二个参数,以及当前的编译   环境(或当前的词汇环境,如果形式是   由第三个以外的其他东西处理   论点。反过来,macroexpand钩子调用扩展函数   表单作为第一个参数,环境作为第二个参数   论点。 扩展函数的返回值,即   通过macroexpand钩子传递,可能是相同的形式,   或者是一个表格,可以由代码自行决定   扩展,用于代替原始形式

  

DEFINE-COMPILER-MACRO

     
      
  • 与普通的宏不同,编译器宏只能通过返回与原始格式相同的表单来拒绝提供扩展   (可以通过使用&amp; whole获得)。
  •   

举个例子:

(defun exponent (base power)
  "Just like CL:EXPT, but with a longer name."
  (expt base power))

(define-compiler-macro exponent (&whole form base power)
  "A compiler macro that replaces `(exponent base 2)` forms
with a simple multiplication.  Other invocations are left the same."
  (if (eql power 2)
      (let ((b (gensym (string '#:base-))))
        `(let ((,b ,base))
           (* ,b ,b)))
      form))

请注意,这与自我评估表格并不完全相同,因为编译器仍在检查表单是否为汽车具有关联编译器宏的缺点,然后调用该表单用表格编译宏函数。但它的相似之处在于表单转到了某些东西,而相同的形式回归的情况很重要。

答案 1 :(得分:3)

您所描述的内容和自我评估表单(不是类型!)是无关的。

? (list (foo (+ 1 2)))

可评估为

-> (foo 3)

但是那个运行函数foo并且它返回一些带有符号foo的列表及其第一个参数值。而已。你已经写了一个函数。但不是自定义自我评估表单

表单是一些要评估的数据。它需要是有效的Lisp代码。

关于表格评估

当你有这样的来源时,评估表格是一个主题:

(defun foo ()
  (list #(1 2 3)))

上述载体的内容是什么? (foo)是否返回一个以向量作为第一个元素的列表?

在Common Lisp中,这种矢量形式是自我评估的。在其他一些Lisps中它是不同的。在一些较旧的Lisp方言中,可能必须编写下面的代码以使编译器高兴。它甚至可能与解释器不同。 (我之前在标准Lisp的一些变体的实现中已经看过这个。)

(defun foo ()
  (list '#(1 2 3)))    ; a vector form quoted

注意引用。必须引用非自我评估表格。这样做比较容易。您必须查看源代码并确保引用此类表单。但还有另一个问题使其更加困难。这些数据对象可能是由代码中的宏引入的。因此,还必须确保宏生成的所有代码都引用了所有文字数据。这真是一种痛苦。

这在其他一些Lisp方言中是错误的(不是在Common Lisp中):

(defmacro foo (a)
  (list 'list a #(1 2 3)))

或甚至(注意添加的quote

(defmacro foo (a)
  (list 'list a '#(1 2 3)))

使用

(foo 1)

将是代码(list 1 #(1 2 3))。但是在这些Lisps中会有一个引用缺失...所以那里错了。

一个人必须写:

(defmacro foo (a)
  (list 'list a ''#(1 2 3)))  ; note the double quote

因此

(foo 1)

将是代码(list 1 '#(1 2 3))。然后工作。

为了摆脱这些问题,像Common Lisp这样的Lisp方言要求除符号和词汇之外的所有形式都是自我评估参见CL标准:{{3 }}。这也与使用解释器或编译器无关。

请注意,Common Lisp也没有提供改变它的机制。

使用自定义mechanim可以做些什么?可以让数据表单评估为不同的东西。或者可以实施不同的评估方案。但在Common Lisp中没有类似的东西。基本上我们将符号作为变量,将其作为特殊形式/函数/宏,其余的是自我评估。对于任何不同的东西,您需要编写自定义评估程序/编译器。