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
)。
答案 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中没有类似的东西。基本上我们将符号作为变量,将其作为特殊形式/函数/宏,其余的是自我评估。对于任何不同的东西,您需要编写自定义评估程序/编译器。