码迷,mamicode.com
首页 > 其他好文 > 详细

【SICP练习】9 练习1.29-1.33

时间:2015-02-07 11:38:43      阅读:179      评论:0      收藏:0      [点我收藏+]

标签:

练习1.29

这道题的数学气息比较浓厚。像变量h和函数y在书中已经有了定义,并且n是偶数。另外通过观察我们发现当k等于0或者n时,y的系数为1;当k为偶数时,y的系数为2;当k为奇数时(在函数中直接用else也可以),y的系数为4。根据书中前面的讲解,需要有一个term作用在a上,还要有一个next来产生下一个a值。下面我们依次来完成这5个部分。

(define h (/ (- b a) n))

我曾将这一部分拿来编译过,但报错说n未定义。由此可见采用应用序取值的Lisp在采用应用序的同时还是从后往前求值的。不信的话,可以将n拿来define定义一下,会继续报错说a未定义而不是所b。好了我们再继续写后面的内容:

(define (y k) (f (+ a (* k h))))

(define (factor k)

   (cond ((or (= k 0) (= k n)) 1)

         ((even? k) 2)

       (else 4)))

(define (term k) (* (factor k) (y k)))

(define (next k) (+ k 1))

前文已经说了n是偶数,因此在调用simpson-ruler函数前应该先判断n的正负性:当n为奇数时报错,n为偶数时则计算并返回积分值。

(define (simpson-ruler f a b n)

(define h (/ (- b a) n))

(define (y k) (f (+ a (* k h))))

(define (factor k)

        (cond ((or (= k 0) (= k n)) 1)

              ((even? k) 2)

       (else 4)))

(define (term k) (* (factor k) (y k)))

(define (next k) (+ k 1))

(if (odd? n)

   (error “Error: You just input an odd number.”)

   (* (/ h 3) (sum term (exact->inexact 0) next n))))

exact->inexact在【Scheme归纳】2中有介绍,其用来把分数转换为浮点数。

函数sum和函数cube我们可以直接copy书中的代码。下面我们按照题目中的要求将n=100和n=1000来测试所写的函数是否正确。

(simpson-ruler cube 0 1 100)

;Value: .24999999999999992

(simpson-ruler cube 0 1 1000)

;Value: .2500000000000003

通过和书中的integral对比,辛普森规则很明显要精确得多。

 

练习1.30

这道题要求将一个递归的规程改成是迭代的,通过对递归版本的sum的观察得出以下变换形式:

a—(next a)

other—(+ (term a) other)

博主英文不太好实在想不出什么好词语了。变换成迭代通俗点讲就是要将sum中最后一行的加号去掉,因为这个是消耗大量空间的根源。

如果我们将a和other作为迭代中的产生,将这个过程展开即有:

(sum-iter a other)

(sum-iter (next a) (+ (term a) other))

(sum-iter (next (next a)) (+ (term (term a)) (+ (term a) other)))

博主就不再一直写下去了,就是按照前面的变换规则做对应变换罢了。

于是我们便可以完成题中的空缺部分了。

(define (sum term a next b)

    (define (sum-iter a other)

        (if (> a b)

          other

           (sum-iter (next a) (+ (term a) other))))

    (sum-iter a 0))

 

练习1.31

题目中已经说的很清楚了,product用来返回在给定范围中各点的某个函数值的乘积。我们惊奇的发现这和前面的sum有着很大的相似,无非是将加法换成了乘法:

(define (product term a next b)

    (if (> a b)

       1

       (* (term a)

         (product term (next a) next b))))

既然在上一道习题中已经得出了sum的迭代版本,在这里同样也可以将它写成迭代的。

(define (product term a next b)

    (define (product-iter a other)

        (if (> a b)

           other

           (product-iter (next a)

                        (* (term a) other))))

    (product-iter a 1))

不怕被笑话,我还去查了factorial的中文意思。有了product来写factorial不要太容易呀,只不过要借助以下很久之前用到过的lambda。不过完全也可以用额外定义的函数实现同样的功能,只不过在函数内用lambda会使代码更加简洁。

(define (factorial n)

    (product (lambda (x) x) 1 (lambda (x) (+ x 1)) n))

下面我们来测试一下这个函数。

晕倒。。。博主轻飘飘的来了一个(factorial 50)结果返回了半个屏幕宽的数字。

话说我写到这里的时候才把a题做完,b题都没有看,没想到居然不知不觉中把b也碰巧做了。不过再看看原来a还没有写完,还要求pi的近似值。

那么这部分的策略是将分子和分母分开来看。先来看分子,我们可以准备一个函数,有一个参数n,如果n是1则返回2,n是奇数则加上1,n是偶数则加2。分母也可以用这种函数来产生。然后我们将左式中的4乘到右式,并且通过前面学的exact->inexact将分数转换成浮点数。最后我们就求出了pi。下面是完整的代码。

(define (get-pi n)

   (define (get-numerator a)

       (cond ((= a 1) 2)

            ((odd? a) (+ a 1))

       (else (+ a 2))))

   (define (get-denominator b)

       (cond ((odd? b) (+ b 2))

             (else (+ b 1))))

    (define (add1 c)

       (+ c 1))

 (* 4 (exact->inexact  (/ (product get-numerator 1 add1 n)

                        (product get-denominator 1 add1 n)))))

如是,我们再来检测检测。

(get-pi 300)

;Value: 3.1467982645089903

参数n越大,计算得到的pi越精确。

 

练习1.32

因为递归比迭代要更容易实现,因此我先考虑的递归。先将sum和product都列出来。

(define (sum term a next b)

       (if (> a b)

      0

      (+ (term a)

         (sum term (next a) next b))))

(define (product term a next b)

    (if (> a b)

       1

       (* (term a)

         (product term (next a) next b))))

通过对比我们发现,仅仅是有2个地方的区别。按照题中的要去,我们将0或1的位置用null-value代替,将+或*用combiner代替。在函数的参数中添加这两个新的参数即可。通过对比,其实也不难嘛。

(define (accumulate combiner null-value term a next b)

    (if (> a b)

       null-value

       (combiner (term a) (accumulate combiner null-value term (next a) next b))))

题中还要求我们定义出sum和product来,这里我就列出sum的递归accumulate版本。

(define (sum term a next b)

   (accumulate + 0 term a next b))

接下来我们再看看如何写出迭代版本的accumulate。还是一样,先列出迭代版本的sum和product。

(define (sum term a next b)

    (define (sum-iter a other)

        (if (> a b)

          other

           (sum-iter (next a)

(+ (term a) other))))

    (sum-iter a 0))

(define (product term a next b)

    (define (product-iter a other)

        (if (> a b)

           other

           (product-iter (next a)

                        (* (term a) other))))

    (product-iter a 1))

同样是通过类比,我们又可以写出迭代版本的accumulate。

(define (accumulate combiner null-value term a next b)

    (define (accumulate-iter a other)

       (if (> a b)

          other

          (accumulate-iter (next a)

                          (combiner (term a) other))))

   (accumulate-iter a null-value))

这次我们就来写迭代版本的product。

(define (product term a next b)

        (accumulate * 1 term a next b))

通过这些对比,感觉枯燥的递归和迭代还挺有意思的。

 

练习1.33

题目第二行描述,只组合起由给定范围得到的项里的那些满足特定条件的项,因此我们需要在这一版本的accumulate中添加一个need-filter?,这个新的谓词可以用来传递下面就要用到的prime?。

(define (filtered-accumulate need-filter? combiner null-value term a next b)

(if (> a b)

   null-value

   (let ((other-term (filtered-accumulate need-filter?

                                       combiner

                                       null-value

                                        term

                                        (next a)

                                        b)))

        (if (need-filter? a)

                other-term

           (combiner (term a) other-term)))))

因此我们就可以通过accumulate来构造一个求a到b之间所有素数的和了。就像上一道题中将accumulate补充称product等一样,这里也是将抽象的filtered-accumulate添加一些固定的元素让它稍微”具体“点。a小题的函数也就出来了。

(define (accumulate-prime-sum a b)

    (filtered-accumulate prime? + 0 (lambda (x) x) a (lambda (x) (+ x 1)) b))

编译这段函数的前提是你已经将prime?加载上来了。

其实解答b小题就是要写出一个能够判断互素的谓词,这里定为a-prime-to-b?。

(define (a-prime-to-b? a b)

    (and (< a b) (= 1 (gcd a b))))

同样的,在这里也应该要将gcd函数加载上来。

类似于前面将need-to-filter?替换成prime?的过程,这里是用的谓词a-prime-to-b?。

(define (product-of –prime-accumulate n)

   (filtered-accumulate (lambda (x) (a-prime-to-b? x n))

                       *

                       1

                       (lambda (x) x)

                       1

                       (lambda (x) (+ x 1))

                       n))

作为初学者,还是多做点实践好了,再来写出迭代版本的filtered-accumulate

我就不再将我对比的过程写下来了,大家可以翻到前面看看。

(define (filtered-accumulate need-to-filter? combiner null-value term a next b)

    (define (filtered-accumulate-iter a other)

         (cond ((> a b) other)

               ((need-to-filter? a)

                (filtered-accumulate-iter (next a) (combiner (term a) other)))

               (else

                (filtered-accumulate-iter (next a) other))))

   (filtered-accumulate a null-value))

这道题我们就这样写完了,接下来我们会开始着重学习lambda了。虽然前面用过不少,但要想灵活运用lambda则比较难了。个人理解,lambda就像是C语言中的指针,灵活运用则威力强大。

【SICP练习】9 练习1.29-1.33

标签:

原文地址:http://www.cnblogs.com/NoMasp/p/4278462.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!