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

SICP 习题 (1.46)解题总结

时间:2014-09-21 00:46:49      阅读:443      评论:0      收藏:0      [点我收藏+]

标签:style   blog   io   os   使用   ar   2014   sp   问题   

SICP 习题 1.46 要求我们写一个过程iterative-improve,它以两个过程为参数,其中一个参数用来检测猜测是否足够好,另一个参数用来改进猜测。过程iterative-improve应该返回另一个过程,所返回的过程接收一个参数作为初始猜测,然后不断改进猜测直到结果足够好。题目还要求我们使用iterative-improve重写1.1.7的sqrt过程和1.3.3节的fixed-point过程。


因为涉及到高阶函数,所以整个题目理解起来有一点点费劲。不过这道题作为第一章的收官题确实非常合适,它涉及到“过程作为参数”,也涉及到“过程作为返回值”,这两点差不多已经是高阶函数的全部了。当然,还有最重要的一点,就是高阶函数的灵魂——抽象,是对一般过程的抽象,也包括对抽象过程的抽象。这里涉及到的就是将sqrt和fixed-point这两个看上去相差挺大的两个过程抽象成一个通用的过程。


为了完成这道题,我们需要先回去看看1.1.7节定义的sqrt过程,过程代码如下:


(define (sqrt-iter guess x)
	(if (good-enough? guess x)
	guess
	(sqrt-iter (improve guess x) x)))



如果我们将sqrt过程中通用的东西抽象出来,关键的东西有三样:

1. 当前猜测,

2. 判断结果是否足够好,

3. 改进猜测。

用语言来表达的话,对以上三样关键东西的组织形式如下:


看当前猜测是否足够好,好的话就返回当前猜测,如果不够好就通过某种手段改进猜测,再次对新的猜测进行判断。


如果用伪代码来表示大概情形如下:


(define (my-proc 判断猜测的方法   改进猜测的方法    当前猜测)
		(if   (当前猜测足够好)
				当前猜测
				(my-proc 判断猜测的方法   改进猜测的方法    (改进猜测的方法  当前猜测))))


对应的代码如下:

 

 (define (improve-guess-inner good-enough? improve current-guess)
    (if (good-enough? current-guess)
	current-guess
	(improve-guess-inner good-enough? improve (improve current-guess)))

书中还提到,要求我们做的方法iterative-improve应该返还一个过程,这个过程接受初始猜测作为唯一参数,然后启动不断改进猜测的计算。

所以将以上代码打包一下可以得出我们的iterative-improve过程:


(define (iterative-improve good-enough? improve)
  (define (improve-guess-inner good-enough? improve current-guess)
    (if (good-enough? current-guess)
	current-guess
	(improve-guess-inner good-enough? improve (improve current-guess))))
      
  (lambda (guess)
    (improve-guess-inner good-enough? improve guess)))



注意它返回的是一个lamda函数。


好,以上是题目的前半部分,后半部分要求我们用iterative-improve重写sqrt过程和fixed-point过程。


如果是直接调用improve-guess-inner过程的话,新的sqrt过程就可以写成这样:


(improve-guess-inner sqrt-good-enough? sqrt-improve 1.0)



不过我们的improve-guess-inner是内部方法,不能直接使用,我们需要使用iterative-improve来调用,调用方法如下:


((iterative-improve sqrt-good-enough? sqrt-improve) 1.0)


其中的good-enough?过程为:

(define (sqrt-good-enough? guess)
    (< (abs (- x (* guess guess))) small-number)



其中的sqrt-improve过程如下:

(define (sqrt-improve cur-guess)
    (/ (+ (/ x cur-guess) cur-guess) 2))



所以,新的sqrt过程整体实现如下:

(define (new-sqrt x)
  (define (sqrt-good-enough? guess)
    (< (abs (- x (* guess guess))) small-number))
  
  (define (sqrt-improve cur-guess)
    (/ (+ (/ x cur-guess) cur-guess) 2))

  ((iterative-improve sqrt-good-enough? sqrt-improve) 1.0))





再来看看1.3.3节中得fixed-point过程的定义,代码如下:


(define (fixed-point f first-guess)
  (define (close-enough? v1 v2)
    (< (abs (- v1 v2 )) tolerance))
  (define (try guess)
    (newline)
    (display (+ 0.0 guess))
    (let (( next (f guess)))
      (if (close-enough? guess next)
	  (+ 0.0 next)
	  (try next))))
  (try first-guess))



这段代码看上去和sqrt程序差别还挺大,不过,如果你仔细观察过程fixed-point,可以发现它的关键思想也是和sqrt一样的,就是看当前猜测是否足够好,好的话就返回当前猜测,如果不够好就通过某种手段改进猜测,再次对新的猜测进行判断。。。


其中判断猜测的方法就是那个close-enough。

其中有一个问题,前面的good-enough?方法只接受一个参数,而close-enough接受两个参数,判断两个参数是否足够接近。

要解决这个问题,我们需要考察fixed-point的工作原理,其中close-enough需要判断的是guess 和 (f guess)是否足够接近,所以我们可以这样从新定义close-enough

(define (fp-good-enough? cur-guess)
    (< (abs (- cur-guess (f cur-guess))) small-number))


用来比较cur-guess(f cur-guess)是否足够接近,注意这个过程必须定义在过程f有效的地方。


对于fixed-piont 过程来讲,改进猜测的方法就是f本身,有关这一点如果不明白就需要回去看看1.3.3节有关fixed-point过程的讨论。


所以新的fixed-point过程可以写成这样:

(improve-guess-inner fp-good-enough? f guess)



因为improve-guess-inner不能直接调用,所以使用iterative-improve来调用的话代码如下:

((iterative-improve fp-good-enough? f) first-guess)

最终完成的new-fiexed-point过程如下:

(define (new-fixed-point f first-guess)
  (define (fp-good-enough? cur-guess)
    (< (abs (- cur-guess (f cur-guess))) small-number))

  ((iterative-improve fp-good-enough? f) first-guess))




最后还有一个就是定义small-number等于多少:

(define small-number 0.000001)


以上就是SICP习题1.46的解题总结,其中有几个重点,一个是“过程作为参数”,一个是“过程作为返回值”,还有就是对一般过程的抽像,通过一般过程的抽象产生更为通用的过程。


最后还有一个隐藏的知识点,就是lambda函数中的变量作用范围,这个在写代码的时候需要特别留意。


SICP 习题 (1.46)解题总结

标签:style   blog   io   os   使用   ar   2014   sp   问题   

原文地址:http://blog.csdn.net/keyboardota/article/details/34809027

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