为什么Common Lisp不区分大小写?

24

定义一个函数时,如何使用 (defun hi () "Hi!") 的方式,并可以通过 (hi)(HI)(Hi) 调用它,或者使用 (setf a-number 5) 的方式并能够使用 a-numberA-NUMBERA-Number 访问该数字?这样做有什么优势呢?

如果存在这样的优势,那为什么大多数其他编程语言对大小写敏感呢?

4个回答

36
在交互式会话中,在代码中使用区分大小写的名称只会更容易出错:不仅需要正确输入字符,还需要注意它们的大小写,并且可能存在几个标识符的名称只有大小写不同。
Common Lisp 区分大小写。只是默认情况下,Common Lisp 读取器功能将所有未转义符号的字符转换为大写。这也在 Common Lisp 标准中定义。预定义的 Common Lisp 符号也全部内部大写。
符号在内部是大写的:
CL-USER 3 > 'defparameter
DEFPARAMETER

默认情况下,输入大小写不敏感:

CL-USER 4 > 'deFParameTER
DEFPARAMETER

CL-USER 5 > 'DEFPARAMETER
DEFPARAMETER

一个符号有一个大写的名称:

CL-USER 6 > (symbol-name 'defparameter)
"DEFPARAMETER"

CL包中有多少个符号:

CL-USER 7 > (length (apropos-list "" "CL"))
978

CL包中的每个符号都是大写的吗?

CL-USER 8 > (every (lambda (symbol)
                     (every (lambda (c)
                              (eql c (char-upcase c)))
                            (symbol-name symbol)))
                   (apropos-list "" "CL"))
T

在旧机器上使用大写字母是很常见的。请记住,Common Lisp 的设计始于 20 世纪 80 年代初(1982 年),其目标是与早期的 Maclisp 兼容,并支持更多类型的计算机(如所谓的小型计算机和大型机)。其他在旧计算机上使用的编程语言也使用大写标识符,例如 COBOL 或 PL/1。

此外,请注意 Lisp 经常被交互地使用,因此在交互式编程会话期间,正确获取名称的大小写更加困难。当 Lisp 读取器使用默认大小写(这里是大写)并将所有输入转换为此大小写时,情况会稍微容易一些。

Common Lisp 支持其他读取器模式,您还可以转义符号:|This is a Symbol with mixed CASE and spaces|

今天,许多软件要么是小写字母,要么是区分大小写的,而首选是小写字母。一些 Lisp 供应商提供了 Common Lisp 的非标准变体,其中默认情况下所有符号都是小写的,读取器保留大小写。但是,这使其与标准 Common Lisp 不兼容,标准 Common Lisp 的预期是 (symbol-name 'cl:defun) 是 "DEFUN" 而不是 "defun"。


17

在Common Lisp标准确定时,对于交互式会话来说,不区分大小写通常是默认设置。

但实际上,Common Lisp阅读器在对符号进行intern和求值之前会将所有符号转换为大写。这是默认设置,但如果需要,您可以随时更改它。

*readtable*对象具有一个属性readtable-case,该属性控制读取的符号如何进行intern和求值。您可以将setf readtable-case设置为:upcase(默认),:downcase:preserve:invert

默认情况下,readtable-case设置为:upcase,这会导致所有符号都转换为大写。

如果您想要区分大小写,则应该执行:

(setf (readtable-case *readtable*) :invert)
=> :invert

乍一看,您可能认为选择 :preserve 选项会更好,但它有一些小问题:所有由标准定义的符号必须大写。因此,您只能对自己定义的符号进行大小写敏感处理,并且需要编写以下内容:

* (DEFUN hi () "Hi!")
=> hi
* (SETF a-number 5)
=> a-number
* (HI)
=> ;error: the stored function is #'HI in the *readtable*, but by 
   ;       calling (HI) you try to acces a function named #'hi(downcase), which
   ;       gives an error
* A-NUMBER
=> ;error: same for the variable
* (hi)
=> "Hi!"
* a-number
=> 5

:downcase选项是与默认值相反的,将所有内容转换为小写字母,使您不区分大小写。

但是使用:invert,在源代码中编写的符号(如defunsetfhi函数),会被转换为大写字母,任何CamelCase中的符号都会像原来一样保留:

* (setf (readtable-case *readtable*) :invert)
=> :invert
* (defun Hi () "Hi!")
=> Hi
* (Hi)
=> "Hi!"
* (eq 'Hi 'hi)
=> nil
* (eq 'HI 'hi)
=> nil
* (eq 'Hi 'Hi)
=> t

1
这个链接更清楚地解释了一切。(setf (readtable-case *readtable*) :invert)将所有大写字母转换为小写字母,所有小写字母转换为大写字母,因为所有原始函数默认都是大写字母编写的。 - Lime

9

(正如其他人指出的那样,它实际上是区分大小写的,但标准阅读器行为是将所有内容大写。)

至于优点:

  • 你真的想让 Hashtable HashTable 命名不同的东西吗?
  • 由于Common Lisp提供了不同的命名空间,因此您也不需要使用大写来区分类、变量和函数名称(以及其他名称)。您可以有一个类 name 和一个函数 name 而不会产生歧义。甚至可以将 Name 作为变量名。
  • 如上一句所示,您可以像任何其他单词一样在散文中大写符号名称。

1
好观点!但是如果HashtableHashTable应该明确指向同一件事情,那么name也不应该明确地指向一个函数、类或变量吗? - wrongusername
3
当你看到一个求值的形式 (name foo) 时,它明确地是一个函数 name;当你看到 (defmethod bar ((baz name)) ...) 时,它明确地是一个类 name(或者说类型…);当你看到一个求值的形式 (quux name) 时,它明确地是一个变量。这就像英语中你可以将 "name" 既用作动词又用作名词而不会混淆一样。 - Svante

3

默认情况下,CL中的读取器会进行大小写转换,所有转义字符都会变成大写。你可以使用readtable-case自定义此行为。这是因为易于与遵循相同约定的其他语言进行接口。


哦,CL 接口可以与哪些语言配合使用? - wrongusername
2
那个时候?可能是Fortran。请记住,Common Lisp及其前身是在遥远的星系中设计的很久以前。 - Stuart Cook
3
这并不是特别针对Fortran,而是由于硬件(Teletype)通常使用大写字母,操作系统也使用大写字母。因此编程语言也使用了大写字母,比如PL/1、Cobol、Fortran和Lisp等。在通过慢速连接附加的终端上,编辑大小写敏感的命令在行编辑模式下有些麻烦...... - Rainer Joswig
1
@Rainer 谢谢您的见解。我一直以为Fortran和其他编程语言是这种惯例的原因。 - Devin M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接