內容簡介
Lisp 語言歷史悠久,在電腦程式語言裡,年齡僅次於Fortran。二十世紀五十年代,計算機科學家先是發明了針對數字計算的 Fortran 語言,後來針對符號計算,由MIT 的John McCarthy於1960年開發出了Lisp (list processing)語言。該語言起初為表處理而設計,後來廣泛用於人工智慧。Lisp 程式中充滿了一對對嵌套的小括弧,這些嵌套的符號表達式體現了遞歸。遞歸是數學上的基本概念之一,從遞歸理論出發,一切可以計算的函式最終都可以劃歸為幾種基本的遞歸函式的種種組合。
1994年時眾多Lisp版本得到了相當的統一,統一之後的版本稱為Common LISP。Common Lisp帶有龐大的函式館,語言的規範長達千頁以上,包括面向對象的 CLOS。
Scheme語言的規範很短,總共只有50頁,甚至連Common Lisp 規範的索引的長度都不到,但是卻被稱為是現代程式語言王國的皇后。它與以前和以後的 Lisp 實現版本都存在一些差異。Scheme易學易用。
Scheme的一個主要特性是可以像運算元據一樣操作函式調用。Scheme 是 MIT 在70年代創造出來,目的之一是訓練人的計算思維,以其簡潔的語言環境和大量的腦力思考而著稱。
正由於lisp語言的歷史悠久,所以最初接觸Scheme的語法,可能不適應或者感到迷惑,但是這種語言自有它獨特的魅力。
語法定義
let表達式語法糖果
語法:(let ((var val) ...) exp1 exp2 ...)
說明:let表達式是 lambda 表達式的語法糖,即:(let ((var val) ...) exp1 exp2 ...),為 ((lambda (var ...) exp1 exp2) val ...)
示例:
(let ((x 2) (y 3)) (+ x y))
; 先綁定:x=2,y=3,再計算x+y的值,結果為5。注意 (x 2) 和 (y 3) 外還有一層括弧。
更多的示例:
(let ((f +))
(f 2 3)) ; return 5
(let ((f +) (x 2))
(f x 3)) ; return 5
(let ((f +) (x 2) (y 3))
(f x y)) ; return 5
用 define 綁定對象和 set! 賦值
語法:(define var exp)
(set! var exp)
說明:define綁定的對象在當前作用域中有效。define 和 set! 的區別是define既能賦值又能定義變數,而set!只能對已經定義的變數賦值。
示例:
(define a 1)
a ; return 1
(set! a 2)
a ; return 2
(let ((a 3)) a) ; return 3
a ; return 2
(let ((a 3)) (set! a 4) a) ; return 4
a ; return 2
(let ((a 3)) (define a 5) a) ; return 5
a ; return 2
(set! b 1) ; 錯誤,b尚未定義
lambda 表達式和函式定義
語法:(lambda (var ...) exp1 exp2 ...)
說明:lambda表達式用於定義函式。var ... 是參數,exp1 exp2 ...是函式的執行 部分。通常需要結合局部定義 let 或者全局定義表達式define,再進行函式調用。
示例:
((lambda (x) (+ x x)) (* 3 4)) ; return 24
說明:先用lambda定義了函式,參數是x,函式返回x+x。同時該語句也完成了函式調用,實參是 12 (等於3*4),因此返回值是 24 (等於12+12)。
在let表達式中定義函式。
Scheme語言中,函式作為一種數據類型,通過賦值語句,將lambda表達式賦值給相應的函式。
示例:
(let ((double (lambda (x) (+ x x))))
(list (double (* 3 4))
(double (/ 99 11))
(double (- 2 7)))) ; return (24 18 -10)
說明:let表達式將lambda定義的函式賦值給double,參數是x,返回 x+x。接下來分別三次調用 double 函式,並將結果以列表形式返回。list表達式負責生成列表。
用define全局定義表達式來定義函式。
用 let 定義的函式只能在 let表達式中有效,如果想定義在整個程式中有效的函式定義,需要用到全局定義表達式——define。
示例:
(define double (lambda (x) (+ x x)))
(double 12) ; return 24
(double (* 3 4)) ; return 24
說明:define表達式定義了全局有效的函式 double。兩次調用double的返回值都是 24。
定義函式的簡寫
用 define 定義的函式的語法可以簡化,即將 lambda 去掉。即將語法
(define var0
(lambda (var1 ... varn)
e1 e2 ...))
簡寫為:
(define (var0 var1 ... varn)
e1 e2 ...)
示例:
(define (double x) (+ x x))
(double 12) ; return 24
(double (* 3 4)) ; return 24
說明:本例是前一個例子的簡化版本,更簡潔明了。
特點
括弧嵌套
Lisp 程式中充滿了一對對嵌套的小括弧,這些嵌套的符號體現了最基本的數學思想——遞歸。
語法簡潔
Scheme語言的規範很短,總共只有50頁。
函式程式語言
一個函式(Function)是這個程式語言中所謂的第一等的公民。也就是說函式可以像一個 int 或者 float 一樣被很方便的傳遞來傳遞去。這也就是所謂“Functional程式語言”中,Functional 一詞的由來。
自動記憶體管理
支持尾遞歸
提高了遞歸效率
continuation
可移植性好
Scheme開發的程式有很好的可移植性,這是由於Scheme在不同的計算機平台有相應的解釋器和編譯器。
腳本語言
由於scheme語法簡潔,一個Scheme解釋器可以非常的小巧。Scheme可以作為腳本語言而內嵌於一些工具之中。
數據結構
數字
下面都是合法的數字表示方法:47,1/3,2.3,4.3e14,1+3i。
字元
字元前面需要用#\做前綴。如下面都是合法字元:
#\a #\A #\b #\B #\space #\newline
字元串
由雙引號括起來的字元組成字元串。如:"A little string"
布爾值
布爾值True和False分別用 #t 和 #f 表示。
列表
用圓括弧括起來的,可以包含任何數據類型的稱為列表。如:(a little (list of) (lists))
數組(vector)
用#為前綴,如:#(1 2 "string" #\x 5)
函式(或稱為過程)
把函式作為一種數據類型,是包括Scheme語言在內的Lisp的特色。
符號
符號除了不能夠以數字開頭的任何字元可組成符號。如:Symbols: this-is-a-symbol foo a32 c$23*4&7+3-is-a-symbol-too!
注釋 分號開始一行注釋。 如:
(+ 3 1) ;return 4
表達式
常量表達式返回本身的值。如:
⒊14 ; 返回 3.14
#t ; 返回布爾值 #t
#\c ; 返回字元 #\c
"Hi!" ; 返回字元串 "Hi!"
引用
語法:(quote obj) 或者簡寫為 'obj
(+ 2 3) ; 返回 5
'(+ 2 3) ; 返回列表 (+ 2 3)
(quote (+ 2 3)) ; 返回列表 (+ 2 3)
記法
Scheme的表達式的寫法有些特別,表達式用括弧括起來。括弧裡面的第一個出現的是函式名或者操作符,其它是參數。Scheme的這種表達式寫法可以叫做前置式。下面是一些Scheme的表達式的例子以及其對應的C語言的寫法。
Scheme | C |
(+ 2 3 4) | (2 + 3 + 4) |
(< low x high) | ((low < x) && (x < high)) |
(+ (* 2 3) (* 4 5)) | ((2 * 3) + (4 * 5)) |
(f x y) | f(x,y) |
(define (sq x) (* x x)) | int sq(int x) { return (x * x); } |
順序計算
語法:(begin exp1 exp2 ...)
說明:順序執行表達式 exp1,exp2,...,返回最後一個表達式的結果
示例:
(define x 3)
(begin
(set! x (+ x 1))
(+ x x)) ; 返回結果 8
說明:begin表達式,依次先用set!表達式為x賦值為4,在運算x+x,返回結果8。
主要的Scheme環境有:Racket、MIT Scheme、Scheme48、Chez Scheme