【问题标题】:Custom self-quoting forms: Useful?自定义自引用表格:有用吗?
【发布时间】:2014-11-07 00:14:27
【问题描述】:

Lisp 经常声明某些类型是自我评估的。例如。在 emacs-lisp 数字中,"strings"、:keyword-symbols 和其他一些对自己进行评估。

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

也可以创建自定义的自我评估表格,例如

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

定义这种形式是否有任何实际用途,或者这更像是一个深奥的概念?

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

*edit* 我又查了一下,好像把“自我评价”和“自我引用”搞混了。 In emacs lisp the later term was applied to the lambda form,至少在没有词法绑定的上下文中。请注意,即使结果是 equal,lambda 形式永远不会对自身求值 (eq)。

(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

正如 Joshua 在他的回答中所说:eval 函数的定点(相对于 equal)。

【问题讨论】:

    标签: lisp elisp


    【解决方案1】:

    您提供的代码没有定义一种自我评估形式。作为参数传递时 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)
    

    现在,对于 equal,这确实为您提供了 evalfixed point,因为

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

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

    自求值形式也是关于 equals 的不动点,但在 Common Lisp 中,自求值形式是 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 返回的函数是两个函数 参数,称为扩展函数。要扩展编译器宏, 扩展函数是通过调用宏扩展钩子来调用的 扩展函数作为它的第一个参数,整个编译器 宏形式作为它的第二个参数,以及当前编译 环境(或者使用当前的词法环境,如果形式是 被编译文件以外的东西处理)作为它的第三个 争论。宏展开钩子依次调用展开函数 形式作为第一个参数,环境作为第二个参数 争论。 扩展函数的返回值,即 由宏扩展钩子传递,可能是相同的形式, 或者一种可以由代码自行决定的表格 展开,用来代替原来的形式

    DEFINE-COMPILER-MACRO

    • 与普通宏不同,编译器宏可以仅通过返回与原始格式相同的形式来拒绝提供扩展 (可以使用 &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))
    

    请注意,这与自评估表单并不完全相同,因为编译器仍在检查表单是否是其汽车具有关联编译器宏的 cons,然后调用该编译器宏功能与形式。但它的相似之处在于表单转到 something 并且 same 表单返回的情况很重要。

    【讨论】:

    • 感谢您澄清我对该术语的误解。看来我混淆了 self-evaluatingself-quoting (请参阅问题的扩展)。您是否碰巧知道后者的实际用途,即定点行为?
    • @kdb 是的,至少有一个重要的地方:编译器宏。我已经更新了我的答案。
    • 我现在只是回顾一下,我认为我的自我评估谓词中有一个错误。以 quote 开头的列表不应该是自我评估的,它应该评估到列表的第二个元素......
    【解决方案2】:

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

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

    可能评估为

    -> (foo 3)
    

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

    form 是一些需要评估的数据。它必须是有效的 Lisp 代码。

    关于表格评估

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

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

    上面的向量是怎么回事? (foo) 是否返回一个以向量作为第一个元素的列表?

    在 Common Lisp 中,这种向量形式是自求值的。在其他一些 Lisp 中,情况有所不同。在一些较旧的 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 方言要求除了符号和 conses 之外的所有形式都自评估请参阅 CL 标准:Self-Evaluating Objects .这也与使用解释器或编译器无关。

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

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

    【讨论】:

    • 我好像把self-evalatingself-quoting混为一谈了,但它似乎带来了一些非常有趣的信息:)跨度>
    猜你喜欢
    • 2014-09-13
    • 1970-01-01
    • 1970-01-01
    • 2013-06-01
    • 1970-01-01
    • 2020-09-22
    • 1970-01-01
    • 2015-03-29
    • 2013-07-25
    相关资源
    最近更新 更多