计算机程序的构造和解释(原书第2版)中文版 Structure and Interpretation of Computer Programs

程序必须写得能够供人们阅读,偶尔地去供计算机执行。 每个计算机程序都是现实中或者精神中的某个过程的一个模型。

我们在适当的时候隐藏一些细节,通过创建抽象去控制复杂性。

第一章 构造过程抽象

Lisp 语言的名字来自表处理(List Processing),这种非官方的语言演化了很多年,今天的Lisp已经形成了很多方言,用于本书的 Lisp方言名为 Scheme。Lisp语言的重要特征:计算过程的Lisp描述本身又可以作为Lisp的数据来表示和操作。能将过程表示为数据的能力,让Lisp也可以用于编写编译器和解释器。

一般来说,在模拟科学研究或者工程中的现象时,我们总是从最简单的不完全的模型开始。随着更细致的检查这些问题,这些简单的模型变得越来越不合适,从而必须用更精细的模型来取代。

程序设计的基本元素

  • 基本表达形式,用于表示语言所关心的最简单的个体
  • 组合的方法,通过它们可以把简单的东西构造成复合的元素
  • 抽象的方法,通过它们可以为复合对象命名,并将它们当作单元去操作

在程序设计中,需要处理两类要素:过程和数据。这样,任何程序设计语言都必须能表述基本的数据和过程,还要提供对过程和数据进行组合和抽象的方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
(+ 137 347)
486

(- 1000 334)
666

(* 25 4 12)
1200

(+ (* 3 5) (- 10 6))
19

上面这样的表达式称为组合式。最左侧的是运算符(前缀表示),其它元素为运算对象。前缀表示的优点:

  • 适用于可能带有任意个实参的过程
  • 可以直接扩充,允许组合式嵌套

程序语言需要提供一种 通过名字 去使用计算对象的方式,将名字标识符成为 变量,它的值就是它对应的那个对象。

1
2
3
4
(define size 2)

(* 5 size)
10

组合式的求值: (1)求值该组合式的各个子表达式 (2)将作为最左子表达式(运算符)的值的那个过程应用于相应的实际参数

为了实现对一个组合式的求值过程,必须先对组合式里的每个元素执行同样的过程。因此,这个求值是递归的,即它在自己的工作步骤中,包含着调用这个规则本身的需要。递归可以简洁的描述深度嵌套的情况。如果不用递归,则求值过程可以看成一个由多个节点构成的树,运算在端点执行,然后在高层组合起来。递归是处理层次性结构(类似树)的强有力的技术。

复合过程: (define (sauare x) (* x x)) 这样一个复合过程,给它取名为 square,表示平方计算。定义好之后就可以使用了。

1
2
(square (+ 2 5))
49

条件表达式和谓词:

1
2
3
4
5
6
7
(define (abs x)
    (cond
        ((> x 0) x)
        ((< x 0) (-x))
        ((= x 0) 0)
    )
)

cond 符号后面是一些称为子句的表达式对偶(

),第一个是谓词,即这是一个表达式,它的值将被解释为真或者假。先求值谓词

,如果为 false,就求值下一个

,直到结果为 true,此时返回表达式的值。

1
2
3
4
5
6
(define (abs x)
    (cond
        ((< x 0) (-x))
        (else x)
    )
)

else 是一个特殊符号,

1
2
3
4
5
6
(define (abs x)
    (if (< x 0)
        (-x)
        x
    )
)

这里是特殊形式的 if 语句。 除了基本谓词 < = > 这3个之外,还有一些逻辑复合运算,可构造出复合谓词,比如

  • (and )
  • (or )
  • (not )

牛顿法求平方根,逐步逼近法: (PS:比如求8的平方根,用8/2=4,再算44>8,所以4/2=2,22<8,上限为4,下限为2,继续3*3>8,递归过程)