关键字符号和引用符号有啥区别?

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>

符号:fooKEYWORD 包中。

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

所以关键字符号是关键字包中的自评估常量符号。它们用作数据结构和关键字参数列表中的标记。好处:您无需为软件包而苦恼,它们会自行评估 - 所以不需要报价。

【讨论】:

嗯,但这正是我没有得到的:如果'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

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

【讨论】:

以上是关于关键字符号和引用符号有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

电脑里的‘命令符号’有啥作用?就一个‘黑板’,如何操作?

单引号和双引号有啥区别?

C++11使用auto关键字进行基于范围的for循环,引用符号&的作用

符号链接和硬链接有啥区别?

“日志”和“符号日志”有啥区别?

Common Lisp 中的原子和符号有啥区别?