标签:
Q: SICP是讲软件工程么?
A:部分,但并非全部。主要是模块化和黑盒抽象,计算机中两大主要基本思想。SICP关心的是:“当系统复杂度爆炸时(或者在此之前),我们如何通过有效的方法和手段去控制系统的复杂度?”
Q: SICP是讲编译原理么?
A: 部分,另外,如同书名说描述的那样,SICP中的“编译”都是“解释”,这种解释的行为,无外乎就是用一种机器的计算行为去模拟另一种机器。
一个计算机语言并不仅仅是让计算机去执行操作的一种方式,而是一种表达有关方法学的思想的新颖的形式化媒介。最基本的材料不是特定语言的语法,不是有效计算的巧妙算法,不是算法的数学分析活着计算的本质基础,而是一些能够控制大型软件系统的智力复杂度的技术。
计算过程是存在于计算机里的一类抽象事物,在其演化过程中,这些过程会去操作一些成为数据的抽象事物。
一个好的编程语言不只是指挥电脑去实现任务。更是作为一个框架,通过它来组织我们思考实现任务计算的过程。
发现并掌握强有力的组织技术,将提升我们构造大型的重要程序的能力。
心智的活动,除了产生简单的认识外,还有三个方面:
1)将简单认识组合成复杂认识
2)将不同认识进行比照,得到其相互关系的认识
3)将普通认识和其所在其他认识隔离开,所谓“抽象”
概况起来就是个体,整体,关系,抽象,好像马哲里的东西哈~
程序设计的基本元素:
当我们描述一个语言时,要特别注意这种语言所提供的,将简单认识组合形成复杂认识的方法。每一种强有力的语言都为此提供了三种机制:
1. 基本表达形式 : 用于表示语言所关心的最简单的个体。
2. 组合的方式: 通过它们能从简单的元素出发构造出复合的元素。
3. 抽象的方法: 通过它们可以为复合元素命名,并将其看作单元来操作。
数据是一种操作对象,而过程是关于如何操作的规则描述。这样,任何强有力的程序设计语言都必须能表述基本的数据和基本的过程,还需要提供对过程和数据进行组合和抽象的方法。
命名和环境
编程语言必须提供为对象命名的机制,这是最基本的抽象机制。
在Scheme中,有:
(define pi 3.14159)
(define radius 10)
(define circumference (* 2 pi radius)
上面的define就是一个最简单的抽象方法,帮助我们将值与数值关联起来,同时意味着解释器必须具备某种存储能力,存储这种关联,及“环境”。
计算对象的结构可能很复杂,需要通过复杂费时的计算才能得到,如果每次使用时都重新计算,可能费时又费力,给计算得到的结果命名,可以很方便地多次使用。
组合式求值:
1)求该组合式的各子式值(递归,处理层次性结构强有力的工具)
2)通过计算符和计算对象求值
复合过程:
1)数和算术运算是基本的数据和过程。
2)组合式的嵌套提供了一种组合起多个操作的方法。
3)定义是一种受限的抽象手段,它为名字关联相应的值
过程定义是一种威力更加强大的抽下技术,通过它可以为复合操作提供名字,而后就可以将这样的操作作为一个单元使用了。
比如,我们考虑定义平方“一个数的平方即它自身乘以自身”,即:
(define (square x) (* x x))
OK,现在我们有了一个复合过程,它的名字叫square(定义)。
定义好square后,我们就可以把它当做一个单元使用了:
(square 10)
100
(square (+ 2 5))
49
我们还可以吧square作为基本组件去构造其他操作,比如x^2+y^2,可以描述为:
(+ (square x) (square y))
我们把这个操作定义为sum_of_square:
(define (sum_of _square x y) (+ (square x) (square y)))
OK,我们又可以用这个单元去构造更复杂的操作,比如:
(define (f a) (sum_of _square (* a 2) (+ a 1)))
复杂程序通常就是为了计算(构造)出很不容易得到的对象,通过多步构造和命名,分解构造过程,使之比较容易进行,建立名字-对象关联是其中最重要的抽象手段。
过程定义是分解和控制程序复杂性的最重要技术之一。
由简而繁,这就是复合的魅力。
过程应用的代换模型:
组合式和复合过程确定的计算过程是(代换模型):
1)求出各参数表达式(子表达式)的值
2)找到要调用的过程的定义(根据第一个子表达式的求值结果)
3)用求出的实际参数代换过程体里的形式参数
4)求值过程体
例:
(f 5) ;用原过程体 (sum-of-squares (+ a 1) (* a 2)),代换得到:
(sum-of-squares (+ 5 1) (* 5 2)) ;求值实参并代入过程体,得到:
(+ (square 6) (square 10)) ;求值实参并代入过程体,得到:
(+ (* 6 6) (* 10 10))
(+ 36 100)
136
可见,上面是一个不断归约的过程,这个计算过程称为过程应用的代换模型。
代换模型只是为了帮助直观理解过程应用的行为,它并没有反映解释器的实际工作过程,实际解释器基于局部环境实现。
应用序和正则序求值:
像上面例子那样,解释器先求值子表达式(运算符和各运算对象),而后把得到的运算应用于运算对象(实际参数),这种方式称为应用序求值,这也是解释器里实际使用的。
当然很容易还可以想到另一种方式完全展开之后归约)称为正则序求值,例如这样:
(sum-of-squares (+ 5 1) (* 5 2))
(+ (square (+ 5 1)) (square (* 5 2)) )
(+ (* (+ 5 1) (+ 5 1)) (* (* 5 2) (* 5 2)))
;先展开,后归约
(+ (* 6 6) (* 10 10))
(+ 36 100)
136
显然,应用序避免了一些重复计算,效率高一些,这或许也是解释器选择这种实现方式的原因吧。
当然,还可以写个程序来测试下解释器是应用序还是正则序:
(define (p) (p))
(define (test x y)
(if (= x 0)
0
y))
(test 0 (p))
应用序的话,显然会无线循环,而正则序会返回0。
条件表达式和谓词:
到目前为止,我们的定义过程的表达能力还很有限,比如我们我们还无法进行条件检测,而复杂计算的描述中总需要描述条件和选择,比如求绝对值。
看下面这个条件表达形式:
依次求值各个 p(条件),遇到第一个非false的条件后求值对应的 e,以其值作为整个cond 表达式的值。
(cond (<p1> <e1>) ; (<p2> <e2>)
…
(<pn> <en>))
像pn这样的表达式(求出真假值)称为谓词。
Scheme里可以写成这样:
(define (abs x)
(cond ((> x 0) x)
((= x 0) 0)
((< x 0) (- x))))
除了>=<这样的谓词外,各种关系运算符也是基本谓词,比如and,or,not
(and … ) 逐个求值 e,直到某个 e 求出假,或最后一个 e 求值完成。以最后求值的那个子表达式的值作为值
(or … ) 逐个求值 e,直到某个 e 求出真,或最后的 e 求值完成。以最后求值的那个子表达式的值作为值
(not ) 如果 e 的值不是真,就得真,否则得假
可以用 and、or、not 组合出各种复杂逻辑条件,可以用过程定义谓词,比如大于5小于10的条件:
(define (num_5_10 x) (and (> x 5) (< x 10)))
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/kzq_qmi/article/details/47619649