baitang36 发表于 2020-7-15 14:20:57

单引号和quote是不同的

本帖最后由 baitang36 于 2020-7-16 09:43 编辑

(setq bb (quote(nil (princ 8))))执行后返回一个表(nil (princ 8))
(setq bb ('(nil (princ 8))))执行后返回的是执行结果8
编译后再反编译,(setq bb (quote(nil (princ 8))))变成了(setqBB '(NIL (PRINC 8 ) )),差了一个括号

在theswamp.org有网友给我推荐了lee mac的一篇文章,我和大家分享一下:
翻译水平差了点,大家凑合看,英文水平好的请看原文http://www.lee-mac.com/quote.html
撇号运算符和Quote函数

介绍
根据我的经验,描述AutoLISP中撇号符号用途的文档非常少,而且我在现有教程中遇到的解释通常非常简短,几乎绕过了该运算符的主要用途。
因此,我发现许多初学者AutoLISP程序员在面对现有代码中的这个符号时常常感到困惑,而且在编写代码时也可能不知道何时可以使用撇号。

撇号
撇号或单引号字符将表达式或符号标记为文字表达式,以“面值”表示,不由AutoLISP解释器计算。
引用的表达式可以是任何形式的AutoLISP数据,尽管撇号通常用于列表或符号,因为大多数其他形式的AutoLISP数据(字符串、整数、实数)已经是文字常量,因此不需要使用它。

恒定数据
包含常量或固定数据的表达式(即计算时保持不变的数据)可以作为文本表达式引用,因为要计算的表达式中没有符号,因此会更改数据内容。
例如,考虑以下整数列表:
_$ (list 1 2 3 4 5)
(1 2 3 4 5)
上面的列表是通过将每个整数作为参数传递给list函数来构造的。然后,list函数将对提供的每个参数求值,并将返回一个列表,其中包含按传递给函数的所有参数的顺序。
但是,由于每个整数都是常量,因此不需要使用list函数计算数据,因为结果将保持不变。
因此,此列表可以引用为使用撇号表达式,以产生相同的结果:
_$ '(1 2 3 4 5)
(1 2 3 4 5)
类似地,构造点对列表:
_$ (list (cons 1 "a") (cons 2 "b") (cons 3 "c"))
((1 . "a") (2 . "b") (3 . "c"))
这里,每个整数和字符串对作为参数传递,并由cons函数求值,cons函数返回一个点对(因为提供了两个原子);然后每个得到的点对都作为参数传递,并由list函数求值以返回列表,如图所示。
然而,在每个阶段,最里面的数据保持完全不变,因此,cons&list函数的每次求值都是多余的,并且在代码中引入了低效率。
常数数据的点对列表可以等效地引用为使用撇号的文字表达式,以产生相同的结果,而无需计算三个函数:
_$ '((1 . "a") (2 . "b") (3 . "c"))
((1 . "a") (2 . "b") (3 . "c"))
为了提供一个实际的例子,考虑可以作为过滤参数提供给ssget函数的点对列表。
以下示例将检索图形数据库中所有闭合的LWPolyline对象的选择集:
(ssget "_X" '((0 . "LWPOLYLINE") (-4 . "&=") (70 . 1)))
由于上面的过滤器列表只包含常量数据,所以可以使用撇号将列表引用为文本表达式。
关于本例中使用的ssget函数和过滤器列表运算符的可用参数的更多信息,请参阅我的ssget函数参考。
考虑以下示例:
_$ (setq x 5)
5
_$ (list 1 2 3 4 x)
(1 2 3 4 5)
在这里,符号x被赋予一个整数值5;这个符号和整数1到4作为参数传递给list函数。
由于每个提供的参数都是由list函数求值的,所以对符号x求值以得到值5,并返回包含符号求值值的列表,如上所示。
但是,使用撇号引用列表时,请观察结果:
_$ (setq x 5)
5
_$ '(1 2 3 4 x)
(1 2 3 4 X)
由于撇号将列表标记为文字表达式,因此AutoLISP解释器不会计算列表的内容,符号x仍将作为符号保留在列表中。
这说明了术语“字面”和“面值”的含义。
在前面的实际示例中,假设我们希望提示用户指定一个图层的名称,该图层包含要由ssget表达式选择的闭合lwpolyline:
(if (snvalid (setq lay (getstring t "\nSpecify layer: ")))
    (ssget "_X" (list '(0 . "LWPOLYLINE") '(-4 . "&=") '(70 . 1) (cons 8 lay)))
)
由于过滤器列表现在包含lay变量的变量数据,因此不能再将该列表作为文本表达式引用,而必须使用list函数构造。
但是,由于过滤器列表中包含的四个点对中有三个仍然包含常量数据,因此可以使用撇号将这些点对引用为文本表达式,最后一个点对通过将两个已计算的参数传递给cons函数来构造。

函数参数
请注意,在上面的例子中,引用的列表几乎总是作为参数提供给其他函数:无论该函数是setq,将数据分配给变量;list,当构造包含常量数据列表的列表时(根据最后一个示例);或者可能是ssget,如提供ssget的第一个选择示例所示带有带引号的过滤器列表参数的函数;举几个例子。
但是,一般来说,引用的参数不必是列表,如下例所示:
_$ (mapcar '+ '(1 2 3 4 5) '(6 7 8 9 10))
(7 9 11 13 15)
这里,撇号用于将+函数标记为mapcar函数的参数,还引用了两个list参数,因为它们包含常量数据。
考虑一下,如果没有引用这个函数参数,mapcar函数将对函数符号求值,以生成指向其函数定义的指针,mapcar函数将因此返回“bad function”错误。
当提供匿名lambda函数作为mapcar对给定列表中的每个项求值的函数参数时,同样的逻辑适用,如下例计算两点之间的中点:
_$ (mapcar '(lambda ( a b ) (/ (+ a b) 2.0)) '(12.3 45.6 78.9) '(32.1 65.4 98.7))
(22.2 55.5 88.8)
有关描述mapcar函数如何工作的更多信息,请参阅我的mapcar&Lambda教程。
要提供另一个示例,请考虑以下getvar表达式:
_$ (getvar 'osmode)
255
这里,引用符号osmode,以确保在将符号作为参数传递给getvar函数时不会对该符号进行求值。由于getvar和setvar函数都接受字符串或符号参数,因此该表达式将返回OSMODE系统变量的值,如上所示。
考虑一下,如果symbol参数没有被引用,getvar函数将对参数求值,以生成已分配给活动文档命名空间中的符号的任何值(例如,通过setq或set表达式);如果符号不包含任何值(或者确实包含除有效字符串或符号参数之外的值),因此,getvar表达式将出现“错误参数类型:(或stringp symbolp)”错误。
最后,观察以下调用visuallisp ActiveX getboundingbox方法的表达式(例如,由mybounding Box和Selection Set Bounding Box函数使用的表达式):
(if (setq ent (car (entsel)))
    (vla-getboundingbox (vlax-ename->vla-object ent) 'pt1 'pt2)
)
getboundingbox方法需要三个参数:要计算其边界框的VLA对象,以及用于保存该方法输出的值的两个符号参数。
使用输出参数使该方法能够返回多个值,而不是函数求值后返回的单个值(此方法恰好为零)。
当调用该方法时,引用符号参数以确保将符号本身作为参数提供给该方法,而不是在计算此类符号时获得的值。在成功评估该方法之后,将为提供的符号分配左下角和右上角的WCS坐标,该坐标描述所提供VLA对象的边界框。


Quote 函数
AutoLISP quote函数与撇号符号的功能等效。此函数接受一个可以是任何AutoLISP表达式的参数,只返回提供的表达式而不计算它。
因此,我提出了一个希望可以理解的解释,详细说明了撇号运算符和AutoLISP quote函数的用途,以及使用它们的后果。
这里,整数列表作为参数传递给quote函数,而不是将列表中的第一个项作为函数进行求值并导致“bad function”错误(如果对列表进行求值,则会发生这种错误),而不是对提供的参数求值,而是由quote函数返回。
同样,我们前面示例中的mapcar表达式也可以使用引号函数代替撇号重写:
_$ (mapcar (quote +) (quote (1 2 3 4 5)) (quote (6 7 8 9 10)))
(7 9 11 13 15)
然而,在涉及第二个mapcar示例的情况下,当代码被编译成VLX或FAS文件时,可以通过用函数函数代替撇号或引号函数来优化lambda函数:
_$ (mapcar (function (lambda ( a b ) (/ (+ a b) 2.0))) (quote (12.3 45.6 78.9)) (quote (32.1 65.4 98.7)))
(22.2 55.5 88.8)
function函数与quote函数相同,但它指示visuallisp编译器封闭的符号或lambda表达式是一个函数参数,可以在编译过程中进行链接和优化。
由于lambda表达式是在运行时定义的,因此visuallisp编译器无法在编译期间优化这些表达式,正如内置AutoLISP函数或用defun定义的命名函数一样。类似地,当使用撇号或引号函数引用时,编译器将带引号的lambda表达式简单地视为文本数据,而不链接或优化函数。
但是,通过使用函数,编译器被指示在编译期间优化lambda函数,以便在计算期间产生与内置函数或命名函数类似的性能。
function函数也可以与定义任何内置函数或命名函数的符号一起使用,但是,由于这些函数在编译时已经进行了优化,因此性能几乎没有提高(如果有的话)。


运行时求值(Run-time Evaluation)
由于quote函数的操作方式与撇号相同,因此这个函数似乎是多余的,因为显然撇号可以用更少的击键来代替它。
但是,在某些情况下,必须使用quote函数来代替撇号来获得所需的结果。
一个这样的例子是在运行时定义的文本表达式
考虑以下简化示例:
_$ (eval (list 'defun-q 'fun '( / lst ) (list 'setq 'lst (list 'quote (list 0.0 (* pi 0.5) pi (* pi 1.5))))))
FUN
当运行时,上述表达式将定义函数fun,其中包含一个setq表达式,将四个数值项(π的倍数)分配给局部变量lst。
我有意使用defun-q表达式定义上述函数,以便在计算符号fun时可以显示函数定义的结果:
_$ fun
((/ LST) (SETQ LST (QUOTE (0.0 1.5708 3.14159 4.71239))))
在定义函数之后,请注意,setq表达式现在正在为符号lst分配一个文本列表,而不需要重复计算列表项,更重要的是,每个项的精度没有损失(但是,为了便于查看,在控制台显示时,显示的值会被截断,这些值仍存储到可用的最大精度级别)。
考虑到为了使用带引号的文本列表获得相同的结果,π的每个无理数都需要在代码中表示为大约15位小数,以达到相同的精度。
或者,可以对函数进行定义,使得算术表达式包含在函数定义中:
_$ (defun-q fun ( / lst ) (setq lst (list 0.0 (* pi 0.5) pi (* pi 1.5))))
FUN
_$ fun
((/ LST) (SETQ LST (LIST 0.0 (* PI 0.5) PI (* PI 1.5))))
然而,用这种方式定义函数时,每次计算函数时都会计算π的倍数,并且不必要地重新计算,这会导致效率低下。对于上面的例子,这种性能损失当然可以忽略不计,而且在选择在运行时定义函数时,还必须考虑对代码可读性的负面影响;但是,如果列表可能包含数千个条目,那么重复计算很快就会变得明显。
因此,这种构造允许在运行时直接将无法管理的大型文本列表包含在要构造的程序源代码中,同时保持准确性和提高效率。
为了提供一个以这种方式使用的quote函数的具体示例,请考虑我的GrText函数。出现在函数定义顶部的引用向量列表已经相当大了,但是如果不使用运行时重新定义,文本列表将是原来的两倍!




yxp 发表于 2020-7-15 19:26:43

本帖最后由 yxp 于 2020-7-15 19:44 编辑

感谢老哥的无私分享
quote 后面不能跟变量,不能跟带小数点的名称

(setq va "123")
(quote va) 返回 VA

(quote 4.52)返回 4.52, 正常
(quote 4.52s) 只返回 4
(quote aa.bb)只返回 AA,小数点后面的名称被吃了。


烟盒迷唇 发表于 2020-7-16 08:44:28

什么情况下能用单引号代替多?

baitang36 发表于 2020-7-16 09:41:59

烟盒迷唇 发表于 2020-7-16 08:44
什么情况下能用单引号代替多?

请仔细看lee mac的文章,本帖子中我翻译了一下

dtucad 发表于 2023-1-20 15:33:03

谢谢盛老师的研究翻译
页: [1]
查看完整版本: 单引号和quote是不同的