日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Scheme 语言概要上

發布時間:2023/12/20 编程问答 64 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Scheme 语言概要上 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作為Lisp 變體,Scheme 是一門非常簡潔的計算語言,使用它的編程人員可以擺脫語言本身的復雜性,把注意力集中到更重要的問題上,從而使語言真正成為解決問題的工具。本文分為上、?下兩部分來介紹 scheme 語言。

一.Scheme語言的特點

Scheme語言是LISP語言的一個方言(或說成變種),它誕生于1975年的MIT,對于這個有近三十年歷史的編程語言來說,它并沒有象C++,java,C#那樣受到商業領域的青睞,在國內更是顯為人知。但它在國外的計算機教育領域內卻是有著廣泛應用的,有很多人學的第一門計算機語言就是Scheme語言。

它是一個小巧而又強大的語言,作為一個多用途的編程語言,它可以作為腳本語言使用,也可以作為應用軟件的擴展語言來使用,它具有元語言特性,還有很多獨到的特色,以致于它被稱為編程語言中的"皇后"。

下面是洪峰對Scheme語言的編程特色的歸納:

  • 詞法定界(Lexical Scoping)
  • 動態類型(Dynamic Typing)
  • 良好的可擴展性
  • 尾遞歸(Tail Recursive)
  • 函數可以作為值返回
  • 支持一流的計算連續
  • 傳值調用(passing-by-value)
  • 算術運算相對獨立

本文的目的是讓有編程基礎(那怕是一點點)的朋友能盡快的掌握Scheme語言的語法規則,如果您在讀完本文后,發現自己已經會用Scheme語言了,那么我的目的就達到了。

二.Scheme語言的標準與實現

R5RS (Revised(5) Report on the Algorithmic Language Scheme)

Scheme語言的語法規則的第5次修正稿,1998年制定,即Scheme語言的現行標準,目前大多數Scheme語言的實現都將達到或遵循此標準,并且幾乎都加入了一些屬于自己的擴展特色。

Guile (GNU's extension language)

Guile是GNU工程的一個項目,它是GNU擴展語言庫,它也是Scheme語言的一個具體實現;如果你將它作為一個庫打包,可以把它鏈接到你的應用程序中去,使你的應用程序具有自己的腳本語言,這個腳本語言目前就是Scheme語言。

Guile可以在LINUX和一些UNIX系統上運行,下面是簡單的安裝過程:

下載guile-1.6.4版,文件名為guile-1.6.4.tar.gz,執行下面的命令:

tar xvfz guile-1.6.4.tar.gz cd guile-1.6.4 ./configure make make install

如此,即可以執行命令guile,進入guile>提示符狀態,輸入調試Scheme程序代碼了,本文的所有代碼都是在guile下調試通過。

其它實現

除了Guile外,Scheme語言的實現還有很多,如:GNU/MIT-Scheme, SCI,Scheme48,DrScheme等,它們大多是開源的,可以自由下載安裝使用,并且跨平臺的實現也很多。你會發現既有象basic的Scheme語言解釋器,也有將Scheme語言編譯成C語言的編譯器,也有象JAVA那樣將Scheme語言代碼編譯成虛擬機代碼的編譯器。

三.基本概念

注釋

Scheme語言中的注釋是單行注釋,以分號[;]開始一直到行尾結束,其中間的內容為注釋,在程序運行時不做處理,如:

; this is a scheme comment line.

標準的Scheme語言定義中沒有多行注釋,不過在它的實現中幾乎都有。在Guile中就有多行注釋,以符號組合"#!"開始,以相反的另一符號組合"!#"結束,其中內容為注釋,如:

#! there are scheme comment area. you can write mulity lines here . !#

注意的是,符號組合"#!"和"!#"一定分做兩行來寫。

Scheme用做腳本語言

Scheme語言可以象sh,perl,python等語言那樣作為一種腳本語言來使用,用它來編寫可執行腳本,在Linux中如果通過Guile用Scheme語言寫可執行腳本,它的第一行和第二行一般是類似下面的內容:

#! /usr/local/bin/guile -s !#

這樣的話代碼在運行時會自動調用Guile來解釋執行,標準的文件尾綴是".scm"。

塊(form)

塊(form)是Scheme語言中的最小程序單元,一個Scheme語言程序是由一個或多個form構成。沒有特殊說明的情況下 form 都由小括號括起來,形如:

(define x 123) (+ 1 2) (* 4 5 6) (display "hello world")

一個 form 也可以是一個表達式,一個變量定義,也可以是一個過程。

form嵌套

Scheme語言中允許form的嵌套,這使它可以輕松的實現復雜的表達式,同時也是一種非常有自己特色的表達式。 下圖示意了嵌套的稍復雜一點的表達式的運算過程:

變量定義

可以用define來定義一個變量,形式如下:?
(define 變量名 值)

如: (define x 123) ,定義一個變量x,其值為123。

更改變量的值

可以用set!來改變變量的值,格式如下:?
(set! 變量名 值)

如: (set! x "hello") ,將變量x的值改為"hello" 。

Scheme語言是一種高級語言,和很多高級語言(如python,perl)一樣,它的變量類型不是固定的,可以隨時改變。

四.數據類型

1. 簡單數據類型

邏輯型(boolean)

最基本的數據類型,也是很多計算機語言中都支持的最簡單的數據類型,只能取兩個值:#t,相當于其它計算機語言中的 TRUE;#f,相當于其它計算機語言中的 FALSE。

Scheme語言中的boolean類型只有一種操作:not。其意為取相反的值,即:

(not #f) => #t(not #t) => #f

not的引用,與邏輯非運算操作類似

guile> (not 1) #f guile> (not (list 1 2 3)) #f guile> (not 'a) #f

從上面的操作中可以看出來,只要not后面的參數不是邏輯型,其返回值均為#f。

數字型(number)

它又分為四種子類型:整型(integer),有理數型(rational),實型(real),復數型(complex);它們又被統一稱為數字類型(number)。

如:復數型(complex) 可以定義為 (define c 3+2i)?
實數型(real)可以定義為 (define f 22/7)?
有理數型(rational)可以定義為 (define p 3.1415)?
整數型(integer) 可以定義為 (define i 123)

Scheme語言中,數字類型的數據還可以按照進制分類,即二進制,八進制,十進制和十六進制,在外觀形式上它們分別以符號組合 #b、 #o、 #d、 #x 來作為表示數字進制類型的前綴,其中表示十進制的#d可以省略不寫,如:二進制的 #b1010 ,八進制的 #o567,十進制的123或 #d123,十六進制的 #x1afc 。

Scheme語言的這種嚴格按照數學定理來為數字類型進行分類的方法可以看出Scheme語言里面滲透著很深的數學思想,Scheme語言是由數學家們創造出來的,在這方面表現得也比較鮮明。

字符型(char)

Scheme語言中的字符型數據均以符號組合 "#\" 開始,表示單個字符,可以是字母、數字或"[ ! $ % & * + - . / : %lt; = > ? @ ^ _ ~ ]"等等其它字符,如:?
#\A 表示大寫字母A,#\0表示字符0,?
其中特殊字符有:#\space 表示空格符和 #\newline 表示換行符。

符號型(symbol)

符號類型是Scheme語言中有多種用途的符號名稱,它可以是單詞,用括號括起來的多個單詞,也可以是無意義的字母組合或符號組合,它在某種意義上可以理解為C中的枚舉類型。看下面的操作:

guile> (define a (quote xyz)) ; 定義變量a為符號類型,值為xyz guile> a xyz guile> (define xyz 'a) ; 定義變量xyz為符號類型,值為a guile> xyz a

此處也說明單引號' 與quote是等價的,并且更簡單一些。符號類型與字符串不同的是符號類型不能象字符串那樣可以取得長度或改變其中某一成員字符的值,但二者之間可以互相轉換。

2. 復合數據類型

可以說復合數據類型是由基本的簡單數據類型通過某種方式加以組合形成的數據類型,特點是可以容納多種或多個單一的簡單數據類型的數據,多數是基于某一種數學模型創建的。

字符串(string) 由多個字符組成的數據類型,可以直接寫成由雙引號括起的內容,如:"hello" 。下面是Guile中的字符串定義和相關操作:

guile> (define name "tomson") guile> name "tomson" guile> (string-length name) ; 取字符串的長度 6 guile> (string-set! name 0 #\g) ; 更改字符串首字母(第0個字符)為小寫字母g (#\g) guile> name "gomson" guile> (string-ref name 3) ; 取得字符串左側第3個字符(從0開始) #\s

字符串還可以用下面的形式定義:

guile> (define other (string #\h #\e #\l #\l #\o )) guile> other "hello"

字符串中出現引號時用反斜線加引號代替,如:"abc\"def" 。

點對(pair)

我把它譯成"點對",它是一種非常有趣的類型,也是一些其它類型的基礎類型,它是由一個點和被它分隔開的兩個所值組成的。形如: (1 . 2) 或 (a . b) ,注意的是點的兩邊有空格。

這是最簡單的復合數據類型,同是它也是其它復合數據類型的基礎類型,如列表類型(list)就是由它來實現的。

按照Scheme語言說明中的慣例,以下我們用符號組合 "=>" 來表示表達式的值。

它用cons來定義,如: (cons 8 9) =>(8 . 9)

其中在點前面的值被稱為 car ,在點后面的值被稱為 cdr ,car和cdr同時又成為取pair的這兩個值的過程,如:

(define p (cons 4 5)) => (4 . 5) (car p) => 4 (cdr p) => 5

還可以用set-car! 和 set-cdr! 來分別設定這兩個值:

(set-car! p "hello") (set-cdr! p "good")

如此,以前定義的 p 又變成了 ("hello" . "good") 這個樣子了。

列表(list)

列表是由多個相同或不同的數據連續組成的數據類型,它是編程中最常用的復合數據類型之一,很多過程操作都與它相關。下面是在Guile中列表的定義和相關操作:

guile> (define la (list 1 2 3 4 )) guile> la (1 2 3 4) guile> (length la) ; 取得列表的長度 4 guile> (list-ref la 3) ; 取得列表第3項的值(從0開始) 4 guile> (list-set! la 2 99) ; 設定列表第2項的值為99 99 guile> la (1 2 99 4) guile> (define y (make-list 5 6)) ;創建列表 guile> y (6 6 6 6 6)

make-list用來創建列表,第一個參數是列表的長度,第二個參數是列表中添充的內容;還可以實現多重列表,即列表的元素也是列表,如:(list (list 1 2 3) (list 4 5 6))。

列表與pair的關系

回過頭來,我們再看看下面的定義:

guile> (define a (cons 1 (cons 2 (cons 3 '())))) guile> a (1 2 3)

由上可見,a本來是我們上面定義的點對,最后形成的卻是列表。事實上列表是在點對的基礎上形成的一種特殊格式。

再看下面的代碼:

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一定是一個pair,而pair不是list。

guile> (car ls) 1 guile> (cdr ls) (2 3 4)

其cdr又是一個列表,可見用于pair的操作過程大多可以用于list。

guile> (cadr ls) ; 此"點對"對象的cdr的car 2 guile> (cddr ls) ; 此"點對"對象的cdr的cdr (3 4) guile> (caddr ls) ; 此"點對"對象的cdr的cdr的car 3 guile> (cdddr ls) ; 此"點對"對象的cdr的cdr的cdr (4)

上在的操作中用到的cadr,cdddr等過程是專門對PAIR型數據再復合形成的數據操作的過程,最多可以支持在中間加四位a或d,如cdddr,caaddr等。

下圖表示了由pairs定義形成的列表:

這個列表可以由pair定義為如下形式:

(define x (cons 'a (cons 'b (cons 'c (cons 'd '())))))

而列表的實際內容則為:(a b c d)

由pair類型還可以看出它可以輕松的表示樹型結構,尤其是標準的二叉樹。

向量(vector)

可以說是一個非常好用的類型 ,是一種元素按整數來索引的對象,異源的數據結構,在占用空間上比同樣元素的列表要少,在外觀上:

列表示為: (1 2 3 4)?
VECTOR表示為: #(1 2 3 4)?
可以正常定義:(define v (vector 3 4 5))?
也可以直接定義:(define v #(3 4 5))

vector是一種比較常用的復合類型,它的元素索引從0開始,至第 n-1 結束,這一點有點類似C語言中的數組。

關于向量表(vector)的常用操作過程:

guile> (define v (vector 1 2 3 4 5)) guile> v #(1 2 3 4 5) guile> (vector-ref v 0) ; 求第n個變量的值 1 guile> (vector-length v) ; 求vector的長度 5 guile> (vector-set! v 2 "abc") ; 設定vector第n個元素的值 guile> v #(1 2 "abc" 4 5) guile> (define x (make-vector 5 6)) ; 創建向量表 guile> x #(6 6 6 6 6)

make-vector用來創建一個向量表,第一個參數是數量,后一個參數是添充的值,這和列表中的make-list非常相似。

我們可以看出,在Scheme語言中,每種數據類型都有一些基本的和它相關的操作過程,如字符串,列表等相關的操作,這些操作過程都很有規律,過程名的單詞之間都用-號隔開,很容易理解。對于學過C++的朋友來說,更類似于某個對象的方法,只不過表現的形式不同了。

3. 類型的判斷、比較、運算、轉換與方法

類型判斷

Scheme語言中所有判斷都是用類型名加問號再加相應的常量或變量構成,形如:

(類型? 變量)

Scheme語言在類型定義中有比較嚴格的界定,如在C語言等一些語言中數字0來代替邏輯類型數據False,在Scheme語言中是不允許的。

以下為常見的類型判斷和附加說明:

邏輯型:

(boolean? #t) => #t (boolean? #f) => #t 因為#t和#f都是boolean類型,所以其值為#t (boolean? 2) => #f 因為2是數字類型,所以其值為 #f

字符型

(char? #\space) => #t (char? #\newline) => #t 以上兩個特殊字符:空格和換行 (char? #\f) => #t 小寫字母 f (char? #\;) => #t 分號 ; (char? #\5) => #t 字符 5 ,以上這些都是正確的,所以返回值都是 #t (char? 5) => #f 這是數字 5 ,不是字符類型,所以返回 #f

數字型

(integer? 1) => #t (integer? 2345) => #t (integer? -90) => #t 以上三個數均為整數 (integer? 8.9) => #f 8.9不整數 (rational? 22/7) => #t (rational? 2.3) => #t (real? 1.2) => #t (real? 3.14159) => #t (real? -198.34) => #t 以上三個數均為實數型 (real? 23) => #t 因為整型屬于實型 (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 ; 此時 'x 為符號x,并不表示變量x的值

在Scheme語言中如此眾多的類型判斷功能,使得Scheme語言有著非常好的自省功能。即在判斷過程的參數是否附合過程的要求。

比較運算

Scheme語言中可以用<,>,<=,>=,= 來判斷數字類型值或表達式的關系,如判斷變量x是否等于零,它的形式是這樣的:(= x 0) ,如x的值為0則表達式的值為#t,否則為#f。

還有下面的操作:

(eqv? 34 34) => #t (= 34 34) => #t

以上兩個form功能相同,說明 eqv? 也可以用于數字的判斷。

在Scheme語言中有三種相等的定義,兩個變量正好是同一個對象;兩個對象具有相同的值;兩個對象具有相同的結構并且結構中的內容相同。除了上面提到的符號判斷過程和eqv?外,還有eq?和equal?也是判斷是否相等的過程。

eq?,eqv?,equal?

eq?,eqv?和equal?是三個判斷兩個參數是否相等的過程,其中eq?和eqv?的功能基本是相同的,只在不同的Scheme語言中表現不一樣。

eq?是判斷兩個參數是否指向同一個對象,如果是才返回#t;equal?則是判斷兩個對象是否具有相同的結構并且結構中的內容是否相同,它用eq?來比較結構中成員的數量;equal?多用來判斷點對,列表,向量表,字符串等復合結構數據類型。

guile> (define v (vector 3 4 5)) guile> (define w #(3 4 5)) ; w和v都是vector類型,具有相同的值#(3 4 5) guile> (eq? v w) #f ; 此時w和v是兩個對象 guile> (equal? v w) #t ; 符合equal?的判斷要求

以上操作說明了eq? 和equal? 的不同之處,下面的操作更是證明了這一點:

guile> (define x (make-vector 5 6)) guile> x #(6 6 6 6 6) guile> (eq? x x) ; 是同一個對象,所以返回#t #t guile> (define z (make-vector 5 6)) guile> z #(6 6 6 6 6) guile> (eq? x z) ; 不是同一個對象 #f guile> (equal? x z) ; 結構相同,內容相同,所以返回#t #t

算術運算

Scheme語言中的運算符有:?
+ , - , * , / 和 expt (指數運算)?
其中 - 和 / 還可以用于單目運算,如:

(- 4) => -4 (/ 4) => 1/4

此外還有許多擴展的庫提供了很多有用的過程,

max 求最大 (max 8 89 90 213) => 213 min 求最小 (min 3 4 5 6 7) => 3 abs 求絕對值 (abs -7) ==> 7

除了max,min,abs外,還有很多數學運算過程,這要根據你用的Scheme語言的運行環境有關,不過它們大多是相同的。在R5RS中規定了很多運算過程,在R5RS的參考資料中可以很容易找到。

轉換

Scheme語言中用符號組合"->"來標明類型間的轉換(很象C語言中的指針)的過程,就象用問號來標明類型判斷過程一樣。下面是一些常見的類型轉換過程:

guile> (number->string 123) ; 數字轉換為字符串 "123" guile> (string->number "456") ; 字符串轉換為數字 456 guile> (char->integer #\a) ;字符轉換為整型數,小寫字母a的ASCII碼值為96 97 guile> (char->integer #\A) ;大寫字母A的值為65 65 guile> (integer->char 97) ;整型數轉換為字符 #\a guile> (string->list "hello") ;字符串轉換為列表 (#\h #\e #\l #\l #\o) guile> (list->string (make-list 4 #\a)) ; 列表轉換為字符串 "aaaa" guile> (string->symbol "good") ;字符串轉換為符號類型 good guile> (symbol->string 'better) ;符號類型轉換為字符串 "better"

五.過程定義

過程(Procedure)

在Scheme語言中,過程相當于C語言中的函數,不同的是Scheme語言過程是一種數據類型,這也是為什么Scheme語言將程序和數據作為同一對象處理的原因。如果我們在Guile提示符下輸入加號然后回車,會出現下面的情況:

guile> + #<primitive-procedure +>

這告訴我們"+"是一個過程,而且是一個原始的過程,即Scheme語言中最基礎的過程,在GUILE中內部已經實現的過程,這和類型判斷一樣,如boolean?等,它們都是Scheme語言中最基本的定義。注意:不同的Scheme語言實現環境,出現的提示信息可能不盡相同,但意義是一樣的。

define不僅可以定義變量,還可以定義過程,因在Scheme語言中過程(或函數)都是一種數據類型,所以都可以通過define來定義。不同的是標準的過程定義要使用lambda這一關鍵字來標識。

Lambda關鍵字

Scheme語言中可以用lambda來定義過程,其格式如下:?
(define 過程名 ( lambda (參數 ...) (操作過程 ...)))

我們可以自定義一個簡單的過程,如下:

(define add5 (lambda (x) (+ x 5)))

此過程需要一個參數,其功能為返回此參數加5 的值,如:

(add5 11) => 16

下面是簡單的求平方過程square的定義:

(define square (lambda (x) (* x x)))

與lambda相同的另一種方式

在Scheme語言中,也可以不用lambda,而直接用define來定義過程,它的格式為:?
(define (過程名 參數) (過程內容 …))

如下面操作:

(define (add6 x) (+ x 6))add6#<procedure: add6 (x)> 說明add6是一個過程,它有一個參數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有三個參數,其中第一個參數proc也是一個操作過程(因為在Scheme語言中過程也是一種數據,可以作為過程的參數),另外兩個參數是數值,所以會出現上面的調用結果。

guile> (define add(lambda (x y)(+ x y))) guile> add #<procedure add (x y)> guile> (fun add 100 200) 300

繼續上面操作,我們定義一個過程add,將add作為參數傳遞給fun過程,得出和(fun + 100 200)相同的結果。

guile> ((lambda (x) (+ x x)) 5) 10

上面的 (lambda(x) (+ x x)) 事實上是簡單的過程定義,在后面直接加上操作參數5,得出結果10,這樣實現了匿名過程,直接用過程定義來操作參數,得出運算結果。

通過上面的操作,相信你已初步了解了過程的用法。 既然過程是一種數據類型,所以將過程作為過程的參數是完全可以的。以下過程為判斷參數是否為過程,給出一個參數,用 procedure? 來判斷參數是否為過程,采用if結構(關于if結構見下面的介紹):

guile> (define isp(lambda (x)(if (procedure? x) 'isaprocedure 'notaprocedure))) guile> isp #<procedure isp (x)> guile> (isp 0) notaprocedure guile> (isp +) isaprocedure

上面的過程就體現了Scheme語言的參數自省(辨別)能力,'0'是數字型,所以返回notaprocedure;而'+'是一個最基礎的操作過程,所以返回isaprocedure。

過程的嵌套定義

在Scheme語言中,過程定義也可以嵌套,一般情況下,過程的內部過程定義只有在過程內部才有效,相當C語言中的局部變量。

如下面的代碼的最終結果是50:

(define fix (lambda (x y z)(define add (lambda (a b) (+ a b)))(- x (add y z)))) (display (fix 100 20 30))

此時過程add只在fix過程內部起做用,這事實上涉及了過程和變量的綁定,可以參考下面的關于過程綁定(let,let* 和letrec)的介紹。

過程是初學者難理解的一個關鍵,隨著過程參數的增加和功能的增強,過程的內容變得越來越復雜,小括號也會更多,如果不寫出清晰的代碼的話,讀代碼也會成為一個難題。

熟悉了 scheme 基本概念、數據類型和過程(函數)后,?下一部分我們來學習 scheme 的結構、遞歸調用和其他擴展功能。

總結

以上是生活随笔為你收集整理的Scheme 语言概要上的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。