Scheme
語言是LISP語言的一個(gè)方言(或說成變種),它誕生于1975年的MIT,對于這個(gè)有近三十年歷史的編程語言來說,它并沒有象C++,java,C#那
樣受到商業(yè)領(lǐng)域的青睞,在國內(nèi)更是顯為人知。但它在國外的計(jì)算機(jī)教育領(lǐng)域內(nèi)卻是有著廣泛應(yīng)用的,有很多人學(xué)的第一門計(jì)算機(jī)語言就是Scheme語言。
作為Lisp 變體,Scheme 是一門非常簡潔的計(jì)算語言,使用它的編程人員可以擺脫語言本身的復(fù)雜性,把注意力集中到更重要的問題上,從而使語言真正成為解決問題的工具。本文分為上、 下兩部分來介紹 scheme 語言。
一.Scheme語言的特點(diǎn)
Scheme
語言是LISP語言的一個(gè)方言(或說成變種),它誕生于1975年的MIT,對于這個(gè)有近三十年歷史的編程語言來說,它并沒有象C++,java,C#那
樣受到商業(yè)領(lǐng)域的青睞,在國內(nèi)更是顯為人知。但它在國外的計(jì)算機(jī)教育領(lǐng)域內(nèi)卻是有著廣泛應(yīng)用的,有很多人學(xué)的第一門計(jì)算機(jī)語言就是Scheme語言。
它是一個(gè)小巧而又強(qiáng)大的語言,作為一個(gè)多用途的編程語言,它可以作為腳本語言使用,也可以作為應(yīng)用軟件的擴(kuò)展語言來使用,它具有元語言特性,還有很多獨(dú)到的特色,以致于它被稱為編程語言中的"皇后"。
下面是洪峰對Scheme語言的編程特色的歸納:
- 詞法定界(Lexical Scoping)
- 動(dòng)態(tài)類型(Dynamic Typing)
- 良好的可擴(kuò)展性
- 尾遞歸(Tail Recursive)
- 函數(shù)可以作為值返回
- 支持一流的計(jì)算連續(xù)
- 傳值調(diào)用(passing-by-value)
- 算術(shù)運(yùn)算相對獨(dú)立
本文的目的是讓有編程基礎(chǔ)(那怕是一點(diǎn)點(diǎn))的朋友能盡快的掌握Scheme語言的語法規(guī)則,如果您在讀完本文后,發(fā)現(xiàn)自己已經(jīng)會(huì)用Scheme語言了,那么我的目的就達(dá)到了。
二.Scheme語言的標(biāo)準(zhǔn)與實(shí)現(xiàn)
R5RS (Revised(5) Report on the Algorithmic Language Scheme)
Scheme語言的語法規(guī)則的第5次修正稿,1998年制定,即Scheme語言的現(xiàn)行標(biāo)準(zhǔn),目前大多數(shù)Scheme語言的實(shí)現(xiàn)都將達(dá)到或遵循此標(biāo)準(zhǔn),并且?guī)缀醵技尤肓艘恍儆谧约旱臄U(kuò)展特色。
Guile (GNU's extension language)
Guile是GNU工程的一個(gè)項(xiàng)目,它是GNU擴(kuò)展語言庫,它也是Scheme語言的一個(gè)具體實(shí)現(xiàn);如果你將它作為一個(gè)庫打包,可以把它鏈接到你的應(yīng)用程序中去,使你的應(yīng)用程序具有自己的腳本語言,這個(gè)腳本語言目前就是Scheme語言。
Guile可以在LINUX和一些UNIX系統(tǒng)上運(yùn)行,下面是簡單的安裝過程:
下載guile-1.6.4版,文件名為guile-1.6.4.tar.gz,執(zhí)行下面的命令:
tar xvfz guile-1.6.4.tar.gz
cd guile-1.6.4
./configure
make
make install
如此,即可以執(zhí)行命令guile,進(jìn)入guile>提示符狀態(tài),輸入調(diào)試Scheme程序代碼了,本文的所有代碼都是在guile下調(diào)試通過。
其它實(shí)現(xiàn)
除了Guile外,Scheme語言的實(shí)現(xiàn)還有很多,如:GNU/MIT-Scheme,
SCI,Scheme48,DrScheme等,它們大多是開源的,可以自由下載安裝使用,并且跨平臺的實(shí)現(xiàn)也很多。你會(huì)發(fā)現(xiàn)既有象basic的
Scheme語言解釋器,也有將Scheme語言編譯成C語言的編譯器,也有象JAVA那樣將Scheme語言代碼編譯成虛擬機(jī)代碼的編譯器。
三.基本概念
注釋
Scheme語言中的注釋是單行注釋,以分號[;]開始一直到行尾結(jié)束,其中間的內(nèi)容為注釋,在程序運(yùn)行時(shí)不做處理,如:
; this is a scheme comment line.
標(biāo)準(zhǔn)的Scheme語言定義中沒有多行注釋,不過在它的實(shí)現(xiàn)中幾乎都有。在Guile中就有多行注釋,以符號組合"#!"開始,以相反的另一符號組合"!#"結(jié)束,其中內(nèi)容為注釋,如:
#!
there are scheme comment area.
you can write mulity lines here .
!#
注意的是,符號組合"#!"和"!#"一定分做兩行來寫。
Scheme用做腳本語言
Scheme語言可以象sh,perl,python等語言那樣作為一種腳本語言來使用,用它來編寫可執(zhí)行腳本,在Linux中如果通過Guile用Scheme語言寫可執(zhí)行腳本,它的第一行和第二行一般是類似下面的內(nèi)容:
#! /usr/local/bin/guile -s
!#
這樣的話代碼在運(yùn)行時(shí)會(huì)自動(dòng)調(diào)用Guile來解釋執(zhí)行,標(biāo)準(zhǔn)的文件尾綴是".scm"。
塊(form)
塊(form)是Scheme語言中的最小程序單元,一個(gè)Scheme語言程序是由一個(gè)或多個(gè)form構(gòu)成。沒有特殊說明的情況下 form 都由小括號括起來,形如:
(define x 123)
(+ 1 2)
(* 4 5 6)
(display "hello world")
一個(gè) form 也可以是一個(gè)表達(dá)式,一個(gè)變量定義,也可以是一個(gè)過程。
form嵌套
Scheme語言中允許form的嵌套,這使它可以輕松的實(shí)現(xiàn)復(fù)雜的表達(dá)式,同時(shí)也是一種非常有自己特色的表達(dá)式。下圖示意了嵌套的稍復(fù)雜一點(diǎn)的表達(dá)式的運(yùn)算過程:
變量定義
可以用define來定義一個(gè)變量,形式如下:
(define 變量名 值)
如: (define x 123) ,定義一個(gè)變量x,其值為123。
更改變量的值
可以用set!來改變變量的值,格式如下:
(set! 變量名 值)
如: (set! x "hello") ,將變量x的值改為"hello" 。
Scheme語言是一種高級語言,和很多高級語言(如python,perl)一樣,它的變量類型不是固定的,可以隨時(shí)改變。
四.?dāng)?shù)據(jù)類型
1. 簡單數(shù)據(jù)類型
邏輯型(boolean)
最基本的數(shù)據(jù)類型,也是很多計(jì)算機(jī)語言中都支持的最簡單的數(shù)據(jù)類型,只能取兩個(gè)值:#t,相當(dāng)于其它計(jì)算機(jī)語言中的 TRUE;#f,相當(dāng)于其它計(jì)算機(jī)語言中的 FALSE。
Scheme語言中的boolean類型只有一種操作:not。其意為取相反的值,即:
(not #f) => #t
(not #t) => #f
not的引用,與邏輯非運(yùn)算操作類似
guile> (not 1)
#f
guile> (not (list 1 2 3))
#f
guile> (not 'a)
#f
從上面的操作中可以看出來,只要not后面的參數(shù)不是邏輯型,其返回值均為#f。
數(shù)字型(number)
它又分為四種子類型:整型(integer),有理數(shù)型(rational),實(shí)型(real),復(fù)數(shù)型(complex);它們又被統(tǒng)一稱為數(shù)字類型(number)。
如:復(fù)數(shù)型(complex) 可以定義為 (define c 3+2i)
實(shí)數(shù)型(real)可以定義為 (define f 22/7)
有理數(shù)型(rational)可以定義為 (define p 3.1415)
整數(shù)型(integer) 可以定義為 (define i 123)
Scheme 語言中,數(shù)字類型的數(shù)據(jù)還可以按照進(jìn)制分類,即二進(jìn)制,八進(jìn)制,十進(jìn)制和十六進(jìn)制,在外觀形式上它們分別以符號組合 #b、
#o、 #d、 #x 來作為表示數(shù)字進(jìn)制類型的前綴,其中表示十進(jìn)制的#d可以省略不寫,如:二進(jìn)制的 #b1010 ,八進(jìn)制的
#o567,十進(jìn)制的123或 #d123,十六進(jìn)制的 #x1afc 。
Scheme語言的這種嚴(yán)格按照數(shù)學(xué)定理來為數(shù)字類型進(jìn)行分類的方法可以看出Scheme語言里面滲透著很深的數(shù)學(xué)思想,Scheme語言是由數(shù)學(xué)家們創(chuàng)造出來的,在這方面表現(xiàn)得也比較鮮明。
字符型(char)
Scheme語言中的字符型數(shù)據(jù)均以符號組合 "#\" 開始,表示單個(gè)字符,可以是字母、數(shù)字或"[ ! $ % & * + - . / : < = > ? @ ^ _ ~ ]"等等其它字符,如:
#\A 表示大寫字母A,#\0表示字符0,
其中特殊字符有:#\space 表示空格符和 #\newline 表示換行符。
符號型(symbol)
符號類型是Scheme語言中有多種用途的符號名稱,它可以是單詞,用括號括起來的多個(gè)單詞,也可以是無意義的字母組合或符號組合,它在某種意義上可以理解為C中的枚舉類型。看下面的操作:
guile> (define a (quote xyz)) ; 定義變量a為符號類型,值為xyz
guile> a
xyz
guile> (define xyz 'a) ; 定義變量xyz為符號類型,值為a
guile> xyz
a
此處也說明單引號' 與quote是等價(jià)的,并且更簡單一些。符號類型與字符串不同的是符號類型不能象字符串那樣可以取得長度或改變其中某一成員字符的值,但二者之間可以互相轉(zhuǎn)換。
2. 復(fù)合數(shù)據(jù)類型
可以說復(fù)合數(shù)據(jù)類型是由基本的簡單數(shù)據(jù)類型通過某種方式加以組合形成的數(shù)據(jù)類型,特點(diǎn)是可以容納多種或多個(gè)單一的簡單數(shù)據(jù)類型的數(shù)據(jù),多數(shù)是基于某一種數(shù)學(xué)模型創(chuàng)建的。
字符串(string) 由多個(gè)字符組成的數(shù)據(jù)類型,可以直接寫成由雙引號括起的內(nèi)容,如:"hello" 。下面是Guile中的字符串定義和相關(guān)操作:
guile> (define name "tomson")
guile> name
"tomson"
guile> (string-length name) ; 取字符串的長度
6
guile> (string-set! name 0 #\g) ; 更改字符串首字母(第0個(gè)字符)為小寫字母g (#\g)
guile> name
"gomson"
guile> (string-ref name 3) ; 取得字符串左側(cè)第3個(gè)字符(從0開始)
#\s
字符串還可以用下面的形式定義:
guile> (define other (string #\h #\e #\l #\l #\o ))
guile> other
"hello"
字符串中出現(xiàn)引號時(shí)用反斜線加引號代替,如:"abc\"def" 。
點(diǎn)對(pair)
我把它譯成"點(diǎn)對",它是一種非常有趣的類型,也是一些其它類型的基礎(chǔ)類型,它是由一個(gè)點(diǎn)和被它分隔開的兩個(gè)所值組成的。形如: (1 . 2) 或 (a . b) ,注意的是點(diǎn)的兩邊有空格。
這是最簡單的復(fù)合數(shù)據(jù)類型,同是它也是其它復(fù)合數(shù)據(jù)類型的基礎(chǔ)類型,如列表類型(list)就是由它來實(shí)現(xiàn)的。
按照Scheme語言說明中的慣例,以下我們用符號組合 "=>" 來表示表達(dá)式的值。
它用cons來定義,如: (cons 8 9) =>(8 . 9)
其中在點(diǎn)前面的值被稱為 car ,在點(diǎn)后面的值被稱為 cdr ,car和cdr同時(shí)又成為取pair的這兩個(gè)值的過程,如:
(define p (cons 4 5)) => (4 . 5)
(car p) => 4
(cdr p) => 5
還可以用set-car! 和 set-cdr! 來分別設(shè)定這兩個(gè)值:
(set-car! p "hello")
(set-cdr! p "good")
如此,以前定義的 p 又變成了 ("hello" . "good") 這個(gè)樣子了。
列表(list)
列表是由多個(gè)相同或不同的數(shù)據(jù)連續(xù)組成的數(shù)據(jù)類型,它是編程中最常用的復(fù)合數(shù)據(jù)類型之一,很多過程操作都與它相關(guān)。下面是在Guile中列表的定義和相關(guān)操作:
guile> (define la (list 1 2 3 4 ))
guile> la
(1 2 3 4)
guile> (length la) ; 取得列表的長度
4
guile> (list-ref la 3) ; 取得列表第3項(xiàng)的值(從0開始)
4
guile> (list-set! la 2 99) ; 設(shè)定列表第2項(xiàng)的值為99
99
guile> la
(1 2 99 4)
guile> (define y (make-list 5 6)) ;創(chuàng)建列表
guile> y
(6 6 6 6 6)
make-list用來創(chuàng)建列表,第一個(gè)參數(shù)是列表的長度,第二個(gè)參數(shù)是列表中添充的內(nèi)容;還可以實(shí)現(xiàn)多重列表,即列表的元素也是列表,如:(list (list 1 2 3) (list 4 5 6))。
列表與pair的關(guān)系
回過頭來,我們再看看下面的定義:
guile> (define a (cons 1 (cons 2 (cons 3 '()))))
guile> a
(1 2 3)
由上可見,a本來是我們上面定義的點(diǎn)對,最后形成的卻是列表。事實(shí)上列表是在點(diǎn)對的基礎(chǔ)上形成的一種特殊格式。
再看下面的代碼:
guile> (define ls (list 1 2 3 4))
guile> ls
(1 2 3 4)
guile> (list? ls)
#t
guile> (pair? ls)
#t
由此可見,list是pair的子類型,list一定是一個(gè)pair,而pair不是list。
guile> (car ls)
1
guile> (cdr ls)
(2 3 4)
其cdr又是一個(gè)列表,可見用于pair的操作過程大多可以用于list。
guile> (cadr ls) ; 此"點(diǎn)對"對象的cdr的car
2
guile> (cddr ls) ; 此"點(diǎn)對"對象的cdr的cdr
(3 4)
guile> (caddr ls) ; 此"點(diǎn)對"對象的cdr的cdr的car
3
guile> (cdddr ls) ; 此"點(diǎn)對"對象的cdr的cdr的cdr
(4)
上在的操作中用到的cadr,cdddr等過程是專門對PAIR型數(shù)據(jù)再復(fù)合形成的數(shù)據(jù)操作的過程,最多可以支持在中間加四位a或d,如cdddr,caaddr等。
下圖表示了由pairs定義形成的列表:
這個(gè)列表可以由pair定義為如下形式:
(define x (cons 'a (cons 'b (cons 'c (cons 'd '())))))
而列表的實(shí)際內(nèi)容則為:(a b c d)
由pair類型還可以看出它可以輕松的表示樹型結(jié)構(gòu),尤其是標(biāo)準(zhǔn)的二叉樹。
向量(vector)
可以說是一個(gè)非常好用的類型 ,是一種元素按整數(shù)來索引的對象,異源的數(shù)據(jù)結(jié)構(gòu),在占用空間上比同樣元素的列表要少,在外觀上:
列表示為: (1 2 3 4)
VECTOR表示為: #(1 2 3 4)
可以正常定義:(define v (vector 3 4 5))
也可以直接定義:(define v #(3 4 5))
vector是一種比較常用的復(fù)合類型,它的元素索引從0開始,至第 n-1 結(jié)束,這一點(diǎn)有點(diǎn)類似C語言中的數(shù)組。
關(guān)于向量表(vector)的常用操作過程:
guile> (define v (vector 1 2 3 4 5))
guile> v
#(1 2 3 4 5)
guile> (vector-ref v 0) ; 求第n個(gè)變量的值
1
guile> (vector-length v) ; 求vector的長度
5
guile> (vector-set! v 2 "abc") ; 設(shè)定vector第n個(gè)元素的值
guile> v
#(1 2 "abc" 4 5)
guile> (define x (make-vector 5 6)) ; 創(chuàng)建向量表
guile> x
#(6 6 6 6 6)
make-vector用來創(chuàng)建一個(gè)向量表,第一個(gè)參數(shù)是數(shù)量,后一個(gè)參數(shù)是添充的值,這和列表中的make-list非常相似。
我們可以看出,在Scheme語言中,每種數(shù)據(jù)類型都有一些基本的和它相關(guān)的操作過程,如字符串,列表等相關(guān)的操作,這些操作過程都很有規(guī)律,過程名的單詞之間都用-號隔開,很容易理解。對于學(xué)過C++的朋友來說,更類似于某個(gè)對象的方法,只不過表現(xiàn)的形式不同了。
3. 類型的判斷、比較、運(yùn)算、轉(zhuǎn)換與方法
類型判斷
Scheme語言中所有判斷都是用類型名加問號再加相應(yīng)的常量或變量構(gòu)成,形如:
(類型? 變量)
Scheme語言在類型定義中有比較嚴(yán)格的界定,如在C語言等一些語言中數(shù)字0來代替邏輯類型數(shù)據(jù)False,在Scheme語言中是不允許的。
以下為常見的類型判斷和附加說明:
邏輯型:
(boolean? #t) => #t
(boolean? #f) => #t 因?yàn)?t和#f都是boolean類型,所以其值為#t
(boolean? 2) => #f 因?yàn)?是數(shù)字類型,所以其值為 #f
字符型:
(char? #\space) => #t
(char? #\newline) => #t 以上兩個(gè)特殊字符:空格和換行
(char? #\f) => #t 小寫字母 f
(char? #\;) => #t 分號 ;
(char? #\5) => #t 字符 5 ,以上這些都是正確的,所以返回值都是 #t
(char? 5) => #f 這是數(shù)字 5 ,不是字符類型,所以返回 #f
數(shù)字型:
(integer? 1) => #t
(integer? 2345) => #t
(integer? -90) => #t 以上三個(gè)數(shù)均為整數(shù)
(integer? 8.9) => #f 8.9不整數(shù)
(rational? 22/7) => #t
(rational? 2.3) => #t
(real? 1.2) => #t
(real? 3.14159) => #t
(real? -198.34) => #t 以上三個(gè)數(shù)均為實(shí)數(shù)型
(real? 23) => #t 因?yàn)檎蛯儆趯?shí)型
(number? 5) => #t
(number? 2.345) => #t
(number? 22/7) => #t
其它型:
(null? '()) => #t ; null意為空類型,它表示為 '() ,即括號里什么都沒有的符號
(null? 5) => #f
(define x 123) 定義變量x其值為123
(symbol? x) => #f
(symbol? 'x) => #t ; 此時(shí) 'x 為符號x,并不表示變量x的值
在Scheme語言中如此眾多的類型判斷功能,使得Scheme語言有著非常好的自省功能。即在判斷過程的參數(shù)是否附合過程的要求。
比較運(yùn)算
Scheme語言中可以用<,>,<=,>=,= 來判斷數(shù)字類型值或表達(dá)式的關(guān)系,如判斷變量x是否等于零,它的形式是這樣的:(= x 0) ,如x的值為0則表達(dá)式的值為#t,否則為#f。
還有下面的操作:
(eqv? 34 34) => #t
(= 34 34) => #t
以上兩個(gè)form功能相同,說明 eqv? 也可以用于數(shù)字的判斷。
在Scheme語言中有三種相等的定義,兩個(gè)變量正好是同一個(gè)對象;兩個(gè)對象具有相同的值;兩個(gè)對象具有相同的結(jié)構(gòu)并且結(jié)構(gòu)中的內(nèi)容相同。除了上面提到的符號判斷過程和eqv?外,還有eq?和equal?也是判斷是否相等的過程。
eq?,eqv?,equal?
eq?,eqv?和equal?是三個(gè)判斷兩個(gè)參數(shù)是否相等的過程,其中eq?和eqv?的功能基本是相同的,只在不同的Scheme語言中表現(xiàn)不一樣。
eq?是判斷兩個(gè)參數(shù)是否指向同一個(gè)對象,如果是才返回#t;equal?則是判斷兩個(gè)對象是否具有相同的結(jié)構(gòu)并且結(jié)構(gòu)中的內(nèi)容是否相同,它用eq?來比較結(jié)構(gòu)中成員的數(shù)量;equal?多用來判斷點(diǎn)對,列表,向量表,字符串等復(fù)合結(jié)構(gòu)數(shù)據(jù)類型。
guile> (define v (vector 3 4 5))
guile> (define w #(3 4 5)) ; w和v都是vector類型,具有相同的值#(3 4 5)
guile> (eq? v w)
#f ; 此時(shí)w和v是兩個(gè)對象
guile> (equal? v w)
#t ; 符合equal?的判斷要求
以上操作說明了eq? 和equal? 的不同之處,下面的操作更是證明了這一點(diǎn):
guile> (define x (make-vector 5 6))
guile> x
#(6 6 6 6 6)
guile> (eq? x x) ; 是同一個(gè)對象,所以返回#t
#t
guile> (define z (make-vector 5 6))
guile> z
#(6 6 6 6 6)
guile> (eq? x z) ; 不是同一個(gè)對象
#f
guile> (equal? x z) ; 結(jié)構(gòu)相同,內(nèi)容相同,所以返回#t
#t
算術(shù)運(yùn)算
Scheme語言中的運(yùn)算符有:
+ , - , * , / 和 expt (指數(shù)運(yùn)算)
其中 - 和 / 還可以用于單目運(yùn)算,如:
(- 4) => -4
(/ 4) => 1/4
此外還有許多擴(kuò)展的庫提供了很多有用的過程,
max 求最大 (max 8 89 90 213) => 213
min 求最小 (min 3 4 5 6 7) => 3
abs 求絕對值 (abs -7) ==> 7
除了max,min,abs外,還有很多數(shù)學(xué)運(yùn)算過程,這要根據(jù)你用的Scheme語言的運(yùn)行環(huán)境有關(guān),不過它們大多是相同的。在R5RS中規(guī)定了很多運(yùn)算過程,在R5RS的參考資料中可以很容易找到。
轉(zhuǎn)換
Scheme語言中用符號組合"->"來標(biāo)明類型間的轉(zhuǎn)換(很象C語言中的指針)的過程,就象用問號來標(biāo)明類型判斷過程一樣。下面是一些常見的類型轉(zhuǎn)換過程:
guile> (number->string 123) ; 數(shù)字轉(zhuǎn)換為字符串
"123"
guile> (string->number "456") ; 字符串轉(zhuǎn)換為數(shù)字
456
guile> (char->integer #\a) ;字符轉(zhuǎn)換為整型數(shù),小寫字母a的ASCII碼值為96
97
guile> (char->integer #\A) ;大寫字母A的值為65
65
guile> (integer->char 97) ;整型數(shù)轉(zhuǎn)換為字符
#\a
guile> (string->list "hello") ;字符串轉(zhuǎn)換為列表
(#\h #\e #\l #\l #\o)
guile> (list->string (make-list 4 #\a)) ; 列表轉(zhuǎn)換為字符串
"aaaa"
guile> (string->symbol "good") ;字符串轉(zhuǎn)換為符號類型
good
guile> (symbol->string 'better) ;符號類型轉(zhuǎn)換為字符串
"better"
五.過程定義
過程(Procedure)
在Scheme語言中,過程相當(dāng)于C語言中的函數(shù),不同的是Scheme語言過程是一種數(shù)據(jù)類型,這也是為什么Scheme語言將程序和數(shù)據(jù)作為同一對象處理的原因。如果我們在Guile提示符下輸入加號然后回車,會(huì)出現(xiàn)下面的情況:
guile> +
#<primitive-procedure +>
這告訴我們"+"是一個(gè)過程,而且是一個(gè)原始的過程,即Scheme語言中最基礎(chǔ)的過程,在GUILE中內(nèi)部已經(jīng)實(shí)現(xiàn)的過程,這和類型判斷一樣,如
boolean?等,它們都是Scheme語言中最基本的定義。注意:不同的Scheme語言實(shí)現(xiàn)環(huán)境,出現(xiàn)的提示信息可能不盡相同,但意義是一樣的。
define不僅可以定義變量,還可以定義過程,因在Scheme語言中過程(或函數(shù))都是一種數(shù)據(jù)類型,所以都可以通過define來定義。不同的是標(biāo)準(zhǔn)的過程定義要使用lambda這一關(guān)鍵字來標(biāo)識。
Lambda關(guān)鍵字
Scheme語言中可以用lambda來定義過程,其格式如下:
(define 過程名 ( lambda (參數(shù) ...) (操作過程 ...)))
我們可以自定義一個(gè)簡單的過程,如下:
- ::
- (define add5 (lambda (x) (+ x 5)))
此過程需要一個(gè)參數(shù),其功能為返回此參數(shù)加5 的值,如:
(add5 11) => 16
下面是簡單的求平方過程square的定義:
(define square (lambda (x) (* x x)))
與lambda相同的另一種方式
在Scheme語言中,也可以不用lambda,而直接用define來定義過程,它的格式為:
(define (過程名 參數(shù)) (過程內(nèi)容 …))
如下面操作:
(define (add6 x) (+ x 6))
add6
#<procedure: add6 (x)> 說明add6是一個(gè)過程,它有一個(gè)參數(shù)x
(add6 23) => 29
再看下面的操作:
guile> (define fun
(lambda(proc x y)
(proc x y)))
guile> fun
#<procedure fun (proc x y)>
guile> (fun * 5 6)
30
guile> (fun / 30 3)
10
更多的過程定義
上面定義的過程fun有三個(gè)參數(shù),其中第一個(gè)參數(shù)proc也是一個(gè)操作過程(因?yàn)樵赟cheme語言中過程也是一種數(shù)據(jù),可以作為過程的參數(shù)),另外兩個(gè)參數(shù)是數(shù)值,所以會(huì)出現(xiàn)上面的調(diào)用結(jié)果。
guile> (define add
(lambda (x y)
(+ x y)))
guile> add
#<procedure add (x y)>
guile> (fun add 100 200)
300
繼續(xù)上面操作,我們定義一個(gè)過程add,將add作為參數(shù)傳遞給fun過程,得出和(fun + 100 200)相同的結(jié)果。
guile> ((lambda (x) (+ x x)) 5)
10
上面的 (lambda(x) (+ x x)) 事實(shí)上是簡單的過程定義,在后面直接加上操作參數(shù)5,得出結(jié)果10,這樣實(shí)現(xiàn)了匿名過程,直接用過程定義來操作參數(shù),得出運(yùn)算結(jié)果。
通過上面的操作,相信你已初步了解了過程的用法。既然過程是一種數(shù)據(jù)類型,所以將過程作為過程的參數(shù)是完全可以的。以下過程為判斷參數(shù)是否為過程,給出一個(gè)參數(shù),用 procedure? 來判斷參數(shù)是否為過程,采用if結(jié)構(gòu)(關(guān)于if結(jié)構(gòu)見下面的介紹):
guile> (define isp
(lambda (x)
(if (procedure? x) 'isaprocedure 'notaprocedure)))
guile> isp
#<procedure isp (x)>
guile> (isp 0)
notaprocedure
guile> (isp +)
isaprocedure
上面的過程就體現(xiàn)了Scheme語言的參數(shù)自省(辨別)能力,'0'是數(shù)字型,所以返回notaprocedure;而'+'是一個(gè)最基礎(chǔ)的操作過程,所以返回isaprocedure。
過程的嵌套定義
在Scheme語言中,過程定義也可以嵌套,一般情況下,過程的內(nèi)部過程定義只有在過程內(nèi)部才有效,相當(dāng)C語言中的局部變量。
如下面的代碼的最終結(jié)果是50:
(define fix
(lambda (x y z)
(define add
(lambda (a b) (+ a b)))
(- x (add y z))))
(display (fix 100 20 30))
此時(shí)過程add只在fix過程內(nèi)部起做用,這事實(shí)上涉及了過程和變量的綁定,可以參考下面的關(guān)于過程綁定(let,let* 和letrec)的介紹。
過程是初學(xué)者難理解的一個(gè)關(guān)鍵,隨著過程參數(shù)的增加和功能的增強(qiáng),過程的內(nèi)容變得越來越復(fù)雜,小括號也會(huì)更多,如果不寫出清晰的代碼的話,讀代碼也會(huì)成為一個(gè)難題。
熟悉了 scheme 基本概念、數(shù)據(jù)類型和過程(函數(shù))后, 下一部分我們來學(xué)習(xí) scheme 的結(jié)構(gòu)、遞歸調(diào)用和其他擴(kuò)展功能。
六.常用結(jié)構(gòu)
順序結(jié)構(gòu)
也可以說成由多個(gè)form組成的form,用begin來將多個(gè)form放在一對小括號內(nèi),最終形成一個(gè)form。格式為:(begin form1 form2 …)
如用Scheme語言寫成的經(jīng)典的helloworld程序是如下樣子的:
(begin
(display "Hello world!") ; 輸出"Hello world!"
(newline)) ; 換行
if結(jié)構(gòu)
Scheme語言的if結(jié)構(gòu)有兩種格式,一種格式為:(if 測試 過程1 過程2),即測試條件成立則執(zhí)行過程1,否則執(zhí)行過程2。例如下面代碼:
(if (= x 0)
(display "is zero")
(display "not zero"))
還有另一種格式:(if 測試 過程) ,即測試條件成立則執(zhí)行過程。例如下面代碼:
(if (< x 100) (display "lower than 100"))
根據(jù)類型判斷來實(shí)現(xiàn)自省功能,下面代碼判斷給定的參數(shù)是否為字符串:
(define fun
(lambda ( x )
(if (string? x)
(display "is a string")
(display "not a string"))))
如執(zhí)行 (fun 123) 則返回值為"not a string",這樣的功能在C++或JAVA中實(shí)現(xiàn)的話可能會(huì)很費(fèi)力氣。
cond結(jié)構(gòu)
Scheme語言中的cond結(jié)構(gòu)類似于C語言中的switch結(jié)構(gòu),cond的格式為:
(cond ((測試) 操作) … (else 操作))
如下是在Guile中的操作:
guile> (define w (lambda (x)
(cond ((< x 0) 'lower)
((> x 0) 'upper)
(else 'equal))))
guile> w
#<procedure w (x)>
guile> (w 9)
upper
guile> (w -8)
lower
guile> (w 0)
equal
上面程序代碼中,我們定義了過程w,它有一個(gè)參數(shù)x,如果x的值大于0,則返回符號upper,如x的值小于0則返回符號lower,如x 的值為0則返回符號equal。
下載已做成可執(zhí)行腳本的 例程。
cond可以用if形式來寫,上面的過程可以如下定義:
guile> (define ff
(lambda (x)
(if (< x 0) 'lower
(if (> x 0) 'upper 'zero))))
guile> ff
#<procedure ff (x)>
guile> (ff 9)
upper
guile> (ff -9)
lower
guile> (ff 0)
zero
這在功能上是和cond一樣的,可以看出cond實(shí)際上是實(shí)現(xiàn)了if的一種多重嵌套。
case結(jié)構(gòu)
case結(jié)構(gòu)和cond結(jié)構(gòu)有點(diǎn)類似,它的格式為:
(case (表達(dá)式) ((值) 操作)) ... (else 操作)))
case結(jié)構(gòu)中的值可以是復(fù)合類型數(shù)據(jù),如列表,向量表等,只要列表中含有表達(dá)式的這個(gè)結(jié)果,則進(jìn)行相應(yīng)的操作,如下面的代碼:
(case (* 2 3)
((2 3 5 7) 'prime)
((1 4 6 8 9) 'composite))
上面的例子返回結(jié)果是composite,因?yàn)榱斜?1 4 6 8 9)中含有表達(dá)式(* 2 3)的結(jié)果6;下面是在Guile中定義的func過程,用到了case結(jié)構(gòu):
guile> (define func
(lambda (x y)
(case (* x y)
((0) 'zero)
(else 'nozero))))
guile> func
#<procedure func (x y)>
guile> (func 2 3)
nozero
guile> (func 2 0)
zero
guile> (func 0 9)
zero
guile> (func 2 9)
nozero
可以下載另一個(gè)腳本文件 te.scm,參考一下。
and結(jié)構(gòu)
and結(jié)構(gòu)與邏輯與運(yùn)算操作類似,and后可以有多個(gè)參數(shù),只有它后面的參數(shù)的表達(dá)式的值都為#t時(shí),它的返回值才為#t,否則為#f。看下面的操作:
guile> (and (boolean? #f) (< 8 12))
#t
guile> (and (boolean? 2) (< 8 12))
#f
guile> (and (boolean? 2) (> 8 12))
#f
如果表達(dá)式的值都不是boolean型的話,返回最后一個(gè)表達(dá)式的值,如下面的操作:
guile> (and (list 1 2 3) (vector 'a 'b 'c))
#(a b c)
guile> (and 1 2 3 4 )
4
guile> (and 'e 'd 'c 'b 'a)
a
or結(jié)構(gòu)
or結(jié)構(gòu)與邏輯或運(yùn)算操作類似,or后可以有多個(gè)參數(shù),只要其中有一個(gè)參數(shù)的表達(dá)式值為#t,其結(jié)果就為#t,只有全為#f時(shí)其結(jié)果才為#f。如下面的操作:
guile> (or #f #t)
#t
guile> (or #f #f)
#f
guile> (or (rational? 22/7) (< 8 12))
#t
guile> (rational? 22/7)
#t
guile> (real? 22/7)
#t
guile> (or (real? 4+5i) (integer? 3.22))
#f
我們還可以用and和or結(jié)構(gòu)來實(shí)現(xiàn)較復(fù)雜的判斷表達(dá)式,如在C語言中的表達(dá)式:
((x > 100) && (y < 100)) 和 ((x > 100) || (y > 100))
在Scheme中可以表示為:
guile> (define x 123)
guile> (define y 80)
guile> (and (> x 100) (< y 100))
#t
guile> (or (> x 100) (> y 100))
#t
Scheme語言中只有if結(jié)構(gòu)是系統(tǒng)原始提供的,其它的cond,case,and,or,另外還有do,when,unless等都是可以用宏定義的方式來定義的,這一點(diǎn)充分體現(xiàn)了Scheme的元語言特性,關(guān)于do,when等結(jié)構(gòu)的使用可以參考R5RS。
七.遞歸調(diào)用
用遞歸實(shí)現(xiàn)階乘
在Scheme語言中,遞歸是一個(gè)非常重要的概念,可以編寫簡單的代碼很輕松的實(shí)現(xiàn)遞歸調(diào)用,如下面的階乘過程定義:
(define factoral (lambda (x)
(if (<= x 1) 1
(* x (factoral (- x 1))))))
我們可以將下面的調(diào)用(factoral 4),即4的階乘的運(yùn)算過程圖示如下:
以下為factoral過程在Guile中的運(yùn)行情況:
guile> (define factoral (lambda (x) (if (<= x 1) 1 (* x (factoral (- x 1))))))
guile> factoral
#<procedure factoral (x)>
guile> (factoral 4)
24
另一種遞歸方式
下面是一另一種遞歸方式的定義:
(define (factoral n)
(define (iter product counter)
(if (> counter n)
product
(iter (* counter product) (+ counter 1))))
(iter 1 1))
(display (factoral 4))
這個(gè)定義的功能和上面的完全相同,只是實(shí)現(xiàn)的方法不一樣了,我們在過程內(nèi)部實(shí)現(xiàn)了一個(gè)過程iter,它用counter參數(shù)來計(jì)數(shù),調(diào)用時(shí)從1開始累計(jì),這樣它的展開過程正好和我們上面的遞歸過程的從4到1相反,而是從1到4。
循環(huán)的實(shí)現(xiàn)
在Scheme語言中沒有循環(huán)結(jié)構(gòu),不過循環(huán)結(jié)構(gòu)可以用遞歸來很輕松的實(shí)現(xiàn)(在Scheme語言中只有通過遞歸才能實(shí)現(xiàn)循環(huán))。對于用慣了C語言循環(huán)的朋友,在Scheme中可以用遞歸簡單實(shí)現(xiàn):
guile> (define loop
(lambda(x y)
(if (<= x y)
(begin (display x) (display #\\space) (set! x (+ x 1))
(loop x y)))))
guile> loop
#<procedure loop (x y)>
guile> (loop 1 10)
1 2 3 4 5 6 7 8 9 10
這只是一種簡單的循環(huán)定義,過程有兩個(gè)參數(shù),第一個(gè)參數(shù)是循環(huán)的初始值,第二個(gè)參數(shù)是循環(huán)終止值,每次增加1。相信讀者朋友一定會(huì)寫出更漂亮更實(shí)用的循環(huán)操作來的。
八.變量和過程的綁定
let,let*,letrec
在多數(shù)編程語言中都有關(guān)于變量的存在的時(shí)限問題,Scheme語言中用let,let*和letrec來確定變量的存在的時(shí)限問題,即局部變量和全局變量,一般情況下,全局變量都用define來定義,并放在過程代碼的外部;而局部變量則用let等綁定到過程內(nèi)部使用。
用let可以將變量或過程綁定在過程的內(nèi)部,即實(shí)現(xiàn)局部變量:
guile> let
#<primitive-macro! let>
從上面的操作可以看出let是一個(gè)原始的宏,即guile內(nèi)部已經(jīng)實(shí)現(xiàn)的宏定義。
下面的代碼顯示了let的用法(注意多了一層括號):
guile> (let ((x 2) (y 5)) (* x y))
10
它的格式是:(let ((…)…) …),下面是稍復(fù)雜的用法:
guile> (let ((x 5))
(define foo (lambda (y) (bar x y)))
(define bar (lambda (a b) (+ (* a b) a)))
(foo (+ x 3)))
45
以上是Guile中的代碼實(shí)現(xiàn)情況。它的實(shí)現(xiàn)過程大致是:(foo 8) 展開后形成 (bar 5 8),再展開后形成 (+ (* 5 8) 5) ,最后其值為45。
再看下面的操作:
guile> (let ((iszero?
(lambda(x)
(if (= x 0) #t #f))))
(iszero? 9))
#f
guile> (iszero? 0) ;此時(shí)會(huì)顯示出錯(cuò)信息
let的綁定在過程內(nèi)有效,過程外則無效,這和上面提到的過程的嵌套定是一樣的,上面的iszero?過程在操作過程內(nèi)定義并使用的,操作結(jié)束后再另行引用則無效,顯示過程未定義出錯(cuò)信息。
下面操作演示了let*的用法:
guile> (let ((x 2) (y 5))
(let* ((x 6)(z (+ x y))) ;此時(shí)x的值已為6,所以z的值應(yīng)為11,如此最后的值為66
(* z x)))
66
還有l(wèi)etrec,看下面的操作過程:
guile> (letrec ((even?
(lambda(x)
(if (= x 0) #t
(odd? (- x 1)))))
(odd?
(lambda(x)
(if (= x 0) #f
(even? (- x 1))))))
(even? 88))
#t
上面的操作過程中,內(nèi)部定義了兩個(gè)判斷過程even?和odd?,這兩個(gè)過程是互相遞歸引用的,如果將letrec換成let或let*都會(huì)不正常,因?yàn)閘etrec是將內(nèi)部定義的過程或變量間進(jìn)行相互引用的。看下面的操作:
guile> (letrec ((countdown
(lambda (i)
(if (= i 0) 'listoff
(begin (display i) (display ",")
(countdown (- i 1)))))))
(countdown 10))
10,9,8,7,6,5,4,3,2,1,listoff
letrec幫助局部過程實(shí)現(xiàn)遞歸的操作,這不僅在letrec綁定的過程內(nèi),而且還包括所有初始化的東西,這使得在編寫較復(fù)雜的過程中經(jīng)常用到letrec,也成了理解它的一個(gè)難點(diǎn)。
apply
apply的功能是為數(shù)據(jù)賦予某一操作過程,它的第一個(gè)參數(shù)必需是一個(gè)過程,隨后的其它參數(shù)必需是列表,如:
guile> (apply + (list 2 3 4))
9
guile> (define sum
(lambda (x )
(apply + x))) ; 定義求和過程
guile> sum
#<procedure sum (x)>
guile> (define ls (list 2 3 4 5 6))
guile> ls
(2 3 4 5 6)
guile> (sum ls)
20
guile> (define avg
(lambda(x)
(/ (sum x) (length x)))) ; 定義求平均過程
guile> avg
#<procedure avg (x)>
guile> (avg ls)
4
以上定義了求和過程sum和求平均的過程avg,其中求和的過程sum中用到了apply來綁定"+"過程操作到列表,結(jié)果返回列表中所有數(shù)的總和。
map
map的功能和apply有些相似,它的第一個(gè)參數(shù)也必需是一個(gè)過程,隨后的參數(shù)必需是多個(gè)列表,返回的結(jié)果是此過程來操作列表后的值,如下面的操作:
guile> (map + (list 1 2 3) (list 4 5 6))
(5 7 9)
guile> (map car '((a . b)(c . d)(e . f)))
(a c e)
除了apply,map以外,Scheme語言中還有很多,諸如:eval,delay,for-each,force,call-with-
current-
continuation等過程綁定的操作定義,它們都無一例外的提供了相當(dāng)靈活的數(shù)據(jù)處理能力,也就是另初學(xué)者望而生畏的算法,當(dāng)你仔細(xì)的體會(huì)了運(yùn)算過
程中用到的簡直妙不可言的算法后,你就會(huì)發(fā)現(xiàn)Scheme語言設(shè)計(jì)者的思想是多么偉大。
九.輸入輸出
Scheme語言中也提供了相應(yīng)的輸入輸出功能,是在C基礎(chǔ)上的一種封裝。
端口
Scheme語言中輸入輸出中用到了端口的概念,相當(dāng)于C中的文件指針,也就是Linux中的設(shè)備文件,請看下面的操作:
guile> (current-input-port)
#<input: standard input /dev/pts/0> ;當(dāng)前的輸入端口
guile> (current-output-port)
#<output: standard output /dev/pts/0> ;當(dāng)前的輸出端口
判斷是否為輸入輸出端口,可以用下面兩個(gè)過程:input-port? 和output-port? ,其中input-port?用來判斷是否為輸入端口,output-port?用來判斷是否為輸出端口。
open-input-file,open-output-file,close-input-port,close-output-port這四個(gè)過程用來打開和關(guān)閉輸入輸出文件,其中打開文件的參數(shù)是文件名字符串,關(guān)閉文件的參數(shù)是打開的端口。
輸入
打開一個(gè)輸入文件后,返回的是輸入端口,可以用read過程來輸入文件的內(nèi)容:
guile> (define port (open-input-file "readme"))
guile> port
#<input: readme 4>
guile> (read port)
GUILE語言
上面的操作打開了readme文件,并讀出了它的第一行內(nèi)容。此外還可以直接用read過程來接收鍵盤輸入,如下面的操作:
guile> (read) ; 執(zhí)行后即等待鍵盤輸入
12345
12345
guile> (define x (read)) ; 等待鍵盤輸入并賦值給x
12345
guile> x
12345
以上為用read來讀取鍵入的數(shù)字,還可以輸入字符串等其它類型數(shù)據(jù):
guile> (define name (read))
tomson
guile> name
tomson
guile> (string? name)
#f
guile> (symbol? name)
#t
此時(shí)輸入的tomson是一個(gè)符號類型,因?yàn)樽址怯靡栆饋淼模猿霈F(xiàn)上面的情況。下面因?yàn)橛靡柫耍?string? str)返回值為#t 。
guile> (define str (read))
"Johnson"
guile> str
"Johnson"
guile> (string? str)
#t
還可以用load過程來直接調(diào)用Scheme語言源文件并執(zhí)行它,格式為:(load "filename"),還有read-char過程來讀單個(gè)字符等等。
輸出
常用的輸出過程是display,還有write,它的格式是:(write 對象 端口),這里的對象是指字符串等常量或變量,端口是指輸出端口或打開的文件。下面的操作過程演示了向輸出文件temp中寫入字符串"helloworld",并分行的實(shí)現(xiàn)。
[root@toymouse test]# guile
guile> (define port1 (open-output-file "temp")) ; 打開文件端口賦于port1
guile> port1
#<output: temp 3>
guile> (output-port? port1)
#t ; 此時(shí)證明port1為輸出端口
guile> (write "hello\\nworld" port1)
guile> (close-output-port port1)
guile> (exit) ; 寫入數(shù)據(jù)并關(guān)閉退出
[root@toymouse test]# more temp 顯示文件的內(nèi)容,達(dá)到測試目的
"hello
world"
在輸入輸出操作方面,還有很多相關(guān)操作,讀者可以參考R5RS的文檔。
十.語法擴(kuò)展
Scheme語言可以自己定義象cond,let等功能一樣的宏關(guān)鍵字。標(biāo)準(zhǔn)的Scheme語言定義中用define-syntax和syntax-rules來定義,它的格式如下:
(define-syntax 宏名
(syntax-rules()
((模板) 操作))
. . . ))
下面定義的宏start的功能和begin相同,可以用它來開始多個(gè)塊的組合:
(define-syntax start
(syntax-rules ()
((start exp1)
exp1)
((start exp1 exp2 ...)
(let ((temp exp1)) (start exp2 ...))) ))
這是一個(gè)比較簡單的宏定義,但對理解宏定義來說是比較重要的,理解了他你才會(huì)進(jìn)一步應(yīng)用宏定義。在規(guī)則 ((start exp1) exp1)
中,(start exp1) 是一個(gè)參數(shù)時(shí)的模板,exp1是如何處理,也就是原樣搬出,不做處理。這樣 (start form1) 和
(form1) 的功能就相同了。
在規(guī)則 ((start exp1 exp2 ...) (let ((temp exp1)) (start exp2 ...)))
中,(start exp1 exp2 …)
是多個(gè)參數(shù)時(shí)的模板,首先用let來綁定局部變量temp為exp1,然后用遞歸實(shí)現(xiàn)處理多個(gè)參數(shù),注意這里說的是宏定義中的遞歸,并不是過程調(diào)用中的遞
歸。另外在宏定義中可以用省略號(三個(gè)點(diǎn))來代表多個(gè)參數(shù)。
在Scheme的規(guī)范當(dāng)中,將表達(dá)式分為原始表達(dá)式和有源表達(dá)式,Scheme語言的標(biāo)準(zhǔn)定義中只有原始的if分支結(jié)構(gòu),其它均為有源型,即是用后來的宏定義成的,由此可見宏定義的重要性。附上面的定義在GUILE中實(shí)現(xiàn)的 代碼。
十一. 其它功能
1. 模塊擴(kuò)展
在R5RS
中并未對如何編寫模塊進(jìn)行說明,在諸多的Scheme語言的實(shí)現(xiàn)當(dāng)中,幾乎無一例外的實(shí)現(xiàn)了模塊的加載功能。所謂模塊,實(shí)際就是一些變量、宏定義和已命名
的過程的集合,多數(shù)情況下它都綁定在一個(gè)Scheme語言的符號下(也就是名稱)。在Guile中提供了基礎(chǔ)的ice-9模塊,其中包括POSIX系統(tǒng)調(diào)
用和網(wǎng)絡(luò)操作、正則表達(dá)式、線程支持等等眾多功能,此外還有著名的SFRI模塊。引用模塊用use-modules過程,它后面的參數(shù)指定了模塊名和我們
要調(diào)用的功能名,如:(use-modules (ice-9
popen)),如此后,就可以應(yīng)用popen這一系統(tǒng)調(diào)用了。如果你想要定義自己的模塊,最好看看ice-9目錄中的那些tcm文件,它們是最原始的定
義。
另外Guile在面向?qū)ο缶幊谭矫妫_發(fā)了GOOPS(Guile Object-Oriented Programming System),對于喜歡OO朋友可以研究一下它,從中可能會(huì)有新的發(fā)現(xiàn)。
2. 如何輸出漂亮的代碼
如何編寫輸出漂亮的Scheme語言代碼應(yīng)該是初學(xué)者的第一個(gè)問題,這在Guile中可以用ice-9擴(kuò)展包中提供的pretty-print過程來實(shí)現(xiàn),看下面的操作:
guile> (use-modules (ice-9 pretty-print)) ; 引用漂亮輸出模塊
guile> (pretty-print '(define fix (lambda (n)
(cond ((= n 0) 'iszero)
((< n 0) 'lower)
(else 'upper))))) ; 此處是我們輸入的不規(guī)則代碼
(define fix
(lambda (n)
(cond ((= n 0) 'iszero)
((< n 0) 'lower)
(else 'upper)))) ; 輸出的規(guī)則代碼
3. 命令行參數(shù)的實(shí)現(xiàn)
在把Scheme用做shell語言時(shí),經(jīng)常用到命令行參數(shù)的處理,下面是關(guān)于命令行參數(shù)的一種處理方法:
#! /usr/local/bin/guile -s
!#
(define cmm (command-line))
(display "應(yīng)用程序名稱:")
(display (car cmm))
(newline)
(define args (cdr cmm))
(define long (length args))
(define loop (lambda (count len obj)
(if (<= count len)
(begin
(display "參數(shù) ")
(display count)
(display " 是:")
(display (list-ref obj (- count 1)))
(newline)
(set! count (+ count 1))
(loop count len obj)))))
(loop 1 long args)
下面是運(yùn)行后的輸出結(jié)果:
[root@toymouse doc]# ./tz.scm abc 123 ghi
應(yīng)用程序名稱:./tz.scm
參數(shù) 1 是:abc
參數(shù) 2 是:123
參數(shù) 3 是:ghi
其中最主要的是用到了command-line過程,它的返回結(jié)果是命令參數(shù)的列表,列表的第一個(gè)成員是程序名稱,其后為我們要的參數(shù),定義loop遞歸調(diào)用形成讀參數(shù)的循環(huán),顯示出參數(shù)值,達(dá)到我們要的結(jié)果。
4. 特殊之處
一些精確的自己計(jì)算自己的符號
數(shù)字 Numbers 2 ==> 2
字符串 Strings "hello" ==> "hello"
字符 Charactors #\\g ==> #\\g
輯值 Booleans #t ==> #t
量表 Vectors #(a 2 5/2) ==> #(a 2 5/2)
通過變量計(jì)算來求值的符號
如:
x ==> 9
-list ==> ("tom" "bob" "jim")
factoral ==> #<procedure: factoral>
==> #<primitive: +>
define 特殊的form
(define x 9) ,define不是一個(gè)過程, 它是一個(gè)不用求所有參數(shù)值的特殊的form,它的操作步驟是,初始化空間,綁定符號x到此空間,然后初始此變量。
必須記住的東西
下面的這些定義、過程和宏等是必須記住的:
define,lambda,let,lets,letrec,quote,set!,if,case,cond,begin,and,or等等,當(dāng)然還有其它宏,必需學(xué)習(xí),還有一些未介紹,可參考有關(guān)資料。
走進(jìn)Scheme語言的世界,你就發(fā)現(xiàn)算法和數(shù)據(jù)結(jié)構(gòu)的妙用隨處可見,可以充分的檢驗(yàn)?zāi)銓λ惴ê蛿?shù)據(jù)結(jié)構(gòu)的理解。Scheme語言雖然是古老的函數(shù)型語言的繼續(xù),但是它的里面有很多是在其它語言中學(xué)不到的東西,我想這也是為什么用它作為計(jì)算機(jī)語言教學(xué)的首選的原因吧。
關(guān)于作者
宋國偉( mailto:gwsong52@sohu.com ),目前在 吉林省德惠市信息中心 從事網(wǎng)絡(luò)維護(hù)工作,著有 《GTK+2.0編程范例》 一書,熱衷于Linux系統(tǒng)上的編程及相關(guān)的研究。