关键字如何在公共Lisp中工作?

关键字如何在公共Lisp中工作?,lisp,common-lisp,Lisp,Common Lisp,问题不在于使用关键字,而在于关键字的实现。例如,当我创建带有关键字参数的函数并进行调用时: (defun fun (&key key-param) (print key-param)) => FUN (find-symbol "KEY-PARAM" 'keyword) => NIL, NIL ;;keyword is not still registered (fun :key-param 1) => 1 (find-symbol "KEY-PARAM" 'keyw

问题不在于使用关键字,而在于关键字的实现。例如,当我创建带有关键字参数的函数并进行调用时:

(defun fun (&key key-param) (print key-param)) => FUN
(find-symbol "KEY-PARAM" 'keyword) => NIL, NIL   ;;keyword is not still registered
(fun :key-param 1) => 1
(find-symbol "KEY-PARAM" 'keyword) => :KEY-PARAM, :EXTERNAL
如何使用关键字传递参数?关键字是符号,其值本身就是符号,那么如何使用关键字绑定相应的参数呢

关于关键字的另一个问题-关键字用于定义包。我们可以使用已存在的关键字定义名为的包:

(defpackage :KEY-PARAM) => #<The KEY-PARAMETER package, 0/16 ...
(in-package :KEY-PARAM) => #<The KEY-PARAMETER package, 0/16 ...
(defun fun (&key key-param) (print key-param)) => FUN
(fun :KEY-PARAM 1) => 1
问题是相同的,Lisp在这里如何区分关键字
:KEY-PARAM
的用法

如果有一些关于Common Lisp中关键字的手册,并解释了它们的机制,如果您在这里发布一个链接,我将不胜感激,因为我只能找到一些关于关键字用法的短文。

有关关键字参数的详细信息,请参阅。注意

(defun fun (&key key-param) ...)
实际上是以下的缩写:

(defun fun (&key ((:key-param key-param)) ) ...)
关键字参数的完整语法为:

((keyword-name var) default-value supplied-p-var)
默认值
提供的-p-var
是可选的。虽然传统上使用关键字符号作为
关键字名称
,但这不是必需的;如果只指定一个
var
而不是
(关键字名称var)
,则默认情况下
关键字名称
是关键字包中与
var
同名的符号

例如,你可以做:

(defun fun2 (&key ((myoption var))) (print var))
然后称之为:

(fun 'myoption 3)
它在内部工作的方式是,当调用函数时,它逐步遍历参数列表,收集成对的参数
。对于每个
标签
,它在参数列表中查找具有该
关键字名称
的参数,并将相应的
变量
绑定到

我们通常使用关键字的原因是
前缀非常突出。这些变量都是自评估的,因此我们不必引用它们,也就是说,您可以编写
:key param
,而不是
”:key param
(仅供参考,后一种符号在早期的Lisp系统中是必要的,但CL设计者认为它是丑陋和冗余的)。我们通常不会使用这个功能来指定一个与变量名称不同的关键字,因为这会让人困惑。这样做完全是为了一般性。此外,允许用常规符号代替关键字对于CLOS这样的工具也很有用,在CLOS中,参数列表被合并,并且您希望避免冲突——如果您要扩展一个通用函数,您可以添加关键字在您自己的包中的参数,并且不会发生冲突

定义包和导出变量时使用关键字参数也是一种惯例
DEFPACKAGE
包内
导出
,等等。只关心给定的名称,而不关心它在哪个包中。你可以写

(defpackage key-param)
它通常也能起到同样的作用。许多程序员之所以不这样做,是因为它在他们自己的包中插入了一个符号,如果这个符号恰好与他们试图从另一个包导入的符号同名,这有时会导致包冲突。使用关键字可以将这些参数从应用程序包中分离出来,从而避免类似这样的潜在问题

底线是:当你使用一个符号时,你只关心它的名字,而不关心它的身份,使用关键字通常是最安全的

最后,关于区分以不同方式使用的关键字。关键字只是一个符号。如果在函数或宏只需要普通参数的地方使用,则该参数的值将是符号。如果您正在调用具有
&key
参数的函数,这是它们唯一一次被用作将参数与参数关联的标签。

不需要“系统”来区分关键字的不同用途。它们只是用作名字。例如,想象两个plist:

(defparameter *language-scores* '(:basic 0 :common-lisp 5 :python 3))
(defparameter *price* '(:basic 100 :fancy 500))
产生语言分数的函数:

(defun language-score (language &optional (language-scores *language-scores*))
  (getf language-scores language))
语言分数
一起使用时,这些关键字指定不同的编程语言:

CL-USER> (language-score :common-lisp)
5
现在,系统如何区分
*语言分数*
中的关键字和
*价格*
中的关键字?绝对没有。关键字只是名称,在不同的数据结构中指定不同的内容。它们并不比自然语言中的同音词更具区别——它们的使用决定了它们在特定语境中的含义

在上面的示例中,没有任何东西可以阻止我们在错误的上下文中使用函数:

(language-score :basic *prices*)
100
语言并没有阻止我们这样做,而不太花哨的编程语言和不太花哨的产品的关键字是一样的

防止这种情况的方法有很多:首先不允许使用
语言分数的可选参数,将
*prices*
放入另一个包中而不将其外部化,关闭词法绑定,而不是使用全局特殊的
*语言分数*
,同时只公开添加和检索条目的方法。也许仅仅是我们对代码库或约定的理解就足以阻止我们这样做。要点是:系统区分关键字本身并不是实现我们想要实现的目标所必需的

您询问的特定关键字使用情况并无不同:实现可能将关键字参数的绑定存储在列表、plist、哈希表或其他任何形式中。在包名称的情况下,关键字仅用作包指示符,而包名称作为字符串(大写)可能仅被使用。无论实现是将字符串转换为关键字、将关键字转换为字符串,还是内部完全不同的内容,都无关紧要。重要的只是名称,以及在什么环境下使用它。

好的手册是

简要回答您的问题:

  • <
    (language-score :basic *prices*)
    100
    
    (let ((key-param (getf args :key-param)))
      body)
    
    (defpackage "KEY-PARAM" ...)
    
    (defpackage :key-param ...)
    
    (defpackage #:key-param ...)