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)
)

  不过总体来讲,我还算满意,毕竟只学了一点点时间,能在指导下搞定这么些,已经挺不错。

相关文章
相关标签/搜索