小議 Common Lisp 的名字

空軍發表於2013-05-19

不要誤會,標題中的名字不是指 Common Lisp 這個名字本身,而是指 Common Lisp 中所使用的名字,也就是符號,或者是其他程式設計語言中所說的識別符號。

Common Lisp 的名字有兩個不同於其他程式設計語言的特點:一是幾乎任何字元都可以出現在一個名字裡(空白字元除外,因為列表的元素是用空格來分隔的);二是所有名字中未轉義的字元都會轉化成大寫形式。

Peter Seibel 的《實用Common Lisp程式設計》是一本極佳的 Lisp 語言入門教程,其 Web 站點 http://www.gigamonkeys.com/book 有英文版全書內容。我們來看看第4章 Syntax and Semantics 對名字的論述(搜尋 Names used):

Names used in Lisp programs, such as FORMAT and hello-world, and *db* are represented by objects called symbols. The reader knows nothing about how a given name is going to be used--whether it's the name of a variable, a function, or something else. It just reads a sequence of characters and builds an object to represent the name.6 Almost any character can appear in a name. Whitespace characters can't, though, because the elements of lists are separated by whitespace. Digits can appear in names as long as the name as a whole can't be interpreted as a number. Similarly, names can contain periods, but the reader can't read a name that consists only of periods. Ten characters that serve other syntactic purposes can't appear in names: open and close parentheses, double and single quotes, backtick, comma, colon, semicolon, backslash, and vertical bar. And even those characters can, if you're willing to escape them by preceding the character to be escaped with a backslash or by surrounding the part of the name containing characters that need escaping with vertical bars.

Two important characteristics of the way the reader translates names to symbol objects have to do with how it treats the case of letters in names and how it ensures that the same name is always read as the same symbol. While reading names, the reader converts all unescaped characters in a name to their uppercase equivalents. Thus, the reader will read foo, Foo, and FOO as the same symbol: FOO. However, \f\o\o and |foo| will both be read as foo, which is a different object than the symbol FOO. This is why when you define a function at the REPL and it prints the name of the function, it's been converted to uppercase. Standard style, these days, is to write code in all lowercase and let the reader change names to uppercase.

仔細閱讀上述文字,可知可以用反斜槓(\)轉義一個字元,也可以用豎線(|)包起一串需要轉義的字元。名字中被轉義的字元不會被轉化成大寫形式,如 f\o\o、f|oo| 和 |Foo| 都是同一個名字 |Foo|。當然,foo、Foo 和 FOO 都是名字 FOO。


Common Lisp 對可以組成名字的字元的限制非常少,數字字元也可以出現在名字中,只要整個名字不被解釋成一個數值,所以可以使用 1+1- 這樣有趣的函式名。

Practical Common Lisp 第6章 Variables 有四處用到函式 1+ 而沒有解釋其含義或作用(請搜尋 1+):

For instance, in this expression:

(let ((count 0)) #'(lambda () (setf count (1+ count))))

……

For instance, you can capture the closure created by the previous expression in a global variable like this:

(defparameter *fn* (let ((count 0)) #'(lambda () (setf count (1+ count)))))

……

A naive translation of that into a SETF expression might look like this:

(setf (aref *array* (random (length *array*)))
      (1+ (aref *array* (random (length *array*)))))

……

In this case, it would probably expand into something more or less equivalent to this:

(let ((tmp (random (length *array*))))
  (setf (aref *array* tmp) (1+ (aref *array* tmp))))

第7、8章也多處用到函式 1+ ,就不一一列舉了。直到第10章 Numbers, Characters, and Strings 才有了說明(亦請搜尋 1+):

The functions 1+ and 1- provide a shorthand way to express adding and subtracting one from a number. Note that these are different from the macros INCF and DECF. 1+ and 1- are just functions that return a new value, but INCF and DECF modify a place. The following equivalences show the relation between INCF/DECF, 1+/1-, and +/-:

(incf x)    === (setf x (1+ x)) === (setf x (+ x 1))
(decf x)    === (setf x (1- x)) === (setf x (- x 1))
(incf x 10) === (setf x (+ x 10))
(decf x 10) === (setf x (- x 10))

相關文章