版權申明:本文爲博主窗戶(Colin Cai)原創,歡迎轉帖。如要轉貼,必須註明原文網址 http://www.cnblogs.com/Colin-Cai/p/9123363.html 做者:窗戶 QQ:6679072 E-mail:6679072@qq.com
教一個基本沒編過什麼程序的朋友scheme,爲何教scheme呢?由於他想學,由於一直聽我鼓吹,而他以爲他本身多少有C語言一點基礎,而又由於我以爲函數式纔像數學,而過程式是偏向物理現實的,感受不夠抽象。固然,對於一個成年人來講,有着太多的生活、學習、工做經驗,這些不少由於是物理現實,頗有過程式的意思,對於理解遞歸這種數學抽象總以爲是不容易的。我告訴他,這個和你曾經讀書時學的C語言有天壤之別。但不管如何,我決定試一試。html
理解了前綴表達,理解了基本的遞歸,想一想仍是從這裏開始吧。sql
我給出了三個函數:eq0,用來判斷是否爲0;inc,用來獲得一個天然數的後繼數;dec,用來獲得一個天然數是哪一個天然數的後繼(有個特例,0不是任何數的後繼,這裏返回0)。而後讓他藉助scheme的遞歸,其他的只利用這三個函數來構造加減乘除乃至餘數、乘方、對數。編程
;使用這三個函數實現天然數內的加減乘除乘方對數(《遞歸論》裏的運算,除法和對數都是向下取整,減法被減數小於減數獲得0) (define (eq0 x) (= x 0)) (define (inc x) (+ x 1)) (define (dec x) (if (= x 0) 0 (- x 1)))
遞歸論裏都是天然數內部的函數,固然遞歸論其實本質上不過是用天然數(一個特殊的可列集)內的遞歸來模擬全部的運算,由於本質上咱們一個肯定的計算都是針對可列集。天然數裏的計算搞定了,全部可計算問題均可以等價的轉爲天然數內的計算。函數式編程
固然,上升到遞歸論層次,有些東西仍是難懂的,好比通常遞歸算子和原始遞歸算子的理解。不過,若是隻是學習函數式編程,而不是爲了學習數學,一些東西能夠庸俗化一點。函數
很顯然,這個問題是難到他了,半天連加法彷佛沒有出來。因而我決定啓發一下他。咱們能夠這樣去考慮遞歸:學習
咱們知道任何一個數加0都等於自身,那麼假設計算4+3,能夠這樣考慮遞歸的過程,spa
4+3=5+2=6+1=7+0=7設計
也就是x+y往(x+1)+(y-1)的方向去遞歸,直到第二個數字遞歸到0爲止。code
很快這位仁兄寫出瞭如下代碼htm
(define (add x y)
(if (eq0 y) x
(add (inc x) (dec y))
)
)
再接再礪,減法也是先後兩個數逐漸dec,直到兩個有一個變爲0,嗯,也很快搞定(由於是天然數內的,這裏的減法小的數減大的數結果爲0,遞歸論裏定義的減法就是這樣)
(define (sub x y)
(if (eq0 x) 0
(if (eq0 y) x
(sub (dec x) (dec y))
)
)
)
我而後說,這裏其實能夠縮減一下(0 dec仍是0)
(define (sub x y)
(if (eq0 y) x
(sub (dec x) (dec y))
)
)
作乘法的時候,遇到了困難,因而我就提醒了一下,其實x*y,y=0的時候爲0,y不爲0的時候,x*y=x*(y-1)+x,因此能夠這樣寫
(define (mul x y) (if (eq0 y) 0 (add x (mul x (dec y))) ) )
他不可理解了,說add不是剛纔定義過的嗎,爲何如今就能夠用了?我頓時明白了他之因此爲難的緣由,因而解釋說,固然能夠用,咱們數學裏面不是爲了證實一個定理,不少時候先去證實一個引理嘛。他頓時理解了。
而後我接着說,若是不直接用也是能夠的,只是複雜一些。我用3*3來解釋這個問題,我須要記錄過程狀態:
3 3 0 0
3 2 3 0
3 2 2 1
3 2 1 2
3 2 0 3
3 1 3 3
3 1 2 4
3 1 1 5
3 1 0 6
3 0 3 6
3 0 2 7
3 0 1 8
3 0 1 9
左邊第一個是被乘數,第二個是乘數,第四個是累計的結果。
計算過程規則以下:
(1)最開始的時候,第三個數和第四個數都爲0。
(2)當第三個數爲0的時候,第三個數減1,第四個數加1。
(3)當第三個數爲0的時候,若是乘數不爲0,就借一個被乘數過來,而後乘數減1;若是乘數爲0,那麼第四個累計的結果就是乘積。
做爲函數式編程使用附加的值只能再加參數,因而有了這樣的計算方法:
(define (_mul x y x2 r) (if (eq0 x2) (if (eq0 y) r (_mul x (dec y) x r) ) (_mul x y (dec x2) (inc r)) ) ) (define (mul x y) (_mul x y 0 0) )
他很驚訝於原來還能夠這麼幹,不過很快理解了個人作法,而後我又告訴他,由於這個_mul是爲了解決mul臨時定義出來的函數,如同證實中的引理,能夠寫到mul的定義裏面,寫成
(define (mul x y) (define (_mul x y x2 r) (if (eq0 x2) (if (eq0 y) r (_mul x (dec y) x r) ) (_mul x y (dec x2) (inc r)) ) ) (_mul x y 0 0) )
接着,很快,他很快完成了乘方,
(define (pow x y) (if (eq0 y) 1 (mul x (pow x (dec y))) ) )
嗯,還算能夠,至少會觸類旁通,只是0的任意次冪爲0,但0的0次冪嘛。。。算了,索性x不能等於0,算解決了。
除法(天然數內的除法這裏只考慮整數部分)也很快作完,
(define (div x y) (if (> y x) 0 (inc (div (sub x y) y)) ) )
我「嗯?」了一下,而後說我這裏只定義了一個謂詞eq0,並無定義>
而後我提醒他須要定義>,他搞不定了。
因而仍是得我來寫,
(define (> x y) (if (eq0 x) #f (if (eq0 y) #t (> (dec x) (dec y)) ) ) )
其實,也能夠用以前的減法定義>
(define (> x y) (if (eq0 (- x y)) #f #t))
接着,他完成了餘數和對數
(define (rem x y) (if (> y x) x (rem (sub x y) y) ) ) ;這裏的對數,是實數下對數的整數部分 (define (log x y) (if (> y x) 0 (inc (log (div x y) y)) ) )
然而對數的實現稍有問題(固然,不考慮x,y爲0,y爲1的狀況),也不符合這個函數我想讓他寫成的樣子,不過我不得不說他寫對了,與其說是寫對了,倒不如說是蒙對了,由於這個的寫法是須要一個數學證實的(不要忘了,div返回的只是整數)。
證實在這裏我就不贅述了,我但願他完成的大體應該以下:
(define (log x y) (define (_log x y y^r r) (if (> y^r x) (dec r) (_log x y (mul y^r y) (inc r)) ) ) (_log x y 1 0) )
不過總體來講,我還算滿意,畢竟只學了一點點時間,能在指導下搞定這麼些,已經挺不錯。