Scheme语言直译为汉语(十二)

序对cons还可以怎样实现

(define (cons x y)
    (define (dispatch m)
        (cond ((= m 0) x)
              ((= m 1) y)
              (else (error "Argument not 0 or 1 -- CONS" m))))
     dispatch)
(define (car z) (z 0))
(define (cdr z) (z 1))

尝试把上述过程直译为汉语:

(定义 (序对 甲 乙)
    (定义 (寻值 序数)
        (情况符合 ((= 序数 0) 甲)
                 ((= 序数 1) 乙)
                 (否则 (错误 "序数既不是0也不是1" 序数))))
     寻值)
(定义 (前项 丙) (丙 0))
(定义 (后项 丙) (丙 1))

应该特别注意这里的一个微妙之处:由(cons x y)返回的值是一个过程——也就是那个内部定义的过程dispatch,它有一个参数,并能根据参数是0还是1,分别返回x或者y。与此相对应,(car z) 被定义为将z应用于0,这样,如果z是由(cons x y)形成的过程,将z应用于0将会产生x,这样就证明了(car (cons x y)) 产生出x,正如我们所需要的。与此类似,(cdr (cons x y)) 将(cons x y)产生的过程应用于1而得到y。因此,序对的这一过程实现确实是–个合法的实现,如果只通过cons、car和cdr访问序对,我们将无法把这一实现与“真正的"数据结构区分开。

上面展示了序对的一种过程性表示,这并不意味着我们所用的语言就是这样做的(Scheme和一般的Lisp系统都直接实现序对,主要是为了效率),而是说它确实可以这样做。这一过程性表示虽然有些隐晦,但它确实是一种完全合适的表示序对的方式,因为它满足了序对需要满足的所有条件。这一实例也说明可以将过程作为对象去操作,因此就自动地为我们提供了一种表示复合数据的能力。这些东西现在看起来好像只是很好玩,但实际上,数据的过程性表示将在我们的程序设计宝库里扮演一种核心角色。有关的程序设计风格通常称为消息传递。在以后讨论模型和模拟时,我们将用它作为一种基本工具。

练习1
下面是序对的另一种过程性表示方式。请针对这一表示验证,对于任意的x和y,(car (cons x y))都将产生出x。

(define (cons x y)
    (lambda (m) (m x y)))
(define (car z)
    (z (lambda (p q) p)))

对应的cdr该如何定义?请利用代换模型验证上述表示确实可行。

尝试把题中过程直译为汉语:

(定义 (序对 甲 乙)
    (规定 (序) (序 甲 乙)))
(定义 (前项 丙)
    (丙 (规定 (戊 己) 戊)))

(car (cons 1 2))

的展开序列:

(car z)
(z (lambda (p q) p))
((cons x y) (lambda (p q) p))
((lambda (m) (m x y)) (lambda (p q) p))
((lambda (p q) p) x y)
x

尝试把上述过程直译为汉语:

(前项 丙)
(丙 (规定 (戊 己) 戊))
((序对 甲 乙) (规定 (戊 己) 戊))
((规定 (序) (序 甲 乙)) (规定 (戊 己) 戊))
((规定 (戊 己) 戊) 甲 乙)
甲

cdr:

(define (cdr z)
    (z (lambda (p q) q)))

尝试把上述过程直译为汉语:

(定义 (后项 丙)
    (丙 (规定 (戊 己) 己)))

练习2
请证明,如果将 a a a b b b的序对表示为乘积 2 a 3 b 2^a 3^b 2a3b对应的整数,我们就可以只用非负整数和算术运算表示序对。请给出对应的过程cons、car和cdr的定义。


我们先按题中意思找两个数试一下,比如12和31?两个数要组成序对<12,31>,那按题中意思,我们就要把它俩写成 2 12 ⋅ 3 31 2^{12}\cdot3^{31} 212331这样,我们发现由于2和3是两个互质数,那要获取前项car的话,就可以一直除2直至除净,每次除2加1,就能把12这个数弄出来;要获得31,则相应的可以一直除3,直至除净,每次除以3时加一就能把31这个数算出来。

(define (cons x y)
    (* (expt 2 x)
       (expt 3 y)))
(define (car z)
    (if (= 0 (remainder z 2))
        (+ 1 (car (/ z 2)))
        0))
(define (cdr z)
    (if (= 0 (remainder z 3))
        (+ 1 (cdr (/ z 3)))
        0))

尝试把上述过程直译为汉语:

(定义 (序对 甲 乙)
    (* (幂 2 甲)
       (幂 3 乙)))
(定义 (前项 丙)
    (如果 (= 0 (取余数 丙 2))
          (+ 1 (前项 (/ 丙 2)))
          0))
(定义 (后项 丙)
    (如果 (= 0 (取余数 丙 3))
          (+ 1 (后项 (/ 丙 3)))
          0))

练习3
如果觉得将序对表示为过程还不够劲儿,那么请考虑,在一个可以对过程做各种操作的语言里,我们完全可以没有数(至少在只考虑非负整数的情况下),可以将0和加一操作实现为:

(define zero (lambda (f) (lambda (x) x)))

(define (add-1 n)
    (lambda (f) (lambda (x) (f ((n f) x)))))

这一表示形式称为Church计数,名字来源于其发明人数理逻辑学家Alonzo Church (丘奇), λ \lambda λ演算也是他发明的。

请直接定义one和two (不用zero和add-1) (提示:利用代换去求值(add-1 zero))。请给出加法过程+的一个直接定义(不要通过反复应用add-1)。


说实话,看着题中的这俩定义,盯了半天看不出这是怎么定义的0和加一运算。不过按照题目的提示,可以先不管它为啥这样就定义了零和加一,利用代换求值(add-1 zero),一步步代换也能在依然处于懵逼状态下给出one的定义:

;(define one (add-1 zero))
;(define one
;    (lambda (f) (lambda (x) (f ((zero f) x)))))
;(define one
;    (lambda (f) (lambda (x) (f ((
;        (lambda (f) (lambda (x) x)) f) x)))))
;(define one
;    (lambda (f) (lambda (x) (f ((lambda (x) x) x)))))
(define one
    (lambda (f) (lambda (x) (f x))))

尝试把上述过程直译为汉语:

;(定义 一 (加一 零))
;(定义 一
;    (规定 (道) (规定 (元) (道 ((零 道) 元)))))
;(定义 一
;    (规定 (道) (规定 (元) (道 ((
;        (规定 (道) (规定 (元) 元)) 道) 元)))))
;(定义 一
;    (规定 (道) (规定 (元) (道 ((规定 (元) 元) 元)))))
(定义 一
    (规定 (道) (规定 (元) (道 元))))

接下来,定义two也就是一加一呗,唉,继续代换:

;(define two
;    (add-1 one))
;(define two
;    (lambda (f) (lambda (x) (f ((one f) x)))))
;(define two
;    (lambda (f) (lambda (x) (f ((
;        (lambda (f) (lambda (x) (f x))) f) x)))))
;(define two
;    (lambda (f) (lambda (x) (f ((lambda (x) (f x)) x)))))
;(define two
    (lambda (f) (lambda (x) (f (f x)))))

尝试把上述过程直译为汉语:

;(定义 二
;    (加一 一))
;(定义 二
;    (规定 (道) (规定 (元) (道 ((一 道) 元)))))
;(定义 二
;    (规定 (道) (规定 (元) (道 ((
;        (规定 (道) (规定 (元) (道 元))) 道) 元)))))
;(定义 二
;    (规定 (道) (规定 (元) (道 ((规定 (元) (道 元)) 元)))))
(定义 二
    (规定 (道) (规定 (元) (道 (道 元)))))

用代换法定义一下add-2试试,也就是(add-1(add-1 n))

;(define (add-2 n)
;    (add-1 (add-1 n)))
;(define (add-2 n)
;    (add-1 (lambda (f) (lambda (x) (f ((n f) x))))))
;(define (add-2 n)
;    (lambda (f) (lambda (x) (f (((lambda (f) (lambda (x) (f ((n f) x)))) f) x)))))
;(define (add-2 n)
;    (lambda (f) (lambda (x) (f ((lambda (x) (f ((n f) x))) x)))))
;(define (add-2 n)
;    (lambda (f) (lambda (x) (f (f ((n f) x))))))

推到这里之后,我查答案,人家说还能往后推出这样一步:

(define (add-2 n)
  (lambda (f) (lambda (x) ((two f) ((n f) x)))))

但我说真的,实在看不出这一步怎么来的,我怀疑是不是需要对邱奇计数的本质真正理解之后才能写出这一步?不知道。。。

但若有了上面这步后,我们可以套用数学归纳法的方法,假定add-m-1为:

(define (add-2 n)
  (lambda (f) (lambda (x) ((m-1 f) ((n f) x)))))

再接下来,继续用代换的方法来求add-m,add-m 也就是(add-1 (add-m-1 n))喽:

; (define (add-m n)
;   (add-1 (add-m-1 n)))
; (define (add-m n)
;   (add-1 (lambda (f) (lambda (x) ((m-1 f) ((n f) x))))))
; (define (add-m n)
;   (lambda (f) (lambda (x) (f (((lambda (f) (lambda (x) ((m-1 f) ((n f) x)))) f) x)))))
; (define (add-m n)
;   (lambda (f) (lambda (x) (f ((lambda (x) ((m-1 f) ((n f) x))) x)))))
; (define (add-m n)
;   (lambda (f) (lambda (x) (f ((m-1 f) ((n f) x))))))
; (define (add-m n)
;   (lambda (f) (lambda (x) ((m f) ((n f) x)))))

因而+的定义也就是:

(define (+ m n)
  (lambda (f) (lambda (x) ((m f) ((n f) x)))))

后来逛知乎,感觉这位网友对邱奇计数对解释还挺有意思的:
在这里插入图片描述
扩展练习:区间算数

AlyssaP.Hacker正在设计一个帮助人们求解工程问题的系统。她希望这个系统提供的一个特征是能够去操作不准确的量(例如物理设备的测量参数),这种量具有已知的精度,所以,在对这种近似量进行计算时,得到的结果也应该是已知精度的数值。

电子工程师将会用Alyssa的系统去计算一些电子量。有时他们必须使用下面公式,从两个电阻R1和R2计算出并联等价电阻Rp的值:
R p = 1 1 / R 1 + 1 / R 2 R_{p}= \frac{1}{1/R_{1} + 1/{R_2}} Rp=1/R1+1/R21

此时所知的电阻值通常是由电阻生产厂商给出的带误差保证的值,例如你可能买到一支标明“6.8欧姆误差10%”的电阻,这时我们就只能确定,这支电阻的阻值在6.8 - 0.68 =6.12和6.8 + 0.68 =7.48欧姆之间。这样,如果将一支6.8欧姆误差10%的电阻与另一支4.7欧姆误差5%的电阻并联,这一组合的电阻值可以在大约2.58欧姆(如果两支电阻都有最小值)和2.97欧姆(如果两支电阻都是最大值)之间。

Alyssa的想法是实现一套“区间算术”,即作为可以用于组合“区间”(表示某种不准确量的可能值的对象)的一组算术运算。两个区间的加、减、乘、除的结果仍是一个区间,表示的是计算结果的范围。

Alyssa假设有一种称为“区间”的抽象对象,这种对象有两个端点,一个下界和一个上界。她还假定,给了一个区间的两个端点,就可以用数据构造函数make-interval构造出相应的区间来。Alyssa首先写出了一个做区间加法的过程,她推理说,和的最小值应该是两个区间的下界之和,其最大值应该是两个区间的上界之和:

(define (add-interval x y)
    (make-interval (+ (lower-bound x) (lower-bound y))
                   (+ (upper-bound x) (upper-bound y))))

尝试把上述过程直译为汉语:

(定义 (区间加法 被加区间 加区间)
    (创建区间 (+ (下界 被加区间) (下界 加区间))
             (+ (上界 加区间) (上界 被加区间))))

Alyssa还找出了这种界的乘积的最小和最大值,用它们做出了两个区间的乘积(min和max是求出任意多个参数中的最小值和最大值的基本过程)。

(define (mul-interval x y)
    (let ((p1 (* (lower-bound x) (lower-bound y)))
          (p2 (* (lower-bound x) (upper-bound y)))
          (p3 (* (upper-bound x) (lower-bound y)))
          (p4 (* (upper-bound x) (upper-bound y))))
      (make-interval (min p1 p2 p3 p4)
                     (max p1 p2 p3 p4))))

尝试把上述过程直译为汉语:

(定义 (区间乘法 被乘区间 乘区间)
    (命 ((甲 (* (下界 被乘区间) (下界 乘区间)))
         (乙 (* (下界 被乘区间) (上界 乘区间)))
         (丙 (* (上界 被乘区间) (下界 乘区间)))
         (丁 (* (上界 被乘区间) (上界 乘区间))))
      (创建区间 (取最小值 甲 乙 丙 丁)
               (取最大值 甲 乙 丙 丁))))

为了做出两个区间的除法,Alyssa用第一个区间乘上第二个区间的倒数。请注意,倒数的两个界限分别是原来区间的上界的倒数和下界的倒数:

(define (div-interval x y)
    (mul-interval x
                  (make-interval (/ 1.0 (upper-bound y))
                                 (/ 1.0 (lower-bound y)))))

尝试把上述过程直译为汉语:

(定义 (区间除法 被除区间 除区间)
    (区间乘法 被除区间
            (创建区间 (/ 1.0 (上界 除区间))
                     (/ 1.0 (下界 除区间)))))

练习2.7
Alyssa的程序是不完整的,因为她还没有确定区间抽象的实现。这里是区间构造符的定义:

(define (make-interval a b) (cons a b))

尝试把上面这句直译为汉语:

(定义 (创建区间 始 终) (序对 始 终))

请定义选择符upper-bound和lower-bound,完成这一实现。

(define (make-interval a b) (cons a b))

(define (upper-bound interval)
    (max (car interval) (cdr interval)))

(define (lower-bound interval)
    (min (car interval) (cdr interval)))

尝试把上述过程直译为汉语:

(定义 (创建区间 始 终) (序对 始 终))

(定义 (上界 区间)
    (取最大值 (前项 区间) (后项 区间)))

(定义 (下界 区间)
    (取最小值 (前项 区间) (后项 区间)))

练习2.8
通过 类似于Alyssa的推理,说明两个区间的差应该怎样计算。请定义出相应的减法过程sub-interval。


我们需要模仿除法的处理,减法就是把减去区间的两个端点取相反数构造新区间与被减区间进行加法运算:

(define (make-interval a b) (cons a b))

(define (upper-bound i)
  (max (car i) (cdr i)))

(define (lower-bound i)
  (min (car i) (cdr i)))

(define (add-interval x y)
  (make-interval (+ (lower-bound x) (lower-bound y))
                 (+ (upper-bound x) (upper-bound y))))

(define (inv-interval i)
  (make-interval (- (lower-bound i))
                 (- (upper-bound i))))

(define (sub-interval x y)
  (add-interval x (inv-interval y)))

尝试把上述过程直译为汉语:

(定义 (创建区间 始 终) (序对 始 终))

(定义 (上界 区间)
    (取最大值 (前项 区间) (后项 区间)))

(定义 (下界 区间)
    (取最小值 (前项 区间) (后项 区间)))

(定义 (区间加法 被加区间 加区间)
    (创建区间 (+ (下界 被加区间) (下界 加区间))
             (+ (上界 被加区间) (上界 加区间))))

(定义 (创建减区间 区间)
  (创建区间 (- (下界 区间))
           (- (上界 区间))))

(定义 (区间减法 被减区间 减区间)
  (区间加法 被减区间 (创建减区间 减区间)))

练习2.9
区间的宽度就是其上界和下界之差的- -半。区间宽度是有关区间所描述的相应数值的非确定性的一种度量。对于某些算术运算,两个区间的组合结果的宽度就是参数区间的宽度的函数,而对其他运算,组合区间的宽度则不是参数区间宽度的函数。证明两个区间的和(与差)的宽度就是被加(或减)的区间的宽度的函数。举例说明,对于乘和除而言,情况并非如此。


说明
I I I 表示区间
u ( I ) u(I) u(I) 表示 I I I 的上界
l ( I ) l(I) l(I) 表示 I I I 的下界
w ( I ) w(I) w(I) 表示 I I I 的宽度
+ , − , × , / +, -, \times, / +,,×,/ 可用于区间
w ( I ) w(I) w(I) 的定义如下:

w ( I ) = u ( I ) − l ( I ) 2 w(I) = \frac{u(I) - l(I)}{2} w(I)=2u(I)l(I)
对于加法:

u ( I 1 + I 2 ) = u ( I 1 ) + u ( I 2 ) u(I_1 + I_2) = u(I_1) + u(I_2) u(I1+I2)=u(I1)+u(I2)
l ( I 1 + I 2 ) = l ( I 1 ) + l ( I 2 ) l(I_1 + I_2) = l(I_1) + l(I_2) l(I1+I2)=l(I1)+l(I2)
因此:

w ( I 1 + I 2 ) = u ( I 1 + I 2 ) − l ( I 1 + I 2 ) 2 = u ( I 1 ) − l ( I 1 ) + u ( I 2 ) − l ( I 2 ) 2 = w ( I 1 ) + w ( I 2 ) \begin{aligned} w(I_1 + I_2) &= \frac{u(I_1 + I_2) - l(I_1 + I_2)}{2} \\ &= \frac{u(I_1) - l(I_1) + u(I_2) - l(I_2)}{2} \\ &= w(I_1) + w(I_2) \end{aligned} w(I1+I2)=2u(I1+I2)l(I1+I2)=2u(I1)l(I1)+u(I2)l(I2)=w(I1)+w(I2)
对于减法:

w ( I 1 − I 2 ) = w ( I 1 + ( − I 2 ) ) = u ( I 1 + ( − I 2 ) ) − l ( I 1 + ( − I 2 ) ) 2 = u ( I 1 ) − l ( I 1 ) + u ( − I 2 ) − l ( − I 2 ) 2 = w ( I 1 ) + w ( I 2 ) \begin{aligned} w(I_1 - I_2) &= w(I_1 + (-I_2)) \\ &= \frac{u(I_1 + (-I_2)) - l(I_1 + (-I_2))}{2} \\ &= \frac{u(I_1) - l(I_1) + u(-I_2) - l(-I_2)}{2} \\ &= w(I_1) + w(I_2) \end{aligned} w(I1I2)=w(I1+I2))=2u(I1+(I2))l(I1+(I2))=2u(I1)l(I1)+u(I2)l(I2)=w(I1)+w(I2)
对于乘法,要证明不是被乘区间宽度的函数,只需证明被乘区间宽度确定时,乘积区间的宽度不定。

不妨取第一个区间为 [ 0 , 1 ] [0, 1] [0,1], 第二个区间分别为 [ 0 , 1 ] [0, 1] [0,1] [ 1 , 2 ] [1, 2] [1,2],此时,乘积区间的宽度分别为 0.5 0.5 0.5 1 1 1,因此,乘积区间的宽度不是被乘区间宽度的函数。

对于除法,证明过程与乘法类似。

练习2.10
Ben Bitdiddle是个专业程序员,他看了Alyssa工作后评论说,除以一个横跨0的区间的意义不清楚。请修改Alyssa的代码,检查这种情况并在出现这一情况时报错。

只需修改除法的实现,在其中加一行判断被除区间有没有跨0,如果有的话输出错误信息即可:

(define (div-interval x y)
  (if (and (> (upper-bound y) 0)
           (< (lower-bound y) 0))
      (error "Not support interval cross 0 -- Div-Interval")
      (mul-interval x
                    (make-interval (/ 1.0 (upper-bound y))
                                   (/ 1.0 (lower-bound y))))))

尝试把上述过程直译为汉语:

(定义 (区间除法 被除区间 除区间)
  (如果 (&& (> (上界 除区间) 0)
           (< (下界 除区间) 0))
      (错误 "区间除法中除区间不能跨过0")
      (区间乘法 被除区间
                    (创建区间 (/ 1.0 (上界 除区间))
                             (/ 1.0 (下界 除区间))))))

练习2.11

在看了这些东西之后,Ben又说出了下面这段有些神秘的话:“通过监测区间的端点,有可能将mu1-interval分解为9种情况,每种情况中所需的乘法都不超过两次”。请根据Ben的建议重写这个过程。

(define (make-interval a b) (cons a b))

(define (upper-bound i)
    (max (car i) (cdr i)))

(define (lower-bound i)
    (min (car i) (cdr i)))

(define (mul-interval x y)
    (let ((ux (upper-bound x))
          (lx (lower-bound x))
          (uy (upper-bound y))
          (ly (lower-bound y)))
      (if (<= ux 0)
          (cond ((<= uy 0)
                 (make-interval (* ux uy) (* lx ly)))
                ((>= ly 0)
                 (make-interval (* lx uy) (* ux ly)))
                (else
                 (make-interval (* lx uy) (* lx ly))))
      (if (>= lx 0)
          (cond ((<= uy 0)
                 (make-interval (* ux ly) (* lx uy)))
                ((>= ly 0)
                 (make-interval (* lx ly) (* ux uy)))
                (else 
                 (make-interval (* ux ly) (* ux uy))))
      (if (and (< lx 0) (> ux 0))
          (cond ((<= uy 0)
                 (make-interval (* ux ly) (* lx ly)))
                ((>= ly 0)
                 (make-interval (* lx uy) (* ux uy)))
                (else 
                 (make-interval (min (* lx uy) (* ux ly))
                                (max (* lx ly) (* ux uy))))))))))

(define (display-interval i)
    (newline)
    (display "(")
    (display (lower-bound i))
    (display ",")
    (display (upper-bound i))
    (display ")"))

(define x (make-interval -1 -2))
(define y (make-interval -4 -3))
(display-interval (mul-interval x y))

(exit)

尝试把上述过程直译为汉语:

(定义 (创建区间 始 终) (序对 始 终))

(定义 (上界 区间)
    (取最大值 (前项 区间) (后项 区间)))

(定义 (下界 区间)
    (取最小值 (前项 区间) (后项 区间)))

(定义 (区间乘法 线区间 面区间)
    (命 ((线区间上界 (上界 线区间))
         (线区间下界 (下界 线区间))
         (面区间上界 (上界 面区间))
         (面区间下界 (下界 面区间)))
      (如果 (<= 线区间上界 0)
          (情况符合 ((<= 面区间上界 0)
                    (创建区间 (* 线区间上界 面区间上界) (* 线区间下界 面区间下界)))
                   ((>= 面区间下界 0)
                    (创建区间 (* 线区间下界 面区间上界) (* 线区间上界 面区间下界)))
                   (否则
                    (创建区间 (* 线区间下界 面区间上界) (* 线区间下界 面区间下界))))
      (如果 (>= 线区间下界 0)
          (情况符合 ((<= 面区间上界 0)
                    (创建区间 (* 线区间上界 面区间下界) (* 线区间下界 面区间上界)))
                   ((>= 面区间下界 0)
                    (创建区间 (* 线区间下界 面区间下界) (* 线区间上界 面区间上界)))
                   (否则 
                    (创建区间 (* 线区间上界 面区间下界) (* 线区间上界 面区间上界))))
      (如果 (&& (< 线区间下界 0) (> 线区间上界 0))
          (情况符合 ((<= 面区间上界 0)
                    (创建区间 (* 线区间上界 面区间下界) (* 线区间下界 面区间下界)))
                   ((>= 面区间下界 0)
                    (创建区间 (* 线区间下界 面区间上界) (* 线区间上界 面区间上界)))
                   (否则 
                    (创建区间 (取最小值 (* 线区间下界 面区间上界) (* 线区间上界 面区间下界))
                            (取最大值 (* 线区间下界 面区间下界) (* 线区间上界 面区间上界))))))))))

(定义 (输出区间 区间)
      (换行)
      (显示 "(")
      (显示 (下界 区间))
      (显示 ",")
      (显示 (上界 区间))
      (显示 ")"))

(定义 线区间 (创建区间 -1 -2))
(定义 面区间 (创建区间 -4 -3))
(输出区间 (区间乘法 线区间 面区间))

(退出)

在排除了自己程序里的错误之后,Alyssa给一个可能用户演示自己的程序。那个用户却说她的程序解决的问题根本不对。他希望能够有一个程序,可以用于处理那种用一个中间值和一个附加误差的形式表示的数,也就是说,希望程序能处理 3.5 ± 0.15 3.5 \pm 0.15 3.5±0.15而不是[3.35,3.65]。Alyssa回到自己的办公桌来纠正这一问题,另外提供了一个构造符和一个选择符:

(define (make-center-width c w)
    (make-interval (- c w) (+ c w)))
(define (center i)
    (/ (+ (lower-bound i) (upper-bound i)) 2))
(define (width i)
    (/ (- (upper-bound i) (lower-bound i)) 2))
(定义 (构建误差区间 中间值 允许误差)
    (创建区间 (- 中间值 允许误差) (+ 中间值 允许误差)))
(定义 (中点 区间)
    (/ (+ (下界 区间) (上界 区间)) 2))
(定义 (半宽 区间)
    (/ (- (上界 区间) (下界 区间)) 2))

不幸的是,Alyssa的大部分用户是工程师,现实中的工程师经常遇到只有很小的不准确性的测量值,而且常常是以区间宽度对区间中点的比值作为度量值。他们通常用的是基于有关部件的参数的百分数描述的误差,就像前面描述电阻值的那种方式一样。

练习2.12
请定义一个构造函数make-center-percent,它以一个中心点和一个百分比为参数,产生出所需要的区间。你还需要定义选择函数percent,通过它可以得到给定区间的百分数误差,选择函数center与前面定义的一样。

(define (make-interval a b) (cons a b))

(define (upper-bound i)
  (max (car i) (cdr i)))

(define (lower-bound i)
  (min (car i) (cdr i)))

(define (center i)
  (/ (+ (lower-bound i) (upper-bound i)) 2))

(define (width i)
  (/ (- (upper-bound i) (lower-bound i)) 2))

(define (make-center-percent c p)
  (let ((w (* c p)))
    (make-interval (- c w) (+ c w))))

(define (percent i)
  (/ (width i) (center i)))

尝试把上述过程直译为汉语:

(定义 (创建区间 始 终) (序对 始 终))

(定义 (上界 区间)
  (取最大值 (前项 区间) (后项 区间)))

(定义 (下界 区间)
  (取最小值 (前项 区间) (后项 区间)))

(定义 (中间值 区间)
  (/ (+ (下界 区间) (上界 区间)) 2))

(定义 (半宽 区间)
  (/ (- (上界 区间) (下界 区间)) 2))

(定义 (构建百分比误差率区间 中间值 误差率)
  (命 ((误差值 (* 中间值 误差率)))
    (创建区间 (- 中间值 误差值) (+ 中间值 误差值))))

(定义 (百分数误差值 区间)
  (/ (半宽 区间) (中间值 区间)))

练习 2.13
请证明,在误差为很小的百分数的条件下,存在着一个简单公式,利用它可以从两个被乘区间的误差算出乘积的百分数误差值。你可以假定所有的数为正,以简化这一问题。


假设所有的数为正, c 1 , c 2 , w 1 , w 2 , p 1 , p 2 c1, c2, w1, w2, p1, p2 c1,c2,w1,w2,p1,p2分别为两个被乘区间的中点、宽度、百分比误差, l , u , w , c l, u, w, c l,u,w,c分别为乘积区间的下界、上界、宽度、中点,则

l = ( c 1 − w 1 ) ( c 2 − w 2 ) u = ( c 1 + w 1 ) ( c 2 + w 2 ) w = u − l 2 c = u + l 2 l = (c_{1} - w_{1})(c_{2} - w_{2})\\ u = (c_{1} + w_{1})(c_{2} + w_{2}) \\ w = \frac{u - l}{2} \\ c = \frac{u + l}{2} l=(c1w1)(c2w2)u=(c1+w1)(c2+w2)w=2ulc=2u+l

百分比误差:

p = w c = u − l u + l = c 1 w 2 + c 2 w 1 c 1 c 2 + w 1 w 2 = p 1 + p 2 p 1 p 2 + 1 ≈ p 1 + p 2 ( p 1 < < 1 , p 2 < < 1 ) \begin{aligned} p =& \frac{w}{c} \\ =& \frac{u-l}{u+l}\\ =& \frac{c_{1}w_{2} + c_{2}w_{1}}{c_{1}c_{2} + w_{1}w_{2}} \\ =& \frac {p_{1} + p_{2}}{p_{1}p_{2} + 1} \\ \approx & p_{1} + p_{2} (p_1 << 1, p_2 << 1) \end{aligned} p====cwu+lulc1c2+w1w2c1w2+c2w1p1p2+1p1+p2p1+p2(p1<<1,p2<<1)

(未完待续……)

参考文献:
[1] [美]Julie Sussman.计算机程序的构造和解释[M]. 裘宗燕译注.北京:机械工业出版社,1996.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X-jazz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值