关键字符号和带引号的符号有什么区别?

时间:2014-05-31 10:54:01

标签: lisp common-lisp symbols

关键字符号

之间有什么区别
:foo

和引用的符号:

'foo

两者都代表自己,可以用作标识符。我可以看到关键字符号主要用于命名参数,但我问自己是否也不可能使用带引号的符号来实现它?

换句话说:为什么我需要两者?

2 个答案:

答案 0 :(得分:16)

首先:'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>

:fookeyword:foo的缩写。因此,符号位于关键字包中,并将其导出。

CL-USER 21 > keyword:foo
:FOO

因此关键字符号是关键字包中的自我评估常量符号。它们用作数据结构和关键字arglists中的标记。好事:你不需要为包装而烦恼,他们会自己评价 - 所以不需要引用。

答案 1 :(得分:9)

关键字与其他符号之间的差异

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列表中的关键字名称

可能更重要的是,默认情况下,它们被用作&#34;关键字参数的指示符&#34;在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同名的关键字包中的符号,但您实际上可以提供该符号并指定您自己的&#34;关键字&#34;。这可以很方便,例如,如果您想要一个描述性的关键字名称但不想要这么长的变量名称:

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

您也可以将其与默认值混合使用,但您现在可能已经明白了这一点。