定义一个函数时,如何使用 (defun hi () "Hi!")
的方式,并可以通过 (hi)
、(HI)
或 (Hi)
调用它,或者使用 (setf a-number 5)
的方式并能够使用 a-number
、A-NUMBER
或 A-Number
访问该数字?这样做有什么优势呢?
如果存在这样的优势,那为什么大多数其他编程语言对大小写敏感呢?
定义一个函数时,如何使用 (defun hi () "Hi!")
的方式,并可以通过 (hi)
、(HI)
或 (Hi)
调用它,或者使用 (setf a-number 5)
的方式并能够使用 a-number
、A-NUMBER
或 A-Number
访问该数字?这样做有什么优势呢?
如果存在这样的优势,那为什么大多数其他编程语言对大小写敏感呢?
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"。
在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
,在源代码中编写的符号(如defun
、setf
、hi
函数),会被转换为大写字母,任何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
(正如其他人指出的那样,它实际上是区分大小写的,但标准阅读器行为是将所有内容大写。)
至于优点:
Hashtable
和 HashTable
命名不同的东西吗? name
和一个函数 name
而不会产生歧义。甚至可以将 Name
作为变量名。Hashtable
和HashTable
应该明确指向同一件事情,那么name
也不应该明确地指向一个函数、类或变量吗? - wrongusername(name foo)
时,它明确地是一个函数 name
;当你看到 (defmethod bar ((baz name)) ...)
时,它明确地是一个类 name
(或者说类型…);当你看到一个求值的形式 (quux name)
时,它明确地是一个变量。这就像英语中你可以将 "name" 既用作动词又用作名词而不会混淆一样。 - Svante默认情况下,CL中的读取器会进行大小写转换,所有转义字符都会变成大写。你可以使用readtable-case
自定义此行为。这是因为易于与遵循相同约定的其他语言进行接口。
(setf (readtable-case *readtable*) :invert)
将所有大写字母转换为小写字母,所有小写字母转换为大写字母,因为所有原始函数默认都是大写字母编写的。 - Lime