在前面的章節(jié)中,我已經(jīng)講解了:
在本章中,我會(huì)講解如何自定義函數(shù)。由于Sheme是函數(shù)式編程語言,你需要通過編寫小型函數(shù)來構(gòu)造程序。因此,明白如何構(gòu)造并組合這些函數(shù)對(duì)掌握Scheme尤為關(guān)鍵。在前端定義函數(shù)非常不便,因此我們通常需要在文本編輯器中編輯好代碼,并在解釋器中加載它們。
你可以使用define
來將一個(gè)符號(hào)與一個(gè)值綁定。你可以通過這個(gè)操作符定義例如數(shù)、字符、表、函數(shù)等任何類型的全局參數(shù)。
讓我們使用任意一款編輯器(記事本亦可)來編輯代碼片段1中展示的代碼,并將它們存儲(chǔ)為hello.scm
,放置在類似于C:\doc\scheme\
的文件夾下。如果可以的話,把這些文件放在你在第一章定義的MIT-Scheme默認(rèn)文件夾下。
代碼片段1(hello.scm)
; Hello world as a variable
(define vhello "Hello world") ;1
; Hello world as a function
(define fhello (lambda () ;2
"Hello world"))
接下來,向Scheme解釋器輸入下面的命令:
(cd "C:\\doc\\scheme")
;Value 14: #[pathname 14 "c:\\doc\\scheme\\"]
(load "hello.scm")
;Loading "hello.scm" -- done
;Value: fhello
通過這些命令,hello.scm
就被加載到解釋器中。如果你的當(dāng)前目錄被設(shè)定在了腳本所在目錄,那么你就不需要再輸入第一行的命令了。然后,向解釋器輸入下面的命令:
vhello
;Value 15: "Hello world"
fhello
;Value 16: #[compound-procedure 16 fhello]
(fhello)
;Value 17: "Hello world"
操作符define
用于聲明變量,它接受兩個(gè)參數(shù)。define
運(yùn)算符會(huì)使用第一個(gè)參數(shù)作為全局參數(shù),并將其與第二個(gè)參數(shù)綁定起來。因此,代碼片段1的第1行中,我們聲明了一個(gè)全局參數(shù)vhello
,并將其與"Hello,World"
綁定起來。
緊接著,在第2行聲明了一個(gè)返回“Hello World”
的過程。
特殊形式lambda
用于定義過程。lambda
需要至少一個(gè)的參數(shù),第一個(gè)參數(shù)是由定義的過程所需的參數(shù)組成的表。因?yàn)楸纠?code>fhello沒有參數(shù),所以參數(shù)表是空表。
在解釋器中輸入vhello
,解釋器返回“Hello,World”。如果你在解釋器中輸入fhello
,它也會(huì)返回像下面這樣的值:#[compound-procedure 16 fhello]
,這說明了Scheme解釋器把過程和常規(guī)數(shù)據(jù)類型用同樣的方式對(duì)待。正如我們?cè)谇懊嬲鹿?jié)中講解的那樣,Scheme解釋器通過內(nèi)存空間中的數(shù)據(jù)地址操作所有的數(shù)據(jù),因此,所有存在于內(nèi)存空間中的對(duì)象都以同樣的方式處理。
如果把fhello
當(dāng)過程對(duì)待,你應(yīng)該用括號(hào)括住這些符號(hào),比如(fhello)
。
然后解釋器會(huì)按照第二章講述的規(guī)則那樣對(duì)它求值,并返回“Hello World”。
可以通過在lambda
后放一個(gè)參數(shù)表來定義有參數(shù)的函數(shù)。將代碼片段2保存為farg.scm
并放在同hello.scm
一致的目錄。
代碼片段2 (farg.scm)
; hello with name
(define hello
(lambda (name)
(string-append "Hello " name "!")))
; sum of three numbers
(define sum3
(lambda (a b c)
(+ a b c)))
保存文件,并在解釋器中載入此文件,然后調(diào)用我們定義的函數(shù)。
(load "farg.scm")
;Loading "farg.scm" -- done
;Value: sum3
(hello "Lucy")
;Value 20: "Hello Lucy!"
(sum3 10 20 30)
;Value: 60
Hello
函數(shù)hello
有一個(gè)參數(shù)(name)
,并會(huì)把“Hello”
、name的值
、和"!"
連結(jié)在一起并返回。
預(yù)定義函數(shù)string-append
可以接受任意多個(gè)數(shù)的參數(shù),并返回將這些參數(shù)連結(jié)在一起后的字符串。
sum3
:此函數(shù)有三個(gè)參數(shù)并返回這三個(gè)參數(shù)的和。
用lambda
定義函數(shù)是一種規(guī)范的方法,但你也可以使用類似于代碼片段3中展示的短形式。
代碼片段3
; hello with name
(define (hello name)
(string-append "Hello " name "!"))
; sum of three numbers
(define (sum3 a b c)
(+ a b c))
在這種形式中,函數(shù)按照它們被調(diào)用的形式被定義。代碼片段2和代碼片段3都是相同的。有些人不喜歡這種短形式的函數(shù)定義,但是我在教程中使用這種形式,因?yàn)樗梢允勾a更短小。
練習(xí)1
按照下面的要求編寫函數(shù)。這些都非常簡(jiǎn)單但實(shí)用。
- 將參數(shù)加1的函數(shù)。
- 將參數(shù)減1的函數(shù)。
練習(xí)2
讓我們按照下面的步驟編寫一個(gè)用于計(jì)算飛行距離的函數(shù)。
- 編寫一個(gè)將角的單位由度轉(zhuǎn)換為弧度的函數(shù)。180度即π弧度。π可以通過下面的式子定義:?
(define pi (* 4 (atan 1.0)))
。- 編寫一個(gè)用于計(jì)算按照一個(gè)常量速度(水平分速度)運(yùn)動(dòng)的物體,t秒內(nèi)的位移的函數(shù)。
- 編寫一個(gè)用于計(jì)算物體落地前的飛行時(shí)間的函數(shù),參數(shù)是垂直分速度。忽略空氣阻力并取重力加速度
g
為9.8m/s^2
。提示:設(shè)落地時(shí)瞬時(shí)豎直分速度為-Vy
,有如下關(guān)系。2 * Vy?= g * t
??> 此處t
為落地時(shí)的時(shí)間。- 使用問題1-3中定義的函數(shù)編寫一個(gè)用于計(jì)算一個(gè)以初速度
v
和角度theta
擲出的小球的飛行距離。- 計(jì)算一個(gè)初速度為40m/s、與水平方向呈30°的小球飛行距離。這個(gè)差不多就是一個(gè)臂力強(qiáng)勁的職業(yè)棒球手的投擲距離。
提示:首先,將角度的單位轉(zhuǎn)換為弧度(假定轉(zhuǎn)換后的角度為
theta1
)。初始水平、豎直分速度分別表示為:v*cos(theta1)
和v*sin(theta1)
。落地時(shí)間可以通過問題3中定義的函數(shù)計(jì)算。由于水平分速度不會(huì)改變, 因此可以利用問題2中的函數(shù)計(jì)算距離。
這里,我會(huì)推薦一些能非常方便地編輯Scheme代碼的編輯器。
Emacs21的Windows版本可以從http://ftp.gnu.org/gnu/emacs/windows/找到,下載emacs-21.3-bin-i386.tar.gz并解壓它。
你會(huì)在bin文件夾下發(fā)現(xiàn)一個(gè)叫runemacs.exe的可執(zhí)行文件。雙擊這個(gè)程序來啟動(dòng)編輯器。盡管鍵位布局和Windows的標(biāo)準(zhǔn)相當(dāng)不同,但是因?yàn)橛幸粋€(gè)菜單欄和鼠標(biāo)控制器而顯得相當(dāng)用戶友好。你也可以通過編輯名為.emacs的配置文件來實(shí)現(xiàn)自定義配置。編輯器提供了一種Scheme模式,此模式下能夠編輯器能識(shí)別預(yù)定義單詞,以及通過Ctri-i或TAB鍵來自動(dòng)縮進(jìn)。除此之外,當(dāng)一個(gè)輸入一個(gè)右括號(hào)后,編輯器會(huì)自動(dòng)顯示與之匹配的左括號(hào)。
在Windows系統(tǒng)中,emacs不能夠與MIT-Scheme進(jìn)行交互。你只能手動(dòng)地儲(chǔ)存并加載源代碼。但從另一個(gè)方面來說,在UNIX和Linux系統(tǒng)下,emacs可以同MIT-Scheme進(jìn)行交互式地調(diào)用,因此編輯代碼也可以在交互中完成。
Edwin是MIT-Scheme配備的編輯器。它有點(diǎn)像emacs18。但它沒有菜單欄和鼠標(biāo)控制,因此顯得不太用戶友好。只有少數(shù)人用這個(gè)編輯器,因此網(wǎng)絡(luò)上可用的說明也很少。雖然如此,你可以使用這個(gè)編輯器進(jìn)行交互式的代碼編輯。我在Windows上使用這個(gè)編輯器編輯Scheme代碼。
雙擊Edwin的圖標(biāo)以啟動(dòng)Edwin。當(dāng)Edwin啟動(dòng)后,一個(gè)叫*Scheme*
的默認(rèn)緩沖區(qū)出現(xiàn)在屏幕上,它對(duì)應(yīng)于emacs中的*scratch*
緩沖區(qū)。你可以將*scheme*
用作解釋器前端。按下Ctrl-X?Ctrl-e?就可以對(duì)S-表達(dá)式進(jìn)行求值。
文件的打開與關(guān)閉,編輯器的關(guān)閉。按下Ctrl-X?Ctrl-F來打開一個(gè)文件。如果你指定的文件并不存在,則會(huì)創(chuàng)建一個(gè)新文件。初始路徑被設(shè)置為了‘C:\’,你在打開文件前應(yīng)該修改這個(gè)路徑。按下Ctrl-X?Ctrl-S來保存文件,而按下Ctrl-x?Ctrl-w則是文件另存為。退出編輯器請(qǐng)按下Ctrl-x?Ctrl-c。
縮進(jìn)。按下Ctrl-i或者TAB可以縮進(jìn)。
剪切,復(fù)制和粘貼。我們無法使用鼠標(biāo),因此復(fù)制(剪切)、粘貼起來就會(huì)顯得不太方便。但你可以像下面這樣做:
求值S-表達(dá)式
define
開頭的S-表達(dá)式。*scheme*
緩沖區(qū)中的S-表達(dá)式。請(qǐng)查閱Scheme用戶手冊(cè)以獲得更多關(guān)于Edwin的幫助。你下載的MIT-Scheme中也附帶了同樣的文檔。
本章中,我講解了如何定義函數(shù)。特殊形式define
用于定義函數(shù)和全局參數(shù)。我也講解了用合適的編輯器(比如emacs)來編輯源代碼,載入源碼文件比在前端直接定義函數(shù)方便多了。
下個(gè)章節(jié)中,我講介紹分支。
; 1
(define (1+ x)
(+ x 1))
; 2
(define (1- x)
(- x 1))
代碼如下所示:
; definition of pi
(define pi (* 4 (atan 1.0)))
; degree -> radian
(define (radian deg)
(* deg (/ pi 180.0)))
; free fall time
(define (ff-time vy)
(/ (* 2.0 vy) 9.8))
; horizontal distance
(define (dx vx t)
(* vx t))
; distance
(define (distance v ang)
(dx
(* v (cos (radian ang))) ; vx
(ff-time (* v (sin (radian ang)))))) ; t
向解釋器中載入后,距離可以像這樣計(jì)算:
(distance 40 30)
;Value: 141.39190265868385
函數(shù)返回一個(gè)合理的值:141.1米,因?yàn)楹雎粤丝諝庾枇?,所以這個(gè)值略微偏大。
更多建議: