1.1 程序设计的基本元素

编程语言的三种机制:基本表达形式、组合方法、抽象方法。

在程序设计中要处理两类要素:过程和数据,实际上两者并不严格区分,用于构造过程的规则同样适用于操作各种数据。

1.1.1 表达式

前缀表达式-将运算符放在所有运算对象的左边, 完全适用于带有任意个实参的过程, 可以直接扩充, 允许出现组合式嵌套的情况。

读入-求值-打印循环

(+ (* 3
      (+ (* 2 4)
         (+ 3 5)))
   (+ (- 10 7)
      6))

1.1.2 命名环境

使用 define 进行变量命名。

为存储值与变量名的关联, 解析器必须维护某种存储能力, 此即为环境。

(define pi 3.14159)
(define radius 10)
(define circumference (* 2 pi radius))

1.1.3 组合式的求值

组合式的求值是一个递归的过程:

  1. 求值该组合式的各个子表达式。

  2. 将作为最左边表达式(运算符)的值的那个过程应用于相应的实际参数, 而这些参数即为其它子表达式(运算符)的值。

define 作为一个求值规则的特殊形式, 并不是将实际参数作用于 define, 而是为第一个参数(变量)关联第二个参数(值)。

1.1.4 复合过程

(define (square x) (* x x))
(define (sum-of-squares x y)
  (+ (square x) (square y)))

1.1.5 过程应用的代换模型

正则序求值-完全展开而后规约。

应用序求值-先求值参数而后应用。

应用序和正则序的不同,Lisp采用应用序以避免对表达式的重复求值

1.1.6 条件表达式和谓词

;; (cond (<p1> <e1>)
;;       (<p2> <e2>)
;;       ...
;;       (<pn> <en>))
(define (abs x)
  (cond ((> x 0) x)
        ((= x 0) 0)
        ((< x 0) (- x))))
(define (abs x)
  (cond ((< x 0) (- x)
        (else x))))

;; (if <predicate> <consequent> <alternative>)
(define (abs x)
  (if (< x 0)
      (- x)
      x))

练习 1.1

> 10
10
> (+ 5 3 4)
12
> (- 9 1)
8
> (/ 6 2)
3
> (+ (* 2 4) (- 4 6))
6
> (define a 3)
a
> (define b (+ a 1))
b
> (+ a b (* a b))
19
> (= a b)
#f
> (if (and (> b a) (< b (* a b)))
      b
      a)
4
> (cond ((= a 4) 6)
        ((= b 4) (+ 6 7 a))
        (else 25))
16
> (+ 2 (if (> b a) b a))
6
> (* (cond ((> a b) a)
           ((< a b) b)
           (else -1))
     (+ a 1))
16

练习 1.2

> (/ (+ 5
        4
        (- 2 (- 3 (+ 6 (/ 4 5)))))
     (* 3
        (- 6 2)
        (- 2 7)))
-37/150

练习 1.3

中文版翻译有误, 原书此题目为

Define a procedure that takes three numbers as arguments and returns the sum of the squares of the two larger numbers.

所以应该是: 返回其中较大两个数的平方和

;; 定义求最小值的函数, 采用 if 方式实现
(define (min-num a b c)
  (if (< a b)
      (if (< a c) a c)
      (if (< b c) b c)))

;; 利用前面的函数和 cond 表达式实现
(define (sum-max a b c)
  (cond ((= (min-sum a b c) a) (sum-of-squares b c))
        ((= (min-sum a b c) b) (sum-of-squares a c))
        (else (sum-of-squares a b))))

练习 1.4

给出的函数实现的功能是: 接受两个参数, 返回第一个参数与第二个参数绝对值的和

(define (a-plus-abs-b a b)
  ((if (> b 0) + -) a b))

练习 1.5

(define (p) (p))
(define (test x y)
  (if (= x 0)
      0
      y))

对于应用序, “先对参数求值然后应用”, 则对 p 的调用将会陷入死循环;

对于正则序, “完全展开而后进行规约”, 则对 test 展开时对 if 语句判断为真后直接返回 0, 从而避免了对 p 的调用。

1.1.7 实例: 采用牛顿法求平方根

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

(define (improve guess x)
  (average guess (/ x guess)))

(define (average x y)
  (/ (+ x y) 2))

(define (good-enough? guess x)
  (< (abs (- (squre guess) x)) 0.0001))

(define (squre x)
  (* x x))

(define (sqrt x)
  (sqrt-iter 1.0 x))

练习 1.6

对于自定义的 new-if, 根据 Lisp 的应用序求值原则, 不论 predicate 真假, 两个参数 then-clauseelse-clause 都会被求值, 这样将会导致陷入对 sqrt-iter 的递归调用, 进而因递归调用的栈深度超过阀值而导致程序崩溃。

练习 1.7

在被求值的数很小或很大时,因为变化程度很小会导致程序进入死循环

修改后的程序如下:

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

(define (improve guess x)
  (average guess (/ x guess)))

(define (average x y)
  (/ (+ x y) 2))

(define (good-enough? old_guess new_guess)
  (< (/ (abs (- old_guess new_guess))
        old_guess)
     0.0001))

(define (squre x)
  (* x x))

(define (sqrt x)
  (sqrt-iter 1.0 (improve 1.0 x) x))

练习 1.7

仿照实例中的实现以及上一题目中的改进之处

(define (square x) (* x x))

(define (cube x) (* x x x))

(define (good-enough old_guess new_guess)
  (< (/ (abs (- old_guess new_guess))
        old_guess)
     0.0001))

(define (improve guess x)
  (/ (+ (/ x (square guess)) (* 2 guess)) 3))

(define (cube-root-iter old_guess new_guess x)
  (if (good-enough old_guess new_guess)
      new_guess
      (cube-root-iter new_guess (improve new_guess x) x)))

(define (cube-root x)
  (cube-root-iter 1.0 (improve 1.0 x) x))

1.1.8 过程作为黑箱抽象

一个问题可以分解成若干个子问题, 而这些子问题因为可以清楚标明自身的工作, 因此可以构成定义其它过程的模块。

一个过程的定义应该能隐藏具体实现的细节, 使得调用者不必自己去实现这些过程, 而是作为一个黑箱接受它。

过程的形式参数作为约束变量, 作用域为定义过程的过程体。

使用内部定义和块结构使子过程局部化, 避免与过程外的环境发生冲突。

(define (average x y)
  (/ (+ x y) 2))

(define (sqrt x)
  (define (good-enough? guess x)
    (< (abs (- (squre guess) x)) 0.0001))
  (define (improve guess x)
    (average guess (/ x guess)))
  (define (sqrt-iter guess x)
    (if (good-enough? guess x)
      guess
      (sqrt-iter (improve guess x) x)))
  (sqrt-iter 1.0 x))
comments powered by Disqus