Common Lisp

2018-02-24 15:18 更新

X分鐘速成Y

其中 Y=Common Lisp

源代碼下載:?commonlisp-cn.lisp

ANSI Common Lisp 是一個廣泛通用于各個工業(yè)領(lǐng)域的、支持多種范式的編程語言。 這門語言也經(jīng)常被引用作“可編程的編程語言”(可以寫代碼的代碼)。

免費的經(jīng)典的入門書籍《實用 Common Lisp 編程》

另外還有一本熱門的近期出版的?Land of Lisp.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; 0\. 語法
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; 一般形式

;; Lisp有兩個基本的語法單元:原子(atom),以及S-表達(dá)式。
;; 一般的,一組S-表達(dá)式被稱為“組合式”。

10  ; 一個原子; 它對自身進(jìn)行求值

:THING ;同樣是一個原子;它被求值為一個符號 :thing

t  ;還是一個原子,代表邏輯真值。

(+ 1 2 3 4) ; 一個S-表達(dá)式。

'(4 :foo  t)  ;同樣是一個S-表達(dá)式。

;;; 注釋

;; 一個分號開頭的注釋表示僅用于此行(單行);兩個分號開頭的則表示一個所謂標(biāo)準(zhǔn)注釋;
;; 三個分號開頭的意味著段落注釋;
;; 而四個分號開頭的注釋用于文件頭注釋(譯者注:即對該文件的說明)。

#| 塊注釋
   可以涵蓋多行,而且...
    #|
       他們可以被嵌套!
    |#
|#

;;; 運行環(huán)境

;; 有很多不同的Common Lisp的實現(xiàn);并且大部分的實現(xiàn)是一致(可移植)的。
;; 對于入門學(xué)習(xí)來說,CLISP是個不錯的選擇。

;; 可以通過QuickLisp.org的Quicklisp系統(tǒng)管理你的庫。

;; 通常,使用文本編輯器和“REPL”來開發(fā)Common Lisp;
;; (譯者注:“REPL”指讀取-求值-打印循環(huán))。
;; “REPL”允許對程序進(jìn)行交互式的運行、調(diào)試,就好像在系統(tǒng)“現(xiàn)場”操作。

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; 1\. 基本數(shù)據(jù)類型以及運算符
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; 符號

'foo ; => FOO  注意到這個符號被自動轉(zhuǎn)換成大寫了。

;; `intern`由一個給定的字符串而創(chuàng)建相應(yīng)的符號

(intern "AAAA") ; => AAAA

(intern "aaa") ; => |aaa|

;;; 數(shù)字
9999999999999999999999 ; 整型數(shù)
#b111                  ; 二進(jìn)制 => 7
#o111                  ; 八進(jìn)制 => 73
#x111                  ; 十六進(jìn)制 => 273
3.14159s0              ; 單精度
3.14159d0              ; 雙精度
1/2                    ; 分?jǐn)?shù)
#C(1 2)                ; 復(fù)數(shù)

;; 使用函數(shù)時,應(yīng)當(dāng)寫成這樣的形式:(f x y z ...);
;; 其中,f是一個函數(shù)(名),x, y, z為參數(shù);
;; 如果你想創(chuàng)建一個“字面”意義上(即不求值)的列表, 只需使用單引號 ' ,
;; 從而避免接下來的表達(dá)式被求值。即,只“引用”這個數(shù)據(jù)(而不求值)。
'(+ 1 2) ; => (+ 1 2)
;; 你同樣也可以手動地調(diào)用一個函數(shù)(譯者注:即使用函數(shù)對象來調(diào)用函數(shù)):
(funcall #'+ 1 2 3) ; => 6
;; 一些算術(shù)運算符
(+ 1 1)              ; => 2
(- 8 1)              ; => 7
(* 10 2)             ; => 20
(expt 2 3)           ; => 8
(mod 5 2)            ; => 1
(/ 35 5)             ; => 7
(/ 1 3)              ; => 1/3
(+ #C(1 2) #C(6 -4)) ; => #C(7 -2)

                     ;;; 布爾運算
t                    ; 邏輯真(任何不是nil的值都被視為真值)
nil                  ; 邏輯假,或者空列表
(not nil)            ; => t
(and 0 t)            ; => t
(or 0 nil)           ; => 0

                     ;;; 字符
#\A                  ; => #\A
#\λ                  ; => #\GREEK_SMALL_LETTER_LAMDA(希臘字母Lambda的小寫)
#\u03BB              ; => #\GREEK_SMALL_LETTER_LAMDA(Unicode形式的小寫希臘字母Lambda)

;;; 字符串被視為一個定長字符數(shù)組
"Hello, world!"
"Benjamin \"Bugsy\" Siegel"   ;反斜杠用作轉(zhuǎn)義字符

;; 可以拼接字符串
(concatenate 'string "Hello " "world!") ; => "Hello world!"

;; 一個字符串也可被視作一個字符序列
(elt "Apple" 0) ; => #\A

;; `format`被用于格式化字符串
(format nil "~a can be ~a" "strings" "formatted")

;; 利用`format`打印到屏幕上是非常簡單的
;;(譯者注:注意到第二個參數(shù)是t,不同于剛剛的nil);~% 代表換行符
(format t "Common Lisp is groovy. Dude.~%")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 2\. 變量
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 你可以通過`defparameter`創(chuàng)建一個全局(動態(tài))變量
;; 變量名可以是除了:()[]{}",'`;#|\ 這些字符之外的其他任何字符

;; 動態(tài)變量名應(yīng)該由*號開頭與結(jié)尾!
;; (譯者注:這個只是一個習(xí)慣)

(defparameter *some-var* 5)
*some-var* ; => 5

;; 你也可以使用Unicode字符:
(defparameter *AΛB* nil)

;; 訪問一個在之前從未被綁定的變量是一種不規(guī)范的行為(即使依然是可能發(fā)生的);
;; 不要嘗試那樣做。

;; 局部綁定:在(let ...)語句內(nèi),'me'被綁定到"dance with you"上。
;; `let`總是返回在其作用域內(nèi)最后一個表達(dá)式的值

(let ((me "dance with you"))
  me)
;; => "dance with you"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 3\. 結(jié)構(gòu)體和集合
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; 結(jié)構(gòu)體
(defstruct dog name breed age)
(defparameter *rover*
    (make-dog :name "rover"
              :breed "collie"
              :age 5))
*rover* ; => #S(DOG :NAME "rover" :BREED "collie" :AGE 5)

(dog-p *rover*) ; => t  ;; ewww)
(dog-name *rover*) ; => "rover"

;; Dog-p,make-dog,以及 dog-name都是由defstruct創(chuàng)建的!

;;; 點對單元(Pairs)
;; `cons`可用于生成一個點對單元, 利用`car`以及`cdr`將分別得到第一個和第二個元素
(cons 'SUBJECT 'VERB) ; => '(SUBJECT . VERB)
(car (cons 'SUBJECT 'VERB)) ; => SUBJECT
(cdr (cons 'SUBJECT 'VERB)) ; => VERB

;;; 列表

;; 所有列表都是由點對單元構(gòu)成的“鏈表”。它以'nil'(或者'())作為列表的最后一個元素。
(cons 1 (cons 2 (cons 3 nil))) ; => '(1 2 3)
;; `list`是一個生成列表的便利途徑
(list 1 2 3) ; => '(1 2 3)
;; 并且,一個引用也可被用做字面意義上的列表值
'(1 2 3) ; => '(1 2 3)

;; 同樣的,依然可以用`cons`來添加一項到列表的起始位置
(cons 4 '(1 2 3)) ; => '(4 1 2 3)

;; 而`append`也可用于連接兩個列表
(append '(1 2) '(3 4)) ; => '(1 2 3 4)

;; 或者使用`concatenate`

(concatenate 'list '(1 2) '(3 4))

;; 列表是一種非常核心的數(shù)據(jù)類型,所以有非常多的處理列表的函數(shù)
;; 例如:
(mapcar #'1+ '(1 2 3))             ; => '(2 3 4)
(mapcar #'+ '(1 2 3) '(10 20 30))  ; => '(11 22 33)
(remove-if-not #'evenp '(1 2 3 4)) ; => '(2 4)
(every #'evenp '(1 2 3 4))         ; => nil
(some #'oddp '(1 2 3 4))           ; => T
(butlast '(subject verb object))   ; => (SUBJECT VERB)

;;; 向量

;; 向量的字面意義是一個定長數(shù)組
;;(譯者注:此處所謂“字面意義”,即指#(......)的形式,下文還會出現(xiàn))
#(1 2 3) ; => #(1 2 3)

;; 使用`concatenate`來將兩個向量首尾連接在一起
(concatenate 'vector #(1 2 3) #(4 5 6)) ; => #(1 2 3 4 5 6)

;;; 數(shù)組

;; 向量和字符串只不過是數(shù)組的特例

;; 二維數(shù)組

(make-array (list 2 2))

;; (make-array '(2 2)) 也是可以的

; => #2A((0 0) (0 0))

(make-array (list 2 2 2))

; => #3A(((0 0) (0 0)) ((0 0) (0 0)))

;; 注意:數(shù)組的默認(rèn)初始值是可以指定的
;; 下面是如何指定的示例:

(make-array '(2) :initial-element 'unset)

; => #(UNSET UNSET)

;; 若想獲取數(shù)組[1][1][1]上的元素:
(aref (make-array (list 2 2 2)) 1 1 1)

; => 0

;;; 變長向量

;; 若將變長向量打印出來,那么它的字面意義上的值和定長向量的是一樣的

(defparameter *adjvec* (make-array '(3) :initial-contents '(1 2 3)
      :adjustable t :fill-pointer t))

*adjvec* ; => #(1 2 3)

;; 添加新的元素:
(vector-push-extend 4 *adjvec*) ; => 3

*adjvec* ; => #(1 2 3 4)

;;; 不怎么嚴(yán)謹(jǐn)?shù)卣f,集合也可被視為列表

(set-difference '(1 2 3 4) '(4 5 6 7)) ; => (3 2 1)
(intersection '(1 2 3 4) '(4 5 6 7)) ; => 4
(union '(1 2 3 4) '(4 5 6 7))        ; => (3 2 1 4 5 6 7)
(adjoin 4 '(1 2 3 4))     ; => (1 2 3 4)

;; 然而,你可能想使用一個更好的數(shù)據(jù)結(jié)構(gòu),而并非一個鏈表

;;; 在Common Lisp中,“字典”和哈希表的實現(xiàn)是一樣的。

;; 創(chuàng)建一個哈希表
(defparameter *m* (make-hash-table))

;; 給定鍵,設(shè)置對應(yīng)的值
(setf (gethash 'a *m*) 1)

;; (通過鍵)檢索對應(yīng)的值
(gethash 'a *m*) ; => 1, t

;; 注意此處有一細(xì)節(jié):Common Lisp往往返回多個值。`gethash`返回的兩個值是t,代表找到了這個元素;返回nil表示沒有找到這個元素。
;;(譯者注:返回的第一個值表示給定的鍵所對應(yīng)的值或者nil;)
;;(第二個是一個布爾值,表示在哈希表中是否存在這個給定的鍵)
;; 例如,如果可以找到給定的鍵所對應(yīng)的值,則返回一個t,否則返回nil

;; 由給定的鍵檢索一個不存在的值,則返回nil
;;(譯者注:這個nil是第一個nil,第二個nil其實是指該鍵在哈希表中也不存在)
 (gethash 'd *m*) ;=> nil, nil

;; 給定一個鍵,你可以指定其對應(yīng)的默認(rèn)值:
(gethash 'd *m* :not-found) ; => :NOT-FOUND

;; 在此,讓我們看一看怎樣處理`gethash`的多個返回值。

(multiple-value-bind
      (a b)
    (gethash 'd *m*)
  (list a b))
; => (NIL NIL)

(multiple-value-bind
      (a b)
    (gethash 'a *m*)
  (list a b))
; => (1 T)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 3\. 函數(shù)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; 使用`lambda`來創(chuàng)建一個匿名函數(shù)。
;; 一個函數(shù)總是返回其形式體內(nèi)最后一個表達(dá)式的值。
;; 將一個函數(shù)對象打印出來后的形式是多種多樣的...

(lambda () "Hello World") ; => #<FUNCTION (LAMBDA ()) {1004E7818B}>

;; 使用`funcall`來調(diào)用lambda函數(shù)
(funcall (lambda () "Hello World")) ; => "Hello World"

;; 或者使用`apply`
(apply (lambda () "Hello World") nil) ; => "Hello World"

;; 顯式地定義一個函數(shù)(譯者注:即非匿名的)
(defun hello-world ()
   "Hello World")
(hello-world) ; => "Hello World"

;; 剛剛上面函數(shù)名"hello-world"后的()其實是函數(shù)的參數(shù)列表
(defun hello (name)
   (format nil "Hello, ~a " name))

(hello "Steve") ; => "Hello, Steve"

;; 函數(shù)可以有可選形參并且其默認(rèn)值都為nil

(defun hello (name &optional from)
    (if from
        (format t "Hello, ~a, from ~a" name from)
        (format t "Hello, ~a" name)))

 (hello "Jim" "Alpacas") ;; => Hello, Jim, from Alpacas

;; 你也可以指定那些可選形參的默認(rèn)值
(defun hello (name &optional (from "The world"))
   (format t "Hello, ~a, from ~a" name from))

(hello "Steve")
; => Hello, Steve, from The world

(hello "Steve" "the alpacas")
; => Hello, Steve, from the alpacas

;; 當(dāng)然,你也可以設(shè)置所謂關(guān)鍵字形參;
;; 關(guān)鍵字形參往往比可選形參更具靈活性。

(defun generalized-greeter (name &key (from "the world") (honorific "Mx"))
    (format t "Hello, ~a ~a, from ~a" honorific name from))

(generalized-greeter "Jim")   ; => Hello, Mx Jim, from the world

(generalized-greeter "Jim" :from "the alpacas you met last summer" :honorific "Mr")
; => Hello, Mr Jim, from the alpacas you met last summer

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 4\. 等式
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Common Lisp具有一個十分復(fù)雜的用于判斷等價的系統(tǒng),下面只是其中一部分的例子

;; 若要比較數(shù)值是否等價,使用`=`
(= 3 3.0) ; => t
(= 2 1) ; => nil

;; 若要比較對象的類型,則使用`eql`
;;(譯者注:抱歉,翻譯水平實在有限,下面是我個人的補充說明)
;;(`eq` 返回真,如果對象的內(nèi)存地址相等)
;;(`eql` 返回真,如果兩個對象內(nèi)存地址相等,或者對象的類型相同,并且值相等)
;;(例如同為整形數(shù)或浮點數(shù),并且他們的值相等時,二者`eql`等價)
;;(想要弄清`eql`,其實有必要先了解`eq`)
;;([可以參考](http://stackoverflow.com/questions/547436/whats-the-difference-between-eq-eql-equal-and-equalp-in-common-lisp))
;;(可以去CLHS上分別查看兩者的文檔)
;;(另外,《實用Common Lisp編程》的4.8節(jié)也提到了兩者的區(qū)別)
(eql 3 3) ; => t
(eql 3 3.0) ; => nil
(eql (list 3) (list 3)) ; => nil

;; 對于列表、字符串、以及位向量,使用`equal`
(equal (list 'a 'b) (list 'a 'b)) ; => t
(equal (list 'a 'b) (list 'b 'a)) ; => nil

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 5\. 控制流
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; 條件判斷語句

(if t                ; “test”,即判斷語句
    "this is true"   ; “then”,即判斷條件為真時求值的表達(dá)式
    "this is false") ; “else”,即判斷條件為假時求值的表達(dá)式
; => "this is true"

;; 在“test”(判斷)語句中,所有非nil或者非()的值都被視為真值
(member 'Groucho '(Harpo Groucho Zeppo)) ; => '(GROUCHO ZEPPO)
(if (member 'Groucho '(Harpo Groucho Zeppo))
    'yep
    'nope)
; => 'YEP

;; `cond`將一系列測試語句串聯(lián)起來,并對相應(yīng)的表達(dá)式求值
(cond ((> 2 2) (error "wrong!"))
      ((< 2 2) (error "wrong again!"))
      (t 'ok)) ; => 'OK

;; 對于給定值的數(shù)據(jù)類型,`typecase`會做出相應(yīng)地判斷
(typecase 1
  (string :string)
  (integer :int))

; => :int

;;; 迭代

;; 當(dāng)然,遞歸是肯定被支持的:

(defun walker (n)
  (if (zerop n)
      :walked
      (walker (1- n))))

(walker) ; => :walked

;; 而大部分場合下,我們使用`DOLIST`或者`LOOP`來進(jìn)行迭代

(dolist (i '(1 2 3 4))
  (format t "~a" i))

; => 1234

(loop for i from 0 below 10
      collect i)

; => (0 1 2 3 4 5 6 7 8 9)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 6\. 可變性
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; 使用`setf`可以對一個已經(jīng)存在的變量進(jìn)行賦值;
;; 事實上,剛剛在哈希表的例子中我們已經(jīng)示范過了。

(let ((variable 10))
    (setf variable 2))
 ; => 2

;; 所謂好的Lisp編碼風(fēng)格就是為了減少使用破壞性函數(shù),防止發(fā)生副作用。

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 7\. 類與對象
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; 我們就不寫什么有關(guān)動物的類了,下面給出的人力車的類

(defclass human-powered-conveyance ()
  ((velocity
    :accessor velocity
    :initarg :velocity)
   (average-efficiency
    :accessor average-efficiency
   :initarg :average-efficiency))
  (:documentation "A human powered conveyance"))

;; `defclass`,后面接類名,以及超類列表
;; 再接著是槽的列表(槽有點像Java里的成員變量),最后是一些可選的特性
;; 例如文檔說明“:documentation”

;; 如果超類列表為空,則默認(rèn)該類繼承于“standard-object”類(standard-object又是T的子類)
;; 這種默認(rèn)行為是可以改變的,但你最好有一定的基礎(chǔ)并且知道自己到底在干什么;
;; 參閱《The Art of the Metaobject Protocol》來了解更多信息。

(defclass bicycle (human-powered-conveyance)
  ((wheel-size
    :accessor wheel-size
    :initarg :wheel-size
    :documentation "Diameter of the wheel.")
   (height
    :accessor height
    :initarg :height)))

(defclass recumbent (bicycle)
  ((chain-type
    :accessor chain-type
    :initarg  :chain-type)))

(defclass unicycle (human-powered-conveyance) nil)

(defclass canoe (human-powered-conveyance)
  ((number-of-rowers
    :accessor number-of-rowers
    :initarg :number-of-rowers)))

;; 在REPL中對human-powered-conveyance類調(diào)用`DESCRIBE`后結(jié)果如下:

(describe 'human-powered-conveyance)

; COMMON-LISP-USER::HUMAN-POWERED-CONVEYANCE
;  [symbol]
;
; HUMAN-POWERED-CONVEYANCE names the standard-class #<STANDARD-CLASS
;                                                    HUMAN-POWERED-CONVEYANCE>:
;  Documentation:
;    A human powered conveyance
;  Direct superclasses: STANDARD-OBJECT
;  Direct subclasses: UNICYCLE, BICYCLE, CANOE
;  Not yet finalized.
;  Direct slots:
;    VELOCITY
;      Readers: VELOCITY
;      Writers: (SETF VELOCITY)
;    AVERAGE-EFFICIENCY
;      Readers: AVERAGE-EFFICIENCY
;      Writers: (SETF AVERAGE-EFFICIENCY)

;; 注意到這些有用的返回信息——Common Lisp一直是一個交互式的系統(tǒng)。

;; 若要定義一個方法;
;; 注意,我們計算自行車輪子周長時使用了這樣一個公式:C = d * pi

(defmethod circumference ((object bicycle))
  (* pi (wheel-size object)))

;; pi在Common Lisp中已經(jīng)是一個內(nèi)置的常量。

;; 假設(shè)我們已經(jīng)知道了效率值(“efficiency value”)和船槳數(shù)大概呈對數(shù)關(guān)系;
;; 那么效率值的定義應(yīng)當(dāng)在構(gòu)造器/初始化過程中就被完成。

;; 下面是一個Common Lisp構(gòu)造實例時初始化實例的例子:

(defmethod initialize-instance :after ((object canoe) &rest args)
  (setf (average-efficiency object)  (log (1+ (number-of-rowers object)))))

;; 接著初構(gòu)造一個實例并檢查平均效率...

(average-efficiency (make-instance 'canoe :number-of-rowers 15))
; => 2.7725887

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 8\. 宏
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; 宏可以讓你擴展語法

;; 例如,Common Lisp并沒有自帶WHILE循環(huán)——所以讓我們自己來為他添加一個;
;; 如果按照匯編程序的直覺來看,我們會這樣寫:

(defmacro while (condition &body body)
    "While `condition` is true, `body` is executed.

`condition` is tested prior to each execution of `body`"
    (let ((block-name (gensym)))
        `(tagbody
           (unless ,condition
               (go ,block-name))
           (progn
           ,@body)
           ,block-name)))

;; 讓我們來看看它的高級版本:

(defmacro while (condition &body body)
    "While `condition` is true, `body` is executed.

`condition` is tested prior to each execution of `body`"
  `(loop while ,condition
         do
         (progn
            ,@body)))

;; 然而,在一個比較現(xiàn)代化的編譯環(huán)境下,這樣的WHILE是沒有必要的;
;; LOOP形式的循環(huán)和這個WHILE同樣的好,并且更易于閱讀。

;; 注意反引號'`',逗號','以及'@'這三個符號; 
;; 反引號'`'是一種所謂“quasiquote”的引用類型的運算符,有了它,之后的逗號“,”才有意義。
;; 逗號“,”意味著解除引用(unquote,即開始求值);
;; “@”符號則表示將當(dāng)前的參數(shù)插入到當(dāng)前整個列表中。
;;(譯者注:要想真正用好、用對這三個符號,需要下一番功夫)
;;(甚至光看《實用 Common Lisp 編程》中關(guān)于宏的介紹都是不夠的)
;;(建議再去讀一讀Paul Graham的兩本著作《ANSI Common Lisp》和《On Lisp》)

;; 函數(shù)`gensym`創(chuàng)建一個唯一的符號——這個符號確保不會出現(xiàn)在其他任何地方。
;; 這樣做是因為,宏是在編譯期展開的
;; 而在宏中聲明的變量名極有可能和常規(guī)代碼中使用的變量名發(fā)生沖突。

;; 可以去《實用 Common Lisp 編程》中閱讀更多有關(guān)宏的內(nèi)容。

拓展閱讀

繼續(xù)閱讀《實用 Common Lisp 編程》一書

致謝

非常感謝Scheme社區(qū)的人們,我基于他們的成果得以迅速的寫出這篇有關(guān)Common Lisp的快速入門 同時也感謝 -?Paul Khuong?,他提出了很多有用的點評。

譯者寄語

“祝福那些將思想鑲嵌在重重括號之內(nèi)的人們。”

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號