scheme實現最基本的天然數下的運算

  版權申明:本文爲博主窗戶(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)
)

  不過總體來講,我還算滿意,畢竟只學了一點點時間,能在指導下搞定這麼些,已經挺不錯。

相關文章
相關標籤/搜索