何时在Lisp中使用(或引用)?
在介绍Lisp书的主要部分之后,我仍然不明白特殊的运算符(quote)
(或等价'
)函数做了什么,但是这已经遍布我所见过的Lisp代码。
它有什么作用?
简短的回答绕过默认的评估规则,不要评估expression式(符号或s-exp),将其按照input的顺序传递给函数。
长答案:默认评估规则
当一个常规(我会来的)函数被调用,所有传递给它的参数都会被计算。 这意味着你可以写这个:
(* (+ a 2) 3)
依次通过评估a
和2来评估(+ a 2)
。在当前的variables绑定集中查找符号a
的值,然后被replace。 说a
目前绑定的值3:
(let ((a 3)) (* (+ a 2) 3))
我们会得到(+ 3 2)
,然后在3和2上调用+,得到5.我们的原始forms现在是(* 5 3)
产生15。
解释quote
已经!
好的。 如上所示,函数的所有参数都被计算,所以如果你想传递符号而不是它的值,你不想评估它。 Lisp符号可以将它们的值都加倍,并且在其他语言中使用string的标记(例如键到哈希表)。
这就是quote
地方。假设你想从Python应用程序绘制资源分配,而是在Lisp中进行绘图。 让你的Python应用程序做这样的事情:
print "'(" while allocating: if random.random() > 0.5: print "(allocate %d)" random.randint(0, 20) else: print "(free %d)" % random.randint(0, 20) ... print ")"
给你输出看起来像这样(稍微满意):
'((allocate 3) (allocate 7) (free 14) (allocate 19) ...)
请记住我所说的quote
(“勾号”)导致默认规则不适用? 好。 否则会发生的是, 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
?
那么,有时候你想评估一下这个论点。 假设你有一个漂亮的函数来处理一个数字和一个string,并返回结果列表。 让我们做一个错误的开始:
(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))
嘿! 这不是我们想要的。 我们希望有select地评估一些论点,并把其他论点作为符号。 试试#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)
不只是quote
, backquote
quote
好多了! 可以肯定的是,这个模式在(大部分)macros中是很常见的,有这样的特殊语法。 反引号:
(defun mess-with (number string) `(value-of-number ,(1+ number) something-with-string ,(length string)))
这就像使用quote
,但可以通过用逗号前缀来显式评估一些参数。 结果等同于使用list
,但是如果从macros生成代码,则通常只需要评估返回的代码的小部分,因此反引用更适合。 对于较短的列表, list
可以更具可读性。
嘿,你忘了关于quote
!
那么,这个离开我们呢? 哦,对, quote
实际上是做什么的? 它只是简单地返回它的论点(s)未评价! 还记得我刚才讲的正规职能吗? 结果发现一些操作符/函数不需要评估它们的参数。 比如IF – 如果没有被采用,你不希望else分支被评估,对吧? 所谓的特殊操作符和macros一起就是这样工作的。 特殊的操作符也是语言的“公理” – 最less的一组规则 – 你可以通过不同的方式将它们结合在一起来实现Lisp的其余部分。
回到quote
,但:
Lisp> (quote spiffy-symbol) SPIFFY-SYMBOL Lisp> 'spiffy-symbol ; ' is just a shorthand ("reader macro"), as shown above SPIFFY-SYMBOL
比较(在Steel-Bank Common 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
!
加起来
quote
, backquote
(用逗号)和list
是你用来创build列表的一些工具,不仅是值列表,而且正如你看到的,可以用作轻量级的(不需要定义struct
)数据结构!
如果您想了解更多信息,我build议Peter Seibel的Practical Common Lisp一本实用的Lisp学习方法,如果您已经在编程。 最后在你的Lisp旅程中,你也会开始使用软件包。 Ron Garret的Common Lisp软件包的白痴指南将给你很好的解释。
快乐的黑客!
它说“不要评价我”。 例如,如果你想使用一个列表作为数据,而不是作为代码,你会在前面加一个引号。 例如,
(print '(+ 3 4))
打印“(+ 3 4)”,而(print (+ 3 4))
打印“7”
其他人已经很好地回答了这个问题,Matthias Benkard提出了一个很好的警告。
不要使用引用来创build你将会修改的列表。 该规范允许编译器将引用列表视为常量。 通常情况下,编译器会通过在内存中为它们创build单个值来优化常量,然后从常量出现的所有位置引用该单个值。 换句话说,它可以像一个匿名的全局variables一样对待常量。
这可能会导致明显的问题。 如果你修改一个常量,它可能很好地修改了完全不相关的代码中相同常量的其他用法。 例如,你可以在一些函数中比较一些variables到'(1 1),在一个完全不同的函数中,用'(1 1)开始一个列表,然后添加更多的东西。 在运行这些函数时,可能会发现第一个函数不能正确地匹配事物,因为它现在试图将variables与第二个函数返回的“(1 1 2 3 5 8 13)”进行比较。 这两个函数是完全不相关的,但是由于常量的使用,它们相互影响。 即使更疯狂的坏的影响可能发生,就像一个完美的正常列表迭代突然无限循环。
当你需要一个常数列表时使用引号,比如比较。 使用列表,当你将修改结果。
这个问题的一个答案是QUOTE“创build列表数据结构”。 这是不正确的。 QUOTE比这更重要。 事实上,QUOTE是一个微不足道的操作符:它的目的是防止任何事情发生。 特别是,它不创造任何东西。
(QUOTE X)所说的基本上是“不要做任何事情,只要给我X.”X不必如(QUOTE(ABC))中的列表或QUOTE FOO中的符号。 它可以是任何对象。 实际上,评估由(LIST'QUOTE SOME-OBJECT)产生的列表的结果将总是返回SOME-OBJECT,不pipe它是什么。
现在,(QUOTE(ABC))看起来像创build了一个元素是A,B和C的列表的原因是这样的列表真的是它返回的; 但在评估QUOTE表单时,列表通常已经存在了一段时间(作为QUOTE表单的一个组件),在执行代码之前由加载器或读取器创build。
其中一个隐含的意思是,经常更新新手是因为修改QUOTE表单返回的列表是非常不明智的。 QUOTE返回的数据是为了所有的意图和目的,被视为正在执行的代码的一部分,因此应该被视为只读!
报价阻止执行或评估表单,而是将其转换为数据。 一般来说,您可以通过评估来执行数据。
报价创build列表数据结构,例如下面是等价的:
(quote a) 'a
它也可以用来创build列表(或树):
(quote (1 2 3)) '(1 2 3)
你可能最好拿一本关于lisp的介绍性书籍,比如Practical Common Lisp (可以在线阅读)。
当我们想传递一个参数本身而不是传递参数的值时,我们使用quote。 这主要与使用C编程语言中不能使用的列表,对和primefaces的过程有关(大多数人开始使用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? '(abc)) ; since it is a list it is false #f
最后一行(atom?'abc)正在传递abc,因为它是检查abc是否是primefaces的程序,但是当你传递(atom?abc)时,它会检查abc的值,并将值传递给它。 因为,我们没有提供任何价值
在Emacs Lisp中:
什么可以引用?
列表和符号。
引用一个数字评估数字本身: '5
和5
。
当你引用列表时会发生什么?
例如:
'(one two)
评估
(list 'one 'two)
评估
(list (intern "one") (intern ("two")))
。
(intern "one")
会创build一个名为“one”的符号,并将其存储在“中央”哈希映射中,所以无论何时您说"one"
都会在该中央哈希映射中查找名为"one"
的符号。
但是什么是符号?
例如,在OO语言(Java / Javascript / Python)中,符号可以表示为具有name
字段的对象,该name
字段是符号名称,如上面的"one"
,并且数据和/或代码可以与其关联这个对象。
所以Python中的符号可以被实现为:
class Symbol: def __init__(self,name,code,value): self.name=name self.code=code self.value=value
在Emacs Lisp中,例如一个符号可以有1)和它相关的数据AND(同时 – 对于同一个符号)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
例如,使用我们在上面Python中定义的Symbol
类,这个add
ELisp-Symbol可以用Python Symbol("add",(lambda x,y: x+y),2)
作为Python编写。
非常感谢IRC #emacs上的人向我解释符号和引号。
引用返回其参数的内部表示。 在对报价没有做的事情进行太多的解释之后,就是灯泡继续运转的时候。 如果REPL在我引用它时没有把函数名转换为UPPER-CASE,那么我可能就不会明白了。
所以。 普通的Lisp函数将它们的参数转换为内部表示,评估参数并应用函数。 引用将其参数转换为内部表示forms,并返回它。 从技术上讲,引用“不要评价”是正确的,但是当我试图理解它做了什么,告诉我什么没有做到让人沮丧。 我的烤面包机也不评估Lispfunction; 但这不是你如何解释烤面包机的作用。
Anoter简短的回答:
quote
意味着没有评估, 反引号是报价,但留下门 。
一个很好的参考:
Emacs Lisp参考手册说得很清楚
9.3引用
特殊的forms引用返回它的单一的论点,没有评估它写。 这提供了一种在程序中包含常量符号和列表(不是自我评估对象)的方法。 (没有必要引用自我评估的对象,例如数字,string和向量。)
特殊forms:报价对象
This special form returns object, without evaluating it.
由于在程序中经常使用quote,Lisp为它提供了一个方便的读取语法。 一个撇号字符(''')后跟一个Lisp对象(在读取语法中)展开为一个列表,其第一个元素是quote,第二个元素是该对象。 因此,读取语法'x是(quote x)的缩写。
下面是一些使用quote的expression式的例子:
(quote (+ 1 2)) ⇒ (+ 1 2) (quote foo) ⇒ foo 'foo ⇒ foo ''foo ⇒ (quote foo) '(quote foo) ⇒ (quote foo)
9.4反引号
反引号构造允许引用列表,但是select性地评估该列表的元素。 在最简单的情况下,它与特殊的forms报价(在前面的章节中描述;参见引用)相同。 例如,这两种forms产生相同的结果:
`(a list of (+ 2 3) elements) ⇒ (a list of (+ 2 3) elements) '(a list of (+ 2 3) elements) ⇒ (a list of (+ 2 3) elements)
在反引号的参数内部的特殊标记“,”表示一个不恒定的值。 Emacs Lisp评估器评估“,”的参数,并将值放入列表结构中:
`(a list of ,(+ 2 3) elements) ⇒ (a list of 5 elements)
也可以在列表结构的更深层次上用','replace。 例如:
`(1 2 (3 ,(+ 4 5))) ⇒ (1 2 (3 9))
您还可以使用特殊标记“@”将评估值拼接到结果列表中。 拼接列表的元素与结果列表的其他元素成为同一级别的元素。 不使用“`”的等效代码通常是不可读的。 这里有些例子:
(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)