代码之家  ›  专栏  ›  技术社区  ›  Cristián Romo

在Lisp中何时使用'(或引号)?

  •  99
  • Cristián Romo  · 技术社区  · 16 年前

    在完成了Lisp入门书的主要部分之后,我仍然不明白这个特殊的操作员是什么。 (quote) (或同等) ' )函数是这样的,但这已经覆盖了我所见过的所有Lisp代码。

    它是做什么的?

    9 回复  |  直到 6 年前
        1
  •  160
  •   Mateen Ulhaq    6 年前

    简短回答 绕过默认评估规则并执行 计算表达式(symbol或s-exp),并将其与类型完全相同地传递给函数。

    长答案:默认评估规则

    当调用一个常规函数(稍后我将讨论)时,将计算传递给它的所有参数。这意味着你可以写下:

    (* (+ a 2)
       3)
    

    然后评估 (+ a 2) 通过评估 a 2。符号的值 在当前变量绑定集中查找,然后替换。说 当前绑定到值3:

    (let ((a 3))
      (* (+ a 2)
         3))
    

    我们会得到 (+ 3 2) 然后在3和2上调用,+生成5。我们原来的形式是现在 (* 5 3) 产量15。

    解释 quote 已经!

    好吧。如上所示,对一个函数的所有参数都进行了计算,因此如果要传递 符号 而不是它的价值,你不想评估它。Lisp符号既可以作为它们的值,也可以作为其他语言中使用字符串(如哈希表的键)的标记。

    这里就是 引用 进来。假设您想从一个python应用程序中绘制资源分配,而不是在lisp中绘制。让您的python应用程序执行如下操作:

    print("'(")
    while allocating:
        if random.random() > 0.5:
            print(f"(allocate {random.randint(0, 20)})")
        else:
            print(f"(free {random.randint(0, 20)})")
        ...
    print(")")
    

    让您的输出看起来像这样(稍微预处理一下):

    '((allocate 3)
      (allocate 7)
      (free 14)
      (allocate 19)
      ...)
    

    记住我说过的话 引用 (勾选)导致默认规则不适用?很好。否则会发生的是 allocate free 我们不想这样。在我们的Lisp中,我们希望:

    (dolist (entry allocation-log)
      (case (first entry)
        (allocate (plot-allocation (second entry)))
        (free (plot-free (second entry)))))
    

    对于上面给出的数据,将执行以下函数调用序列:

    (plot-allocation 3)
    (plot-allocation 7)
    (plot-free 14)
    (plot-allocation 19)
    

    但是呢? list ?

    嗯,有时候你 想要评估参数。假设您有一个漂亮的函数处理一个数字和一个字符串,并返回一个结果列表…东西。让我们做一个错误的开始:

    (defun mess-with (number string)
      '(value-of-number (1+ number) something-with-string (length string)))
    
    Lisp> (mess-with 20 "foo")
    (VALUE-OF-NUMBER (1+ NUMBER) SOMETHING-WITH-STRING (LENGTH STRING))
    

    嘿!这不是我们想要的。我们想要 选择性地 评估一些参数,将其他参数保留为符号。试试~ 2!

    (defun mess-with (number string)
      (list 'value-of-number (1+ number) 'something-with-string (length string)))
    
    Lisp> (mess-with 20 "foo")
    (VALUE-OF-NUMBER 21 SOMETHING-WITH-STRING 3)
    

    不只是 引用 但是 backquote

    好多了!顺便说一句,这种模式在宏(大部分)中很常见,因此有专门的语法来实现这一点。背引号:

    (defun mess-with (number string)
      `(value-of-number ,(1+ number) something-with-string ,(length string)))
    

    就像使用 引用 ,但可以通过在某些参数前面加逗号来显式计算这些参数。结果相当于使用 列表 但是,如果从宏生成代码,则通常只需要计算返回的代码的一小部分,因此后引号更适合。对于较短的列表, 列表 更具可读性。

    嘿,你忘了 引用 !

    那么,这会把我们留在哪里?哦,对了,什么事 引用 事实上呢?它只是简单地返回它的参数没有被计算!还记得我刚开始讲的关于正则函数的内容吗?结果表明,一些运算符/函数需要 评估他们的论点。比如,如果——如果不采用其他分支,您不希望对其进行评估,对吧?所谓 特种作业人员 和宏一起工作。特殊的操作符也是语言的“公理”——最小的规则集——通过以不同的方式将它们组合在一起,您可以在此基础上实现Lisp的其余部分。

    回到 引用 虽然:

    Lisp> (quote spiffy-symbol)
    SPIFFY-SYMBOL
    
    Lisp> 'spiffy-symbol ; ' is just a shorthand ("reader macro"), as shown above
    SPIFFY-SYMBOL
    

    比较(在钢铁银行通用LISP上):

    Lisp> spiffy-symbol
    debugger invoked on a UNBOUND-VARIABLE in thread #<THREAD "initial thread" RUNNING   {A69F6A9}>:
      The variable SPIFFY-SYMBOL is unbound.
    
    Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.
    
    restarts (invokable by number or by possibly-abbreviated name):
      0: [ABORT] Exit debugger, returning to top level.
    
    (SB-INT:SIMPLE-EVAL-IN-LEXENV SPIFFY-SYMBOL #<NULL-LEXENV>)
    0] 
    

    因为没有 spiffy-symbol 在当前范围内!

    总结

    引用 , 背引号 (用逗号),和 列表 是用于创建列表的一些工具,这些工具不仅是值列表,而且可以作为轻量级工具使用(无需定义 struct )数据结构!

    如果你想了解更多,我推荐彼得·塞贝尔的书 Practical Common Lisp 对于学习Lisp的实用方法,如果您已经开始学习编程了。最终,在您的Lisp旅程中,您也将开始使用软件包。Ron Garret The Idiot's Guide to Common Lisp Packages 会给你很好的解释。

    快乐黑客!

        2
  •  48
  •   Adam Rosenfield    16 年前

    上面写着“不要评价我”。例如,如果您想使用一个列表作为数据,而不是作为代码,那么您应该在它前面放一个引号。例如,

    (print '(+ 3 4)) 打印“(+3 4)”,鉴于 (print (+ 3 4)) 打印“7”

        3
  •  17
  •   Xanthir    16 年前

    其他人对此问题的回答令人钦佩,马蒂亚斯·本卡德提出了一个极好的警告。

    不要使用引号来创建以后要修改的列表。规范允许编译器将引用的列表视为常量。通常,编译器会通过在内存中为常量创建一个值,然后从常量出现的所有位置引用该值来优化常量。换句话说,它可以将常量视为匿名全局变量。

    这会导致明显的问题。如果修改一个常量,它很可能会在完全不相关的代码中修改相同常量的其他用途。例如,您可以在某些函数中将某个变量与“(1 1)”进行比较,在完全不同的函数中,用“(1 1)”开始一个列表,然后向其中添加更多内容。在运行这些函数时,您可能会发现第一个函数不再正确匹配事物,因为它现在正试图将变量与(1 1 2 3 5 8 13)进行比较,这就是第二个函数返回的结果。这两个函数是完全不相关的,但由于使用常量,它们对彼此都有影响。甚至可能发生更疯狂的不良影响,就像一个完全正常的列表迭代突然无限循环。

    当需要常量列表时使用引号,例如用于比较。修改结果时使用列表。

        4
  •  14
  •   Matthias Benkard    16 年前

    这个问题的一个答案是,引号__创建了列表数据结构__。这不太对。引用比这更重要。事实上,引号是一个微不足道的运算符:其目的是 防止 任何事情都不会发生。尤其是,它不会创造任何东西。

    (引语x)说的基本上是什么都不做,只要给我x。x不必是(引语(a b c))中的列表或(引语foo)中的符号。它可以是任何物体。实际上,对(list'quote some-object)生成的列表进行评估的结果总是返回some-object,不管它是什么。

    现在,(引号(a b c))似乎创建了一个元素为a、b和c的列表,原因是这样的列表实际上是它返回的;但是在计算引号表单时,该列表通常已经存在一段时间了(作为引号表单的一个组件!)在代码执行之前由加载程序或读卡器创建。

    这一点的一个暗示是,修改报价表返回的列表是非常不明智的。报价返回的数据,无论出于何种目的,都应被视为 代码 正在执行,因此应视为只读!

        5
  •  10
  •   Kyle Burton    16 年前

    引号阻止窗体的执行或计算,将其转换为数据。一般来说,您可以执行数据,然后对其进行评估。

    Quote创建列表数据结构,例如,以下是等效的:

    (quote a)
    'a
    

    它还可以用于创建列表(或树):

    (quote (1 2 3))
    '(1 2 3)
    

    你最好是拿一本关于口齿不清的入门书,比如 Practical Common Lisp (可在线阅读)。

        6
  •  2
  •   unknownerror    10 年前

    当我们想要传递参数本身而不是传递参数的值时,我们使用引号。它主要与使用列表、对和原子时传递的过程有关。 这在C编程语言中是不可用的(大多数人开始使用C编程,因此我们会感到困惑) 这是Scheme编程语言中的代码,它是Lisp的一种方言,我想您可以理解这段代码。

    (define atom?              ; defining a procedure atom?
      (lambda (x)              ; which as one argument x
    (and (not (null? x)) (not(pair? x) )))) ; checks if the argument is atom or not
    (atom? '(a b c)) ; since it is a list it is false #f
    

    最后一行(原子??abc)正在按程序传递abc,以检查abc是否为原子,但当您传递(atom?abc)然后检查abc的值,并将值传递给它。从那以后,我们没有给它提供任何价值

        7
  •  2
  •   jhegedus    8 年前

    在Emacs Lisp中:

    可以引用什么?

    列表和符号。

    引用一个数字等于该数字本身: '5 是一样的 5 .

    当你引用列表时会发生什么?

    例如:

    '(one two) 评估为

    (list 'one 'two) 其计算结果为

    (list (intern "one") (intern ("two"))) .

    (intern "one") 创建一个名为“one”的符号,并将其存储在“中心”哈希图中,因此只要您说 'one 然后这个符号命名为 "one" 将在中心哈希图中查找。

    但什么是象征?

    例如,在OO语言(Java/JavaScript / Python)中,符号可以表示为具有 name 字段,符号的名称类似于 “一” 和数据和/或代码可以与此对象关联。

    因此,python中的符号可以实现为:

    class Symbol:
       def __init__(self,name,code,value):
           self.name=name
           self.code=code
           self.value=value
    

    例如,在EmacsLisp中,符号可以有1)与之关联的数据和(同时-对于同一符号)2)与之关联的代码-根据上下文,可以调用数据或代码。

    例如,在elisp中:

    (progn
      (fset 'add '+ )
      (set 'add 2)
      (add add add)
    )
    

    评估为 4 .

    因为 (add add add) 评估如下:

    (add add add)
    (+ add add)
    (+ 2 add)
    (+ 2 2)
    4
    

    例如,使用 Symbol 我们在上面的python中定义的类,这个 add elisp符号可以用python编写为 Symbol("add",(lambda x,y: x+y),2) .

    非常感谢IRC Emacs上的人向我解释符号和引用。

        8
  •  0
  •   Steve    9 年前

    QUOTE返回其参数的内部表示形式。在翻阅了太多关于引用什么的解释之后 是的,这时灯泡亮了。如果在引用函数名时repl没有将其转换为大写,我可能就不会明白。

    所以。普通的lisp函数将其参数转换为内部表示,计算参数并应用函数。Quote将其参数转换为内部表示,并只返回它。从技术上讲,这句话是正确的,说“不要评估”,但当我试图理解它做了什么,告诉我它没有做什么是令人沮丧的。我的烤面包机也不评估lisp函数;但这不是你解释烤面包机做什么的方式。

        9
  •  0
  •   Andrew_1510    9 年前

    另一个简短的回答:

    quote 意味着不需要评估,以及 背引号 是引述但离开 后门 .

    一个很好的参考:

    Emacs Lisp参考手册非常清楚

    9.3引用

    特殊形式的引号返回其所写的单个参数,而不进行计算。这提供了一种在程序中包含常量符号和列表的方法,这些常量符号和列表不是自评估对象。(无需引用自评估对象,如数字、字符串和向量。)

    特殊形式:引用对象

    This special form returns object, without evaluating it. 
    

    因为在程序中经常使用引号,所以lisp为它提供了一种方便的读取语法。一个撇号字符(___)后跟一个lisp对象(在读语法中),它扩展到一个列表,该列表的第一个元素是引号,第二个元素是对象。因此,读取语法'x是(引号x)的缩写。

    下面是一些使用引号的表达式示例:

    (quote (+ 1 2))
         ⇒ (+ 1 2)
    
    (quote foo)
         ⇒ foo
    
    'foo
         ⇒ foo
    
    ''foo
         ⇒ (quote foo)
    
    '(quote foo)
         ⇒ (quote foo)
    

    9.4背引号

    后引号结构允许引用列表,但有选择地评估该列表的元素。在最简单的情况下,它与特殊形式的引号相同(在前一节中描述;请参见引号)。例如,这两种形式产生相同的结果:

    `(a list of (+ 2 3) elements)
         ⇒ (a list of (+ 2 3) elements)
    
    '(a list of (+ 2 3) elements)
         ⇒ (a list of (+ 2 3) elements)
    

    后引号的参数内部的特殊标记__、_稹表示一个非_稹t常量的值。Emacs Lisp Evaluator评估_、_的参数,并将该值放入列表结构中:

    `(a list of ,(+ 2 3) elements)
         ⇒ (a list of 5 elements)
    

    用__替换__,也允许在列表结构的更深层进行替换。例如:

    `(1 2 (3 ,(+ 4 5)))
         ⇒ (1 2 (3 9))
    

    您还可以使用特殊标记_,@226;_将评估值拼接到结果列表中。拼接列表的元素将成为与结果列表的其他元素处于同一级别的元素。不使用__` _秷的等效代码通常不可读。以下是一些例子:

    (setq some-list '(2 3))
         ⇒ (2 3)
    
    (cons 1 (append some-list '(4) some-list))
         ⇒ (1 2 3 4 2 3)
    
    `(1 ,@some-list 4 ,@some-list)
         ⇒ (1 2 3 4 2 3)