《计算机程序的构造与解释》(十一)

第三章关于程序的模块化、对象和状态

局部状态

    前面介绍了过程抽象和数据抽象,通过这两种抽象已经可以完成较强的计算功能。并且程序的组织结构也显得非常的优雅、易扩展;抽象背后的实现也可以随时修改,只要保证抽象接口不变。有一种强有力的设计原则就是依据模拟的真实世界的对象,去设计程序的结构。也就是说,程序的计算对象与待模拟的真实物理对象有对应关系。

    前面说的过程定义,有输入参数和返回值,它们的重要特点是只要参数相同,返回值总是固定的。例如加法操作add,有表达式(add 1 2),它总是返回3,无论什么时候,在什么情况下上面的表达式的值都是固定的。但是有时候,甚至是绝大多数的时候,真实物理世界的对象是“有状态”的。对象的操作过程会改变这个状态,并且会影响这个操作的下一次调用的返回情况。例如一个人作为对象,他有吃饭(eat)的动作。在吃了一碗饭之后,他就感觉有点饱了,虽然他可能还以再吃,但是并不能一直吃,因为“饱的状态”是会积累的。这就是说这个函数(eat)有side effect。这个副作用是通过对象的中间状态保持的。

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (make-monitor sqrt)
    (let ((count 0))
       (lambda(fun)
          (cond ((eq? fun 'how-many-call?) count)
                ((eq? fun 'reset) 
                 (set! count 0) )
                ((number? fun)
                 (begin (set! count (+ count 1))
                        (sqrt fun)))
                (else "no requre demanded!")))))
> (define s (make-monitor sqrt))
> (s 1)
1
> (s 100)
10
> (s 'how-many-call?)
2
> (s 'reset)
> (s 'how-many-call?)
0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> (define (make-acc acc)
    (lambda(x)
      (begin (set! acc (+ acc x))
              acc)))
> (define A (make-acc 5))
> (A 1)
6
> (A 1)
7
> (A 1)
8

上面举了两个例子,说明含有状态的过程,以及如何改变这个状态。第一个例子利用count,保存sqrt被调用的次数,而且还可以显示或者置零该计数器。如果所带的参数不是'reset和how-many-call?,而是数字,那么就求这个数的平方根,并把它作为整个过程的返回值。第二例子比较简单,acc是累加器,将参数累加到acc中,并作为整个过程的值。

在命令式语言中,如C、java,局部变量的赋值顺序将改变过程的执行结果,使得学习者不断考虑是变量的赋值顺序,以保障每个语句使用的是变量的正确版本。如果运行并发执行的语言,这个复杂性将大大增加。

同一与变化

变量和对象名称,与它们实际指代的东西(或者某种计算过程)相对应,我叫它们“名与实”。
同一个变量名,可能指向不同的对象;同样,不同的变量名称可能指代同一个对象。这里的“对象”是指实际所指的空间及其所存储的值或者抽象的过程,也就是“实”。在某一过程中定义一个与外部变量同名的局部变量,它们就指向不同的对象,但是名称相同。
(define peter-acc (make-account 100))
(define palu-acc peter acc)
这里peter-acc和paul-acc虽然名称不同,但是它们都指向同一个对象,修改它们中的任何一个,另一个都可以看见。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1,过程作为返回值 在1.3中我们明白了高阶函数之后,“用一个过程作为另外一个过程的返回值”则是稀松平常的事情了,比如下面的代码: (define (f x) (+ x 1)) (define (g) f) ((g) 2) 函数g没有参数,其返回值为函数f,所以((g) 2)就运算结果就是(f 2),最后运算结果为3。 上面是用一个已命名的函数作为返回结果的,相应的,也可以将一个“匿名过程”作为结果返回,这里的“匿名过程”也就是我们的Lambda表达式,所以上面的代码可以改造成: (define (g) (lambda (x) (+ x 1))) ((g) 2) 那么((g) 2)的运算结果就是((lambda (x) (+ x 1)) 2),最后运算结果为3。 2,牛顿法 学到这里,你可能需要复习一下高等数学的基本内容,包括“导数”和“微分”,高数的在线教材可以在这里找到:http://sxyd.sdut.edu.cn/gaoshu1/index.htm 关于牛顿法的介绍可以看这里:http://en.wikipedia.org/wiki/Newton%27s_method ,下面是程序: (define (close-enough? v1 v2) (< (abs (- v1 v2)) 0.000000001)) ;定义不动点函数 (define (fixed-point f first-guess) (define (try guess step-count) (let ((next (f guess))) (if (close-enough? guess next) next (try next (+ step-count 1))))) (try first-guess 0)) ;定义导数函数 (define (D f) (lambda (x dx) (/ (- (f (+ x dx)) (f x)) dx))) ;牛顿法 (define (newton g first-guess) (fixed-point (lambda (x) (- x (/ (g x) ((D g) x 0.000000001)))) first-guess)) ;平方 (define (square x) (* x x)) ;定义开方,来测试下牛顿法 (define (sq x) (newton (lambda (y) (- (square y) x)) 1.0)) (sq 5) 3,“一等公民” 这里列出了程序语言中作为“一等公民”的语言元素所具备的几个“特权”: 可以用变量命名 可以作为过程参数 可以作为过程返回结果 可以被包含在数据结构中 4,练习1.40 求三次方程 x^3 + ax^2 + bx + c 的零点。 首先,证明 函数f(x) = x^3 + ax^2 + bx + c 是“可微”的: 由可导和可微的性质知道,可导和可微互为充要条件,所以,要证可微我们可以先证可导, f ’ (x) = (x^3)’ + (ax^2)’ + (bx)’ + (c)’ = 3x^2 + 2ax + b 所以f(x)的导数存在,那么f(x)可导,其必定可微。 其次,利用“牛顿法”:如果f(x)是可微函数,那么f(x)=0的一个解就是函数(x – f(x)/df(x)的一个不动点,其中df(x)是f(x)的导数。所以我们可以轻松得到下面的代码: (define (close-enough? v1 v2) (< (abs (- v1 v2)) 0.000000001)) ;定义不动点函数 (define (fixed-point f first-guess) (define (try guess step-count) (let ((next (f guess))) (if (close-enough? guess next) next (try next (+ step-count 1))))) (try first-guess 0)) ;定义导数函数 (define (D f) (lambda (x dx) (/ (- (f (+ x dx)) (f x)) dx))) ;牛顿法 (define (newton g first-guess) (fixed-point (lambda (x) (- x (/ (g x) ((D g) x 0.000000001)))) first-guess)) ;定义cubic函数,也就是我们题目中所谓的f(x) (define (cubic a b c) (lambda (x) (+ (* x x x) (* a x x) (* b x) c))) ;随便定义几个系数 (define a 3) (define b 5) (define c 8) (define result (newton (cubic a b c) 1.0)) ;定义一个验证过程,让其验证得到的解,是否让方程成立 (define (validate x) (= 0 (+ (* x x x) (* a x x) (* b x) c))) ;输出结果 result ;验证结果 (validate result) 比如上面我们计算 x^3 + 3x^2 + 5x + 8 = 0, 其一个解为:-2.3282688556686084 .....
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值