看了《黑客與畫家》,對Lisp語言比較好奇,於是就弄了本《Lisp使用教程》,有空的時候就學習一下。Lisp的實現有很多,我用的Emacs+sbcl+slime,因為網上資料比較好找。跟着的前幾章體驗了一下,先記錄一下:
在slime中,你會看到
CL-USER>
敲入命令,只要合乎Lisp語法,便會馬上得到執行。
在Lisp中,這個過程叫做read-eval-print loop(REPL)。
有時候又被叫做top-level,top-level listener,Lisp listener。
舉個簡單的例子:
CL-USER> 10
10
第一個10是用戶輸入,第二個則是slime打印的值。
這個過程是這樣的:首先reader(即REPL里的R)讀取用戶輸入的10並創建一個表示這個值的Lisp對象。
然后evaluator(R)對其求值,得到10,(在Lisp中數值是自求值對象,意味着對它求值得到本身)。
最后這個值交給printer打印出來。
這個例子還不夠過癮的話,再來一個:
CL-USER>(+ 2 3)
5
這個又是怎么求值的呢?
其實在Lisp中,任何在括號中的東西都被看成是list。(不要忘了lisp名字的由來~),在上面的情況下,
是一個由+,2,3 這3個元素組成的list。通常情況下Lisp對list求值時將第一個元素看成是函數名,
余下的元素被看成是要被求值的函數參數。因此上面的過程就是:+意味着一個加法函數,2和3被讀取並求值,
然后傳遞給加法函數,返回5。最后5被傳遞給printer打印。
好吧。。。Lisp還有別的辦法對表達式求值,這里只是一個簡單的介紹。
----------------------------------------終於來到了Lisp的“Hello World”程序--------------------------------------------------------
沒有“Hello World”程序的編程書是不完整的,所以“Hello World”來了。
CL-USER>"hello, world"
"hello, world"
上面便是該程序的輸入和輸出。和數值一樣字符串也是自求值變量,所以上面的過程跟輸入10,打印10類似。
要注意的是:雙引號並不是字符串的一部分,它之所以在輸入輸出都出現是因為,輸入時它作為提示reader
讀取字符串的語法,輸出時則是因為printer會以reader能理解的同樣語法輸出。
這樣一講,似乎上面的例子不算是一個program,而是一個value,那就來真的吧。
CL-USER>(format t "hello, world")
hello, world
NIL
上面的例子中,format是一個函數,跟C語言的printf有點像,不要深究,倒是那個NIL需要注意下。
哪來的NIL呢?NIL是Lisp版本的false/NULL,在這里它是format表達式的返回值。只是這里format函數
的副作用比返回值更有用。C語言中其實也一樣的,printf不也有返回值嘛,只不過被丟棄了而已。
其實這也不算一個完整的程序,不過也快要達到我們的最終目的了,你已經有了“hello world”的所有部件,
現在所有要做的事情就是把他們打包進一個函數。(這正是Lisp的美妙之處,邊改進邊運行以達到你最終想要的效果)
切入正題,我們要的那個函數如下:
CL-USER>(defun hello-world () (format t "hello, world"))
HELLO-WORLD
這個程序就更有意思了,打印的值居然是HELLO-WORLD?那這個神秘的HELLO-WORLD是怎么來的呢?另外它還向我們初步
展示了怎樣去定義個函數。那么就看看究竟發生了什么!
先看函數定義,跟其它語言很類似,hello-world是函數名,括號里是參數(這里不需要參數),在后面是函數體;
然后到了神秘的HELLO-WORLD,它跟我們的函數名不是一樣的嗎,換個大寫而已嘛!
其實,HELLO-WORLD就是我們定義的函數。當我們輸入上面的表達式時,名為HELLO-WORLD的函數被創建了,然后表達式返回
了這個函數的名字,和上面的format表達式一樣,這個表達式的副作用也比返回值要有用的多-創建了HELLO-WORLD,所以
接下來就可以直接調用這個函數了
CL-USER>(hello-world)
hello, world
NIL
它和我們直接輸入format表達式的結果是一樣的,包括最后的NIL,因為Common Lisp中的函數總是返回最后一個被求值的表達式的值。
可能鑽牛角尖的人還是認為它不是一個“程序”,因為一旦退出了lisp,這個函數就消失了,再進入lisp之后自然就不可以用了,
這個問題很好解決,把你的函數定義寫入文件,然后從文件加載函數不就好了?至於如何在.lisp文件中定義函數,然后加載文件就
不詳細介紹了~