关键字符号和引用符号有啥区别?
Posted
技术标签:
【中文标题】关键字符号和引用符号有啥区别?【英文标题】:What is the difference between a keyword symbol and a quoted symbol?关键字符号和引用符号有什么区别? 【发布时间】:2014-07-21 01:49:03 【问题描述】:关键字符号有什么区别
:foo
和引用的符号:
'foo
两者都代表自己,并且可以用作标识符。我可以看到关键字符号主要用于命名参数,但我问自己是否也无法使用带引号的符号来实现这一点?
换句话说:为什么我需要两者?
【问题讨论】:
您不必使用关键字符号作为“关键字参数”;这只是默认值。我添加了an answer,它展示了如何使用,例如,引用符号作为 lambda 列表中的关键字名称。 @JoshuaTaylor:尽管在我见过的代码库中,引用符号作为参数列表中的关键字似乎很少使用。 @RainerJoswig 我完全同意。老实说,我认为我从来没有在实践中看到过它,除非是在模拟 Scheme 中。我真正想到的唯一用例是使用非导出符号来指示某些东西不是 supported 关键字选项,但它仍然存在。例如,可能类似于(frob bar baz :from-end t 'frobber::include-debug-output t)
.
【参考方案1】:
首先:'something
是 (quote something)
的较短表示法。读者会将引号字符转换为一个列表,其中符号cl:quote
作为第一项。对于评估者,这意味着:不要评估something
,只需将其作为结果返回即可。
CL-USER 22 > '(quote foo)
(QUOTE FOO)
CL-USER 23 > ''foo
(QUOTE FOO)
CL-USER 24 > (read-from-string "'foo")
(QUOTE FOO)
冒号:
是一个包标记。如果缺少包名,则符号在KEYWORD
包中。
我们可以给foo
一个值:
CL-USER 11 > (setq foo 10)
10
foo
计算为它的值。
CL-USER 12 > foo
10
带引号的符号计算为该符号。
CL-USER 13 > 'foo
FOO
我们不能给:foo
一个值:
CL-USER 14 > (setq :foo 10)
Error: Cannot setq :FOO -- it is a keyword.
1 (abort) Return to level 0.
2 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
CL-USER 15 : 1 > :top
:foo
已经有一个值:它自己。
CL-USER 16 > :foo
:FOO
引用:foo
的结果自然是:foo
。
CL-USER 17 > ':foo
:FOO
符号foo
在某个包中,这里是CL-USER
。
CL-USER 18 > (symbol-package 'foo)
#<The COMMON-LISP-USER package, 92/256 internal, 0/4 external>
符号:foo
在KEYWORD
包中。
CL-USER 19 > (symbol-package ':foo)
#<The KEYWORD package, 0/4 internal, 6230/8192 external>
由于:foo
是:foo
的值,我们也可以这样写:
CL-USER 20 > (symbol-package :foo)
#<The KEYWORD package, 0/4 internal, 6230/8192 external>
:foo
是keyword:foo
的缩写。因此符号在关键字包中并被导出。
CL-USER 21 > keyword:foo
:FOO
所以关键字符号是关键字包中的自评估常量符号。它们用作数据结构和关键字参数列表中的标记。好处:您无需为软件包而苦恼,它们会自行评估 - 所以不需要报价。
【讨论】:
嗯,但这正是我没有得到的:如果'foo
的计算结果为foo
,而:foo
的计算结果为:foo
,实际上不一样吗?正如你所说,你不能给:foo
赋值,但我也不能给'foo
赋值,可以吗?
@GoloRoden: 'foo
是(quote foo)
的简写符号。给列表一个值是没有意义的。由于:foo
的计算结果为:foo
,因此没有必要引用它。
好的,知道了。谢谢:-))
我认为重要的是要注意 :foo 评估为 :foo,因此它是自评估的,而 'foo 评估为 foo 并且因此不是。所以就不一样了。
另外值得注意的是,可能是:(string= (symbol-name 'foo) (symbol-name :foo))
。 IE。关键字的符号名称不包含冒号。【参考方案2】:
关键字与其他符号的区别
Rainer Joswig's answer 很好地描述了符号本身。不过,总而言之,每个符号都属于一个包。 p::foo
(或p:foo
,如果它是外部的)是包p
中的一个符号。如果您尝试将其评估为表单,您将获得其符号值,您可以使用 set
或 `(setf symbol-value) 设置:
CL-USER> (set 'foo 'bar)
BAR
CL-USER> foo
BAR
CL-USER> (setf (symbol-value 'foo) 'baz)
BAZ
CL-USER> foo
BAZ
有一个名为keyword
的特殊包。如果你愿意,你可以写keyword::foo
,但是所有关键字包的符号都是外部的,所以你可以写keyword:foo
。但是,由于它们非常常用,您甚至可以获得它们的特殊语法::foo
。它们还具有您无法设置其值的特殊属性;他们的价值观就是他们自己:
CL-USER> :foo
:FOO
CL-USER> (symbol-value :bar)
:BAR
这就是使关键字符号与众不同的全部原因。
关键字和其他符号作为 lambda 列表中的关键字名称
可能更重要的是,默认情况下,它们被用作 lambda 列表中“关键字参数”的指示符。例如,
CL-USER> ((lambda (&key foo bar)
(list foo bar))
:bar 23 :foo 12)
(12 23)
可以看出关键字符号主要用于命名参数, 但我问自己是否无法使用 引用符号也一样?
lambda 列表的语法实际上允许您使用关键字参数进行更多自定义。一个常见的事情是指定默认值:
CL-USER> ((lambda (&key (foo 'default-foo) bar)
(list foo bar))
:bar 23)
(DEFAULT-FOO 23)
您还可以提供一个绑定到布尔值的变量名,指示是否指定了参数:
CL-USER> ((lambda (&key (foo 'default-foo foo-p) (bar 'default-bar bar-p))
(format t "~~A:~7t~A~%~"
(list 'foo foo
'foo-p foo-p
'bar bar
'bar-p bar-p)))
:bar 23)
FOO: DEFAULT-FOO
FOO-P: NIL
BAR: 23
BAR-P: T
不过,3.4.1 Ordinary Lambda Lists 的完整语法让我们可以做更多事情。这是
lambda-list::= (var*
[&optional var | (var [init-form [supplied-p-parameter]])*]
[&rest var]
[&key var | (var | (keyword-name var) [init-form [supplied-p-parameter]])* [&allow-other-keys]]
[&aux var | (var [init-form])*])
请注意,您可以指定keyword-name
。它默认为关键字包中与var
同名的符号,但您实际上可以提供它并指定您自己的“关键字”。这可能很方便,例如,如果您想要一个描述性的关键字名称但不想要这么长的变量名称:
CL-USER> ((lambda (&key ((:home-directory dir)))
(list dir))
:home-directory "/home/me")
("/home/me")
您还可以使用它来指定不是关键字符号的关键字名称:
CL-USER> ((lambda (&key ((surprise surprise)))
(list surprise))
'surprise "hello world!")
("hello world!")
您也可以将两者结合起来:
CL-USER> ((lambda (&key ((hidden-parameter secret)))
(format t "the secret is ~A" secret))
'hidden-parameter 42)
the secret is 42
您也可以将其与默认值混合使用,但您现在可能明白了。
【讨论】:
以上是关于关键字符号和引用符号有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章