引用:
Printing
Some functions can cause output. The simplest one is print, which
prints its argument and then returns it.
> (print 3)
3
3
The first 3 above was printed, the second was returned.
If you want more complicated output, you will need to use format.
Here's an example:
> (format t "An atom: ~S~%and a list: ~S~%and an integer: ~D~%"
nil (list 5) 6)
An atom: NIL
and a list: (5)
and an integer: 6
The first argument to format is either t, nil, or a stream. T specifies
output to the terminal. Nil means not to print anything but to return a
string containing the output instead. Streams are general places for
output to go: they can specify a file, or the terminal, or another
program. This handout will not describe streams in any further detail.
The second argument is a formatting template, which is a string
optionally containing formatting directives.
All remaining arguments may be referred to by the formatting
directives. LISP will replace the directives with some appropriate
characters based on the arguments to which they refer and then print
the resulting string.
Format always returns nil unless its first argument is nil, in which
case it prints nothing and returns a string.
There are three different directives in the above example: ~S, ~D, and
~%. The first one accepts any LISP object and is replaced by a printed
representation of that object (the same representation which is
produced by print). The second one accepts only integers. The third one
doesn't refer to an argument; it is always replaced by a carriage
return.
Another useful directive is ~~, which is replaced by a single ~.
Refer to a LISP manual for (many, many) additional formatting
directives.
Printing(顯示)
有些函數會導致輸出,而最簡單的輸出,就是透過呼叫 print 函數,
它會把參數給輸出到螢幕上,然後函數的傳回值也是剛剛輸出的結果。
如下例:
> (print 3)
3
3
第一個 3 是因為叫用 print 函數而把參數輸出到螢幕上,第二個 3 則是呼叫函數
之後的傳回值。
如果你希望輸出結果複雜一點,你可以使用 format 函數。見下面範例:
> (format t "An atom: ~S~%and a list: ~S~%and an integer: ~D~%"
nil (list 5) 6)
An atom: NIL
and a list: (5)
and an integer: 6
在呼叫 format 函數的時候,第一個參數只可以 T, NIL, 或是其他的輸出串流。
其 T 表示要輸出到終端螢幕上, NIL 表示不要輸出任何值,而使要把原本要輸出的
字串當作是函數的傳回值回傳。而如果是其他的輸出串列,則可以指定是任何像是
檔案、終端機、其他程式都可以。此教學講義不會對其他的輸出串流提供更多的解釋,
言謹於此。第二個輸入的參數則是一個格式化的樣板,有就是一個字串,字串裡面可能
含有一些的格式化指令。
其他剩下的參數,則是跟之前字串裡面的格式化指令是相對應的,Lisp 將會把剩下的
參數用來代換至字串裡面相對應的格式化指令。 Lisp 會根據格式化指令的適當屬性,
把其餘參數用適當的方式帶換掉之後,在輸出格式化之後的字串。
Format 函數的傳回值預設會是 NIL ,除非在呼叫 Format 函數的第一個參數是 NIL ,
如此,則不會把格式化之後的字串輸出到任何對象,而是會把格式化之後的字串當作
是函數呼叫的傳回值。
在上面範例裡面用到的三個不同的格式化指令:~S, ~D 跟 ~% 。第一個 ~S 會接受任何的
Lisp 物件,並且會用該物件的可以顯示的方式來取代掉 ~S (當中可以顯示的方式跟直接
利用 print 函數輸出該物件是一樣的方式)。第二個 ~D 會接受任何的整數值。第三個 ~%
則不會被任何之後的輸入參數所取代,可是它會自動轉換成換行的指令。
另外還有一個有用的格式化指令是 ~~ ,它會自動輸出成只有一個 ~ 。
如果還要更多、更多額外的格式化指令,可以參考其他的 Lisp 手冊。
引用:
Forms and the Top-Level Loop
The things which you type to the LISP interpreter are called forms; the
LISP interpreter repeatedly reads a form, evaluates it, and prints the
result. This procedure is called the read-eval-print loop.
Some forms will cause errors. After an error, LISP will put you into
the debugger so you can try to figure out what caused the error. LISP
debuggers are all different; but most will respond to the command
"help" or ":help" by giving some form of help.
In general, a form is either an atom (for example, a symbol, an
integer, or a string) or a list. If the form is an atom, LISP evaluates
it immediately. Symbols evaluate to their value; integers and strings
evaluate to themselves. If the form is a list, LISP treats its first
element as the name of a function; it evaluates the remaining elements
recursively, and then calls the function with the values of the
remaining elements as arguments.
For example, if LISP sees the form (+ 3 4), it treats + as the name of
a function. It then evaluates 3 to get 3 and 4 to get 4; finally it
calls + with 3 and 4 as the arguments. The + function returns 7, which
LISP prints.
The top-level loop provides some other conveniences; one particularly
convenient convenience is the ability to talk about the results of
previously typed forms. LISP always saves its most recent three
results; it stores them as the values of the symbols *, **, and ***.
For example:
> 3
3
> 4
4
> 5
5
> ***
3
> ***
4
> ***
5
> **
4
> *
4
Forms(表單) 跟 the Top-Level Loop(最高層評估值迴圈)
你一行行打字,所輸入給 Lisp 直譯器的那些資料就稱做是表單(forms) ,
Lisp 直譯器會一直讀取你給它的表單,然後進行運算/評估,並且把傳回值
顯示出來,這個一再重複的過程就稱作是個〝讀取
(資料)─評估
(傳回值)─顯示
(傳回值)〞的迴圈。
有些表單可能會導致錯誤(也就是程式碼沒寫好啦),當執行程式的時候
發生錯誤的話, Lisp 會把進入除錯狀態,以便讓我們找出錯誤發生的原因。
每個 Lisp 版本的除錯模式都不太一樣,但是至少當我們對大多數的除錯程式
輸入 "help" 或是 ":help" ,它應該會顯示相關輔助說明文字。
一般而言,表單裡的資料要嘛就是無法再細分的原子(atom),像是字符、整數、
字串....,這些都是屬於無法再細分的原子,要不然表單裡的資料就是一個串列。
如果表單的資料是原子,那 Lisp 通常很快就可以評估出它的傳回值,字符傳回值
就是它所表示的值,整數跟字串的傳回值就是它們本身而已。但如果表單的資料
是一個串列,那 Lisp 會把這個串列的第一個元素當作是函數的名稱,把其他元素
評估完之後的值當作是輸入參數,然後把這整個串列當作是函數呼叫,
舉例來說,如果表單的資料是 (+ 3 4) ,Lisp 會把 + 當作是最後要呼叫的函數
名稱,然後它逐步評估求值,3 評估值(運算)之後是傳回值是 3 ,4 評估值(運算)
之後傳回值是 4 ,而後呼叫 + 這個函數,而傳入 + 這個函數的參數則是剛剛已經
評估完的值 3 跟 4 ,因此呼叫完 + 這個函數的傳回值會是 7 ,最後 Lisp 再把它顯
示給我們看。
譯註:而我們在使用 Lisp 直譯器的時候,位在 > 之後要給它的,就是位在最高層的
〝讀取
(資料)─評估
(傳回值)─顯示
(傳回值)〞的迴圈。
位在最高層的〝讀取─評估─顯示〞的迴圈,其實有其他的好處,其中一個好處
就是可以隨時取出之前運算的表單資料,Lisp 用 *, **, 跟 *** 分別表示在此表單
的前一、二、三個評估值的表單。如下例:
> 3 ; 要評估的表單是 3 ,所以傳回值是 3
3
> 4 ; 要評估的表單是 4 ,所以傳回值是
4
> 5 ; 要評估的表單是 5 ,所以傳回值是 5
5
> *** ; 要評估的表單是,在這之前推三步的那個表單,所以評估 3 之後,傳回值是 3
3
> *** ; 要評估的表單是,在這之前推三步的那個表單,所以評估 4 之後,傳回值是 4
4
> *** ; 要評估的表單是,在這之前推三步的那個表單,所以評估 5 之後,傳回值是 5
5
> ** ; 要評估的表單是,在這之前推兩步的那個表單,所以評估 4 之後,傳回值是 4
4
> * ; 要評估的表單是,在這之前推一步的那個表單,所以評估 4 之後,傳回值是 4
4
引用:
Special forms
There are a number of special forms which look like function calls but
aren't. These include control constructs such as if statements and do
loops; assignments like setq, setf, push, and pop; definitions such as
defun and defstruct; and binding constructs such as let. (Not all of
these special forms have been mentioned yet. See below.)
One useful special form is the quote form: quote prevents its argument
from being evaluated. For example:
> (setq a 3)
3
> a
3
> (quote a)
A
> 'a ;'a is an abbreviation for (quote a)
A
Another similar special form is the function form: function causes its
argument to be interpreted as a function rather than being evaluated.
For example:
> (setq + 3)
3
> +
3
> '+
+
> (function +)
#<Function + @ #x-fbef9de>
> #'+ ;#'+ is an abbreviation for (function +)
#<Function + @ #x-fbef9de>
The function special form is useful when you want to pass a function as
an argument to another function. See below for some examples of
functions which take functions as arguments.
Special forms (特殊表單)
有一些比較特殊的輸入表單看起來就像是函數呼叫,可是實際上卻不是函數呼叫。
這些特殊表單包含有流程控制命令,如 if 跟 do loop 敘述等,以及用來設定變數的
命令,如 setq, setf, push, 跟 pop ,還有用來定義的命令,如定義函數的 defun 及
定義結構的 defstruct ,還有用來綁定的命令,如 let 。(當然上面並沒有提及所有
的特殊表單,往下看,還會繼續介紹其他的特殊表單。)
最特別的有一個特殊表單 quote 是用來避免它的輸入參數進入評估值的步驟,也
就是它會讓輸入參數以原來的形式當作是傳回值,並不會先經過評估值的步驟。
舉例如下:
> (setq a 3)
3
> a
3
> (quote a)
A
> 'a ; 'a 是 (quote a) 的縮寫
A
另外還有一個類似的特殊表單就是 function 表單,function 會讓它的輸入參數被視作
是某個函數,而不是被拿來評估值。
範例如下:
> (setq + 3)
3
> +
3
> '+
+
> (function +)
#<Function + @ #x-fbef9de>
> #'+ ;#'+ 是 (function +) 的縮寫
#<Function + @ #x-fbef9de>
function 這個特殊表單常常被拿來用在,當你要把函數當作是參數來傳遞的時候。
本文後面會繼續介紹到一些例子,就是把函數拿來當作是輸入參數,此時就會需要
用到 function 這個特殊表單。
引用:
Binding
Binding is lexically scoped assignment. It happens to the variables in
a function's parameter list whenever the function is called: the formal
parameters are bound to the actual parameters for the duration of the
function call. You can bind variables anywhere in a program with the
let special form, which looks like this:
(let ((var1 val1)
(var2 val2)
...)
body)
Let binds var1 to val1, var2 to val2, and so forth; then it executes
the statements in its body. The body of a let follows exactly the same
rules that a function body does. Some examples:
> (let ((a 3)) (+ a 1))
4
> (let ((a 2)
(b 3)
(c 0))
(setq c (+ a b))
c)
5
> (setq c 4)
4
> (let ((c 5)) c)
5
> c
4
Instead of (let ((a nil) (b nil)) ...), you can write (let (a b) ...).
The val1, val2, etc. inside a let cannot reference the variables var1,
var2, etc. that the let is binding. For example,
> (let ((x 1)
(y (+ x 1)))
y)
Error: Attempt to take the value of the unbound symbol X
If the symbol x already has a global value, stranger happenings will
result:
> (setq x 7)
7
> (let ((x 1)
(y (+ x 1)))
y)
8
The let* special form is just like let except that it allows values to
reference variables defined earlier in the let*. For example,
> (setq x 7)
7
> (let* ((x 1)
(y (+ x 1)))
y)
2
The form
(let* ((x a)
(y b))
...)
is equivalent to
(let ((x a))
(let ((y b))
...))
Binding(綁定)
綁定是 lexically scoped 的變數值設定。它發生在當函數呼叫時候,參數列的變數
是用綁定的方式設定變數值:在函數呼叫期間,此時此函數定義時的參數列,其值被
綁定在函數呼叫發生時的輸入參數。其實不管在哪程式裡面的哪裡,你也可以利用 let
這個特殊表單來綁定變數值,其使用形式如下:
(let ((var1 val1)
(var2 val2)
...)
body)
Let 把 var1 綁定成 val1 ,把 var2 綁定成 val2 ,如此類推,然後它會執行
body 這一區塊的程式敘述。 上面 Let 特殊表單裡面的 body 程式區塊敘述執行
的結果就會像是在函數呼叫時的成是敘述有一樣的效果。如下範例:
(譯註:這像是函數呼叫,只是把參數列改成 let 特殊表單而已,而 body 執行完
之後的傳回值,就會是函數傳回值。)
> (let ((a 3)) (+ a 1)) ; 在 let 表單裡面,綁定 a 為 3,然後執行 a+1 ,傳回值就是 4 。
4
> (let ((a 2)
(b 3)
(c 0))
(setq c (+ a b))
c)
5
> (setq c 4)
4
> (let ((c 5)) c)
5
> c
4
如果有綁定值是 NIL 的,如 (let ((a nil) (b nil)) ...) ,就可以縮寫成 (let (a b) ...) 。
Let 特殊表單裡面的綁定值 val1, val2 ... 等的值不能參照 var1, var2 ... 等,因為綁定正在
發生,還沒有結束。如下範例:
> (let ((x 1)
(y (+ x 1)))
y)
Error: Attempt to take the value of the unbound symbol X
如果變數 x 在上面這段程式執行之前已經有全域變數值,那就會發生很莫名奇妙的結果,
如下範例:
> (setq x 7)
7
> (let ((x 1)
(y (+ x 1)))
y)
8
還有一個 let* 也是特殊表單,它跟 let 很像,但是不同的地方是 let* 可以允許
綁定值參考之前已經綁定的變數。如下範例:
> (setq x 7)
7
> (let* ((x 1)
(y (+ x 1)))
y)
2
下面這樣的表單
(let* ((x a)
(y b))
...)
其實就等同於,如下
(let ((x a))
(let ((y b))
...))
引用:
Dynamic Scoping
The let and let* forms provide lexical scoping, which is what you
expect if you're used to programming in C or Pascal. Dynamic scoping is
what you get in BASIC: if you assign a value to a dynamically scoped
variable, every mention of that variable returns that value until you
assign another value to the same variable.
In LISP, dynamically scoped variables are called special variables. You
can declare a special variable with the defvar special form. Here are
some examples of lexically and dynamically scoped variables.
In this example, the function check-regular references a regular (ie,
lexically scoped) variable. Since check-regular is lexically outside of
the let which binds regular, check-regular returns the variable's
global value.
> (setq regular 5)
5
> (defun check-regular () regular)
CHECK-REGULAR
> (check-regular)
5
> (let ((regular 6)) (check-regular))
5
In this example, the function check-special references a special (ie,
dynamically scoped) variable. Since the call to check-special is
temporally inside of the let which binds special, check-special returns
the variable's local value.
> (defvar *special* 5)
*SPECIAL*
> (defun check-special () *special*)
CHECK-SPECIAL
> (check-special)
5
> (let ((*special* 6)) (check-special))
6
By convention, the name of a special variable begins and ends with a *.
Special variables are chiefly used as global variables, since
programmers usually expect lexical scoping for local variables and
dynamic scoping for global variables.
For more information on the difference between lexical and dynamic
scoping, see _Common LISP: the Language_.
Dynamic Scoping
let 跟 let* 這樣的特殊表單提供了 lexical scoping ,那就像你在寫 C 或是 Pascal 程式
所預期一樣的變數可視範圍。而還有一種 Dynamic scoping 就如同 BASIC 語言所提供的一樣,
如果你指定一個變數值給 dynamically scoped 的變數,那不管你在何時去讀取變數的值,
都會是一開始指定的那個變數值,除非你有給它另一個新變數值,以取代之。
譯註:既,全域變數 ,參考
http://www.supelec.fr/docs/cltl/clm/node67.html 。
在 Lisp 裡面,這些 dynamically scoped 的變數被稱做是特殊變數(special variables),
你可以透過 defvar 特殊表單來定義特殊變數。下面是一些 lexically 跟 dynamically
scoped 變數的例子。
在下面的這麼範例裡面, check-regular 函數裡面調用了 regular 這個一般變數(亦即
lexically scoped 的變數)。因為 check-regular 函數的定義是在 let 區塊之外,所以 let
區塊裡面的 regular 綁定並不會影響到 check-regular 函數裡面 regular 變數值,所以
check-regular 的傳回值是 regular 變數的全域可視範圍的值。
> (setq regular 5)
5
> (defun check-regular () regular)
CHECK-REGULAR
> (check-regular)
5
> (let ((regular 6)) (check-regular))
5
在下面的這麼範例裡面, check-special 函數裡面調用了 special 這個特殊變數(亦即
dynamically scoped 的變數)。因為在 let 區塊裡面有一段暫時呼叫了 check-special 這個
函數,而且 let 有暫時綁定 special 特殊變數新值,所以 check-special 會傳回的是受到
let 區塊綁定影響的區域變數值。
> (defvar *special* 5)
*SPECIAL*
> (defun check-special () *special*)
CHECK-SPECIAL
> (check-special)
5
> (let ((*special* 6)) (check-special))
6
為了方便記億與區別,通常會把特殊變數的名稱前後會用 * 包圍起來。特殊變數主要
被用在當作是全域變數,因為程式設計師通常會預期區域變數是 lexical scoping ,而
全域變數是 dynamic scoping 。
如果需要更多關於 lexical scoping 跟 dynamic scoping 的區別,請參看
_Common LISP: the Language_ 這本書。
引用:
Arrays
The function make-array makes an array. The aref function accesses its
elements. All elements of an array are initially set to nil. For
example:
> (make-array '(3 3))
#2a((NIL NIL NIL) (NIL NIL NIL) (NIL NIL NIL))
> (aref * 1 1)
NIL
> (make-array 4) ;1D arrays don't need the extra parens
#(NIL NIL NIL NIL)
Array indices always start at 0.
See below for how to set the elements of an array.
Strings
A string is a sequence of characters between double quotes. LISP
represents a string as a variable-length array of characters. You can
write a string which contains a double quote by preceding the quote
with a backslash; a double backslash stands for a single backslash. For
example:
"abcd" has 4 characters
"\"" has 1 character
"\\" has 1 character
Here are some functions for dealing with strings:
> (concatenate 'string "abcd" "efg")
"abcdefg"
> (char "abc" 1)
#\b ;LISP writes characters preceded by #\
> (aref "abc" 1)
#\b ;remember, strings are really arrays
The concatenate function can actually work with any type of sequence:
> (concatenate 'string '(#\a #\b) '(#\c))
"abc"
> (concatenate 'list "abc" "de")
(#\a #\b #\c #\d #\e)
> (concatenate 'vector '#(3 3 3) '#(3 3 3))
#(3 3 3 3 3 3)
Structures
LISP structures are analogous to C structs or Pascal records. Here is
an example:
> (defstruct foo
bar
baaz
quux)
FOO
This example defines a data type called foo which is a structure
containing 3 fields. It also defines 4 functions which operate on this
data type: make-foo, foo-bar, foo-baaz, and foo-quux. The first one
makes a new object of type foo; the others access the fields of an
object of type foo. Here is how to use these functions:
> (make-foo)
#s(FOO :BAR NIL :BAAZ NIL :QUUX NIL)
> (make-foo :baaz 3)
#s(FOO :BAR NIL :BAAZ 3 :QUUX NIL)
> (foo-bar *)
NIL
> (foo-baaz **)
3
The make-foo function can take a keyword argument for each of the
fields a structure of type foo can have. The field access functions
each take one argument, a structure of type foo, and return the
appropriate field.
See below for how to set the fields of a structure.
Arrays(陣列)
函數 make-array 可以產生陣列,而函數 aref 則可以純取陣列裡面的元素。
陣列裡所有元素的初始則設定值是 NIL 。如下範例:
> (make-array '(3 3))
#2a((NIL NIL NIL) (NIL NIL NIL) (NIL NIL NIL))
> (aref * 1 1)
NIL
> (make-array 4) ; 一維陣列的維度不需要額外的小括弧
#(NIL NIL NIL NIL)
陣列的索引值必定是由 0 開始起算。
繼續往下看,將會學到如何設定陣列的元素。
Strings(字串)
所謂的字串就是被兩個 " 所包夾在中間的字元串。 Lisp 實際上是把字串視為是
可變長度的字元陣列。如果要表示的字串裡面本身就包含有 " 的話,那需要在 "
前面加上倒斜線 \ ,而用連續的兩個倒斜線來是表示字串裡面的一個倒斜線。
如下範例:
"abcd" 包含有 4 個字元
"\"" 包含有 1 個字元
"\\" 包含有 1 個字元
下面是一些用來處理字串的函數範例:
> (concatenate 'string "abcd" "efg") ; 連接字串用 concatenate 函數
"abcdefg"
> (char "abc" 1)
#\b ; Lisp 會在字元前面加上 #\ 用來表示字元。
> (aref "abc" 1)
#\b ; 請記住,字串其實就是字元陣列而已。
連接字串用的 concatenate 函數實際上可以用來連接任何型別的序列:
> (concatenate 'string '(#\a #\b) '(#\c))
"abc"
> (concatenate 'list "abc" "de")
(#\a #\b #\c #\d #\e)
> (concatenate 'vector '#(3 3 3) '#(3 3 3))
#(3 3 3 3 3 3)
Structures(結構)
Lisp 的結構就類似 C 語言的 struct 跟 Pascal 語言的 record 。下面是一個範例:
> (defstruct foo
bar
baaz
quux)
FOO
這個範例定義了一個名為 foo 的資料型別,這個型別的結構實際上包含了三個欄位。
在定義結構的同時,實際上它也定義了四個可以操作這個資料型別的的函數,分別是
make-foo, foo-bar, foo-baaz, 跟 foo-quux 。第一個函數 make-foo 可以用來產生 foo 資料
型別的物件,而其他三個函數則可以用來取得 foo 資料型別當中對應的資料欄位。
底下是,如何使用這些函數的範例:
> (make-foo)
#s(FOO :BAR NIL :BAAZ NIL :QUUX NIL)
> (make-foo :baaz 3)
#s(FOO :BAR NIL :BAAZ 3 :QUUX NIL)
> (foo-bar *)
NIL
> (foo-baaz **)
3
只要是 foo 結構所有的欄位,在產生物件時候用的 make-foo 函數都可以接受對應欄位的
keyword 參數。而存取資料欄位的取用函數則可以接受一個 foo 物件當作是輸入參數,並且
傳回該結構裡對應的資料欄位之值。
繼續往下看,將會學到如何設定結構裡各欄位的值。
引用:
Setf
Certain forms in LISP naturally define a memory location. For example,
if the value of x is a structure of type foo, then (foo-bar x) defines
the bar field of the value of x. Or, if the value of y is a one-
dimensional array, (aref y 2) defines the third element of y.
The setf special form uses its first argument to define a place in
memory, evaluates its second argument, and stores the resulting value
in the resulting memory location. For example,
> (setq a (make-array 3))
#(NIL NIL NIL)
> (aref a 1)
NIL
> (setf (aref a 1) 3)
3
> a
#(NIL 3 NIL)
> (aref a 1)
3
> (defstruct foo bar)
FOO
> (setq a (make-foo))
#s(FOO :BAR NIL)
> (foo-bar a)
NIL
> (setf (foo-bar a) 3)
3
> a
#s(FOO :BAR 3)
> (foo-bar a)
3
Setf is the only way to set the fields of a structure or the elements
of an array.
Here are some more examples of setf and related functions.
> (setf a (make-array 1)) ;setf on a variable is equivalent to setq
#(NIL)
> (push 5 (aref a 1)) ;push can act like setf
(5)
> (pop (aref a 1)) ;so can pop
5
> (setf (aref a 1) 5)
5
> (incf (aref a 1)) ;incf reads from a place, increments,
6 ;and writes back
> (aref a 1)
6
Setf
在 Lisp 裡面有某些表單實際上表示的就是記憶體裡的位置,舉例來說,如果 x 是
foo 資料型別的結構的話,那 (foo-bar x) 表示的就是 x 裡面的 bar 資料欄位。
另外,如果 y 是一維陣列,那 (aref y 2) 表示的就是 y 陣列裡面的第三個元素。
而 setf 特殊表單可以接受兩個參數,第一個參數是一個記憶體裡的位置,而第二個參數
在被評估求值之後,所評估出來的值將會被存入第一個參數所指的記憶體位置。舉例如下:
> (setq a (make-array 3))
#(NIL NIL NIL)
> (aref a 1)
NIL
> (setf (aref a 1) 3)
3
> a
#(NIL 3 NIL)
> (aref a 1)
3
> (defstruct foo bar)
FOO
> (setq a (make-foo))
#s(FOO :BAR NIL)
> (foo-bar a)
NIL
> (setf (foo-bar a) 3)
3
> a
#s(FOO :BAR 3)
> (foo-bar a)
3
Setf 是唯一可以用來設定結構裡資料欄位的值,以及設定陣列裡元素之值的方法。
下面是跟 setf 及相關的函數呼叫的一些範例:
> (setf a (make-array 1)) ; setf 作用在單一個變數上面的效果跟 setq 一樣。
#(NIL)
> (push 5 (aref a 1)) ; push 也可以拿來當作是 setf 使用(不過參數順序不太一樣喔!)
(5)
> (pop (aref a 1)) ; 既然 push 可以存值,那 pop 當然就可以取值。
5
> (setf (aref a 1) 5)
5
> (incf (aref a 1)) ; incf 的功用是從記憶體位置讀取出值,然後累加
6 ; 最後在把累加完之後的值,存回到相同的記憶體位置。
> (aref a 1)
6
引用:
Booleans and Conditionals
LISP uses the self-evaluating symbol nil to mean false. Anything other
than nil means true. Unless we have a reason not to, we usually use the
self-evaluating symbol t to stand for true.
LISP provides a standard set of logical functions, for example and, or,
and not. The and and or connectives are short-circuiting: and will not
evaluate any arguments to the right of the first one which evaluates to
nil, while or will not evaluate any arguments to the right of the first
one which evaluates to t.
LISP also provides several special forms for conditional execution. The
simplest of these is if. The first argument of if determines whether
the second or third argument will be executed:
> (if t 5 6)
5
> (if nil 5 6)
6
> (if 4 5 6)
5
If you need to put more than one statement in the then or else clause
of an if statement, you can use the progn special form. Progn executes
each statement in its body, then returns the value of the final one.
> (setq a 7)
7
> (setq b 0)
0
> (setq c 5)
5
> (if (> a 5)
(progn
(setq a (+ b 7))
(setq b (+ c 8)))
(setq b 4))
13
An if statement which lacks either a then or an else clause can be
written using the when or unless special form:
> (when t 3)
3
> (when nil 3)
NIL
> (unless t 3)
NIL
> (unless nil 3)
3
When and unless, unlike if, allow any number of statements in their
bodies. (Eg, (when x a b c) is equivalent to (if x (progn a b c)).)
> (when t
(setq a 5)
(+ a 6))
11
More complicated conditionals can be defined using the cond special
form, which is equivalent to an if ... else if ... fi construction.
A cond consists of the symbol cond followed by a number of cond
clauses, each of which is a list. The first element of a cond clause is
the condition; the remaining elements (if any) are the action. The cond
form finds the first clause whose condition evaluates to true (ie,
doesn't evaluate to nil); it then executes the corresponding action and
returns the resulting value. None of the remaining conditions are
evaluated; nor are any actions except the one corresponding to the
selected condition. For example:
> (setq a 3)
3
> (cond
((evenp a) a) ;if a is even return a
((> a 7) (/ a 2)) ;else if a is bigger than 7 return a/2
((< a 5) (- a 1)) ;else if a is smaller than 5 return a-1
(t 17)) ;else return 17
2
If the action in the selected cond clause is missing, cond returns what
the condition evaluated to:
> (cond ((+ 3 4)))
7
Here's a clever little recursive function which uses cond. You might be
interested in trying to prove that it terminates for all integers x at
least 1. (If you succeed, please publish the result.)
> (defun hotpo (x steps) ;hotpo stands for Half Or Triple Plus One
(cond
((= x 1) steps)
((oddp x) (hotpo (+ 1 (* x 3)) (+ 1 steps)))
(t (hotpo (/ x 2) (+ 1 steps)))))
A
> (hotpo 7 0)
16
The LISP case statement is like a C switch statement:
> (setq x 'b)
B
> (case x
(a 5)
((d e) 7)
((b f) 3)
(otherwise 9))
3
The otherwise clause at the end means that if x is not a, b, d, e, or
f, the case statement will return 9.
Booleans(布林值) and Conditionals(控制判斷條件)
Lisp 使用其值為本身的 NIL 表示〝偽〞。任何其他不是 NIL 的值都表示真。
然而除非有特殊理由要這樣處理,不然我們還是會習慣上利用其值為本身的 T
表示〝真〞。
Lisp 提供了一系列的標準的邏輯函數,比如像是 and, or 以及 not 函數。
and 以及 or 函數是屬於 short-circuit ,也就是說,如果 and 函數的有任何
一個個參數的運算結果已經是 NIL ,拿之後的參數將不用進行運算估值;而 or
函數如果有任何一個參數運算結果事 T ,那之後的參數就不會進行運算估值。
Lisp 也提供了幾個特殊表單用來做控制判斷執行的條件。最簡單的就是 if 敘述
,在 if 敘述的第一個參數將會決定,接下來執行的會是第二個或是第三個參數。
> (if t 5 6)
5
> (if nil 5 6)
6
> (if 4 5 6)
5
如果你在 if 敘述之後的 then(第二個參數) 或是 else(第三個參數) 的部份想要
執行超過一個以上的敘述,那你可以使用 progn 這個特殊表單。 progn 將會執行
在它內部的每一個敘述,並且傳回最後一個評估值之後的結果。
> (setq a 7)
7
> (setq b 0)
0
> (setq c 5)
5
> (if (> a 5)
(progn
(setq a (+ b 7))
(setq b (+ c 8)))
(setq b 4))
13
if 敘述如果缺乏 then(第二個參數) 或是 else(第三個參數) 的部份,其實也可以
用 when 或是 unless 特殊表單改寫,如下範例:
> (when t 3)
3
> (when nil 3)
NIL
> (unless t 3)
NIL
> (unless nil 3)
3
when 跟 unless 特殊表單並不像 if 只可以放一個敘述,他們可以放任一個數的
敘述在他們內部當作參數。(例如: (when x a b c) 就等價於 (if x (progn a b c)) 。 )
> (when t
(setq a 5)
(+ a 6))
11
更複雜的控制判斷條件可以透過 cond 特殊表單來處裡, cond 特殊表單相當於
if ... else if ... fi 控制判斷條件一樣。
cond 特殊表單包含有開頭的 cond 字符,後面接的一連串的判斷子句,每一個
判斷句都是一個串列,該串列的第一個元素就是判斷條件,而剩下的元素(如果有的話)
就是要有可能要執行的敘述句。 cond 特殊表單會找尋第一個滿足判斷條件為真(也就是,
不是 NIL)的子句,然後執行該子句裡面對應的敘述句,並且把運算評估完的結果當作是
傳回值。而剩下的其他子句就不會被運行評估了, cond 特殊表單只會運行至多一個符合
判斷結果為真的子句敘述。如下範例:
> (setq a 3)
3
> (cond
((evenp a) a) ;如果(if) a 是偶數,則傳回值為 a
((> a 7) (/ a 2)) ;不然,如果(else if) a 比 7 大,則傳回值為 a/2
((< a 5) (- a 1)) ;不然,如果(else if) a 比 5 小,則傳回值為 a-1
(t 17)) ;不然(else),傳回值為 17
2
如果在 cond 特殊表單裡面,判斷條件為真且要執行的那個子句,並沒有要執行的
敘述句部分的話,那 cond 表單就會傳回判斷條件為真的那個結果。如下:
> (cond ((+ 3 4)))
7
接下來是一個用到 cond 特殊表單的遞迴函數定義的巧妙小例子。你或許可以試著
証明看任何 x 以比 1 大的整數值帶入,最後這個遞迴函數都會終止。(如果你成功
證明出來了,請務必要昭告天下!)
(譯註:這是數學界有名的 3x+1 猜想,至 2006 年目前依然無人成功証出。)
> (defun hotpo (x steps) ; hotpo 會把偶數減半,把奇數乘三後加一
(cond
((= x 1) steps)
((oddp x) (hotpo (+ 1 (* x 3)) (+ 1 steps)))
(t (hotpo (/ x 2) (+ 1 steps)))))
A
> (hotpo 7 0) ; 從 7 經 hotpo 運算到 1 共要經過 16 步。
16
Lisp 也有一個 case 敘述句,就類似 C 語言的 switch 敘述句一樣。如下範例:
> (setq x 'b)
B
> (case x
(a 5) ; 如果 x 是 a ,那傳回值就是 5
((d e) 7) ; 如果 x 是 d 或 e ,那傳回值就是 7
((b f) 3) ; 如果 x 是 b 或 f ,那傳回值就是 3
(otherwise 9)) ; 此外,那傳回值就是 9
3
最後的 otherwise 子句,所表示的意思是〝如果 x 不是 a, b, d, e, 或是 f ,那傳回值就是 9 。〞
引用:
Iteration
The simplest iteration construct in LISP is loop: a loop construct
repeatedly executes its body until it hits a return special form. For
example,
> (setq a 4)
4
> (loop
(setq a (+ a 1))
(when (> a 7) (return a)))
8
> (loop
(setq a (- a 1))
(when (< a 3) (return)))
NIL
The next simplest is dolist: dolist binds a variable to the elements of
a list in order and stops when it hits the end of the list.
> (dolist (x '(a b c)) (print x))
A
B
C
NIL
Dolist always returns nil. Note that the value of x in the above
example was never nil: the NIL below the C was the value that dolist
returned, printed by the read-eval-print loop.
The most complicated iteration primitive is called do. A do statement
looks like this:
> (do ((x 1 (+ x 1))
(y 1 (* y 2)))
((> x 5) y)
(print y)
(print 'working))
1
WORKING
2
WORKING
4
WORKING
8
WORKING
16
WORKING
32
The first part of a do specifies what variables to bind, what their
initial values are, and how to update them. The second part specifies a
termination condition and a return value. The last part is the body. A
do form binds its variables to their initial values like a let, then
checks the termination condition. As long as the condition is false, it
executes the body repeatedly; when the condition becomes true, it
returns the value of the return-value form.
The do* form is to do as let* is to let.
Iteration(重複結構)
在 Lisp 中最簡單的重複結構就是 loop(迴圈) 了: loop (迴圈)結構會一再重複執行
其內部的指令,直到執行到 return 特殊表單才會結束。如下範例:
> (setq a 4)
4
> (loop
(setq a (+ a 1))
(when (> a 7) (return a)))
8
> (loop
(setq a (- a 1))
(when (< a 3) (return)))
NIL
下一個最簡單的重複結構就是 dolist : dolist 會把變數依序綁值於串列裡面的所有
元素,直到把達到串列底部沒有元素才結束。如下範例:
> (dolist (x '(a b c)) (print x))
A
B
C
NIL
Dolist 的傳回值必定是 NIL 。請注意看上面範例裡面 x 綁訂的值卻從未是 NIL ,
在 C 後面的 NIL 是 dolist 的傳回值,也就是要滿足 〝讀取─評估─顯示〞迴圈必定會
顯示的評估(運算)值。
最複雜的重複結構主要就是 do 迴圈了。一個 do 迴圈的範例看起來就像下面這樣:
> (do ((x 1 (+ x 1))
(y 1 (* y 2)))
((> x 5) y)
(print y)
(print 'working))
1
WORKING
2
WORKING
4
WORKING
8
WORKING
16
WORKING
32
在上面範例裡面,在 do 迴圈的後面的第一個大區塊裡的是變數名稱,以及該變數綁定的
初始值,還有每次迴圈運行一圈之後,變數的更新條件。第二個大區塊裡的則是 do 迴圈的
終止條件,以及 do 迴圈結束之後的傳回值。(譯註:此終止條件是在每次進入迴圈主體前
檢查,也就是迴圈主體可能會連一次都沒有被執行到。)最後一個大區塊,則是迴圈主體。
do 表單會先如同 let 特殊表單依樣綁定變數初始值,然後檢查迴圈終止條件是否成立,
只要每次檢查終止條件不成立,那就會執行迴圈主體,然後再回到檢查終止條件地部份,
直到檢查到終止條件成立,則傳回當初在第二大區塊的所指定的傳回值。
另外還有一個 do* 表單,功能如同上面的 do 表單,只是相對於把上面敘述的 let 改成
let* 而已。
Non-local Exits
The return special form mentioned in the section on iteration is an
example of a nonlocal return. Another example is the return-from form,
which returns a value from the surrounding function:
> (defun foo (x)
(return-from foo 3)
x)
FOO
> (foo 17)
3
Actually, the return-from form can return from any named block -- it's
just that functions are the only blocks which are named by default. You
can create a named block with the block special form:
> (block foo
(return-from foo 7)
3)
7
The return special form can return from any block named nil. Loops are
by default labelled nil, but you can make your own nil-labelled blocks:
> (block nil
(return 7)
3)
7
Another form which causes a nonlocal exit is the error form:
> (error "This is an error")
Error: This is an error
The error form applies format to its arguments, then places you in the
debugger.
引用:
Funcall, Apply, and Mapcar
Earlier I promised to give some functions which take functions as
arguments. Here they are:
> (funcall #'+ 3 4)
7
> (apply #'+ 3 4 '(3 4))
14
> (mapcar #'not '(t nil t nil t nil))
(NIL T NIL T NIL T)
Funcall calls its first argument on its remaining arguments.
Apply is just like funcall, except that its final argument should be a
list; the elements of that list are treated as if they were additional
arguments to a funcall.
The first argument to mapcar must be a function of one argument; mapcar
applies this function to each element of a list and collects the
results in another list.
Funcall and apply are chiefly useful when their first argument is a
variable. For instance, a search engine could take a heuristic function
as a parameter and use funcall or apply to call that function on a
state description. The sorting functions described later use funcall
to call their comparison functions.
Mapcar, along with nameless functions (see below), can replace many
loops.
Lambda
If you just want to create a temporary function and don't want to
bother giving it a name, lambda is what you need.
> #'(lambda (x) (+ x 3))
(LAMBDA (X) (+ X 3))
> (funcall * 5)
8
The combination of lambda and mapcar can replace many loops. For
example, the following two forms are equivalent:
> (do ((x '(1 2 3 4 5) (cdr x))
(y nil))
((null x) (reverse y))
(push (+ (car x) 2) y))
(3 4 5 6 7)
> (mapcar #'(lambda (x) (+ x 2)) '(1 2 3 4 5))
(3 4 5 6 7)
Sorting
LISP provides two primitives for sorting: sort and stable-sort.
> (sort '(2 1 5 4 6) #'<)
(1 2 4 5 6)
> (sort '(2 1 5 4 6) #'>)
(6 5 4 2 1)
The first argument to sort is a list; the second is a comparison
function. The sort function does not guarantee stability: if there are
two elements a and b such that (and (not (< a b)) (not (< b a))), sort
may arrange them in either order. The stable-sort function is exactly
like sort, except that it guarantees that two equivalent elements
appear in the sorted list in the same order that they appeared in the
original list.
Be careful: sort is allowed to destroy its argument, so if the original
sequence is important to you, make a copy with the copy-list or copy-seq
function.
Equality
LISP has many different ideas of equality. Numerical equality is
denoted by =. Two symbols are eq if and only if they are identical. Two
copies of the same list are not eq, but they are equal.
> (eq 'a 'a)
T
> (eq 'a 'b)
NIL
> (= 3 4)
NIL
> (eq '(a b c) '(a b c))
NIL
> (equal '(a b c) '(a b c))
T
> (eql 'a 'a)
T
> (eql 3 3)
T
The eql predicate is equivalent to eq for symbols and to = for numbers
or the same type:
> (eql 2.0 2)
NIL
> (= 2.0 2)
T
> (eq 12345678901234567890 12345678901234567890)
NIL
> (= 12345678901234567890 12345678901234567890)
T
> (eql 12345678901234567890 12345678901234567890)
T
The equal predicate is equivalent to eql for symbols and numbers. It is
true for two conses if and only if their cars are equal and their cdrs
are equal. It is true for two structures if and only if the structures
are the same type and their corresponding fields are equal.
Funcall, Apply, and Mapcar (函數當作參數)
在本文前半塊,我曾說過要給幾個把函數名稱當作是函數傳入參數的例子。舉例如下:
> (funcall #'+ 3 4)
7
> (apply #'+ 3 4 '(3 4))
14
> (mapcar #'not '(t nil t nil t nil))
(NIL T NIL T NIL T)
funcall 會呼叫以第一個參數為名的函數,並把 funcall 的其他參數當作是要呼叫的
函數的傳入參數。
apply 就像是 funcall 一樣的功用,除了 apply 的最後一個參數必須要是串列;這
最後串列裡面的元素,就像是在使用 funcall 時的額外參數一樣。
mapcar 的第一個參數必須是可以作用於單一傳入值的函數名稱, mapcar 會把該函數
名稱套用在,其後參數串列的每一個元素上,並且把函數呼叫結果集合起來,形成新的
串列回傳。
funcall 跟 apply 就是因為他們的第一個參數可以是變數,所以特別有用。舉例應用如,
當一個搜尋引擎可以採用啟發式的函數當作是參數,並且利用 funcall 或 apply 把那個
函數參數作用在狀態敘述上。稍後會介紹的排序函數,也是利用 funcall 來傳遞排序時
要用的哪個比較函數來比較大小。
mapcar 跟未具名函數(後面會介紹)一起使用,可以取代掉很多迴圈的使用。
Lambda (未具名函數)
如果你想要創造一個暫時性使用的函數,並且不想煩惱應該給那個函數什麼名稱,此時就
可以使用 lambda (未具名函數)。
> #'(lambda (x) (+ x 3))
(LAMBDA (X) (+ X 3))
> (funcall * 5) ; 譯註: * 表示前一個輸入表單,在此就是 #'(lambda (x) (+ x 3))
8
把 lambda 跟 mapcar 一起組合使用可以取代掉大多數的迴圈的使用。如下範例,下面的兩個
表單是等價的。
> (do ((x '(1 2 3 4 5) (cdr x))
(y nil))
((null x) (reverse y))
(push (+ (car x) 2) y))
(3 4 5 6 7)
> (mapcar #'(lambda (x) (+ x 2)) '(1 2 3 4 5))
(3 4 5 6 7)
Sorting(排序)
Lisp 提供了兩個主要的排序函數: sort 跟 stable-sort 。
> (sort '(2 1 5 4 6) #'<)
(1 2 4 5 6)
> (sort '(2 1 5 4 6) #'>)
(6 5 4 2 1)
sort 的第一個參數是一個串列,而第二個參數則是一個比較大小用的比較函數的名稱。
sort 函數並不保證排序的穩定性,也就是說,如果有兩個元素 a 與 b 滿足
(and (not (< a b)) (not (< b a))) (譯註:也就是 a 與 b 套用在排序函數時相等),
sort 或許有可能在排序之後,會對調 a 與 b 的順序。而 stable-sort 跟 sort 使用方式
完全一樣,除了 stable-sort 保證對於相同的元素必定不會對調順序。
請務必注意: sort 允許破壞他的輸入參數序列,所以如果原始傳入參數對你而言是很重要的,
請先利用 copy-list 或 copy-seq 做好備份。
Equality(相等)
Lisp 對於"相等"的意義有很多種類型。 數值上的相等是用 = 來判別。兩個字符則是用 eq 來
檢查他們是否是同一個。兩個有相同值的串列拷貝並不是 eq 的(譯註:不同的記憶體位置),但這
兩個有相同值的串列拷貝卻是 equal 的(譯註:儲存的數據是一樣的)。
> (eq 'a 'a)
T
> (eq 'a 'b)
NIL
> (= 3 4)
NIL
> (eq '(a b c) '(a b c))
NIL
> (equal '(a b c) '(a b c))
T
> (eql 'a 'a)
T
> (eql 3 3)
T
eql 判斷式等價於 "判斷是否是相同型別" 加上 "如果同是字符,判斷是否 eq " 再加上
"如果同是數值,判斷是否 = "的合體。
> (eql 2.0 2)
NIL
> (= 2.0 2)
T
> (eq 12345678901234567890 12345678901234567890)
NIL
> (= 12345678901234567890 12345678901234567890)
T
> (eql 12345678901234567890 12345678901234567890)
T
用在 字符跟數值上, equal 判斷式就等價於 eql 。對於兩個 cons 而言,如果他們的 car
跟 cdr 都是 equal ,那這兩個 cons 就是 equal 的。對於兩個 structures (結構) 而言,
如果他們有相同的資料型別,並且相對應的資料欄位是 equal 的,那這兩個結構就是 equal 的。
引用:
Some Useful List Functions
These functions all manipulate lists.
> (append '(1 2 3) '(4 5 6)) ;concatenate lists
(1 2 3 4 5 6)
> (reverse '(1 2 3)) ;reverse the elements of a list
(3 2 1)
> (member 'a '(b d a c)) ;set membership -- returns the first tail
(A C) ;whose car is the desired element
> (find 'a '(b d a c)) ;another way to do set membership
A
> (find '(a b) '((a d) (a d e) (a b d e) ()) :test #'subsetp)
(A B D E) ;find is more flexible though
> (subsetp '(a b) '(a d e)) ;set containment
NIL
> (intersection '(a b c) '(b)) ;set intersection
(B)
> (union '(a) '(b)) ;set union
(A B)
> (set-difference '(a b) '(a)) ;set difference
(B)
Subsetp, intersection, union, and set-difference all assume that each
argument contains no duplicate elements -- (subsetp '(a a) '(a b b)) is
allowed to fail, for example.
Find, subsetp, intersection, union, and set-difference can all take a
:test keyword argument; by default, they all use eql.
一些好用的串列處理函數
下面是一些用來操作串列的有用函數。
> (append '(1 2 3) '(4 5 6)) ; 連結許多串列
(1 2 3 4 5 6)
> (reverse '(1 2 3)) ; 逆轉一個串列裡面的元素
(3 2 1)
> (member 'a '(b d a c)) ; 集合元素的"屬於"判斷 -- 它會傳回第一個找到的元素
(A C) ; 至後方所有元素所形成的串列,也就是找第一個 car 是該元素的串列
; 譯註:空串列NIL 即為偽,其他任何非空串列皆表示真。
> (find 'a '(b d a c)) ; 另一個檢查元素是否屬於該集合的方法就是用 find 。
A
> (find '(a b) '((a d) (a d e) (a b d e) ()) :test #'subsetp)
(A B D E) ; find 是很有彈型的,可以傳入要用來判斷的函數。
; 上面例子就是改用 subsectp (檢查是否為子集合) 來找尋滿足條件的集合。
> (subsetp '(a b) '(a d e)) ; 檢查是否為子集合
NIL
> (intersection '(a b c) '(b)) ; 求集合的交集
(B)
> (union '(a) '(b)) ; 求集合的聯集
(A B)
> (set-difference '(a b) '(a)) ; 求差集合
(B)
Subsetp, intersection, union, 和 set-difference 都有一個基本假設就是傳入值的參數串列內
不會有重複的元素(也就是集合),不然的話,像是 (subsetp '(a a) '(a b b)) 判斷出來的傳回值
就可能是偽。
Find, subsetp, intersection, union, 和 set-difference 都可以加上 :test 這一個 keyword 參數,
用以改變判斷條件,而如果沒有使用 :test 改寫判斷條件的話,預設就是使用 eql 當作是判斷條件。
=.= 慢慢來~不要急~ :P
Getting Started with Emacs
You can use Emacs to edit LISP code: most Emacses are set up to enter
LISP mode automatically when they find a file which ends in .lisp, but
if yours isn't, you can type M-x lisp-mode.
You can run LISP under Emacs, too: make sure that there is a command in
your path called "lisp" which runs your favorite LISP. For example, you
could type
ln -s /usr/local/bin/clisp ~/bin/lisp
Then in Emacs type M-x run-lisp. You can send LISP code to the LISP you
just started, and do all sorts of other cool things; for more
information, type C-h m from any buffer which is in LISP mode.
Actually, you don't even need to make a link. Emacs has a variable
called inferior-lisp-program; so if you add the line
(setq inferior-lisp-program "/usr/local/bin/clisp")
to your .emacs file, Emacs will know where to find CLISP when
you type M-x run-lisp.