【SICP归纳】1 过程和代换模型

版权声明:转载请联系本人,感谢配合!本站地址:http://blog.csdn.net/nomasp https://blog.csdn.net/NoMasp/article/details/44229147

《计算机程序的构造和解释》这本书的目标并不是讲解一门编程语言的语法等,它是一种方法。不是在向你陈述知识,而是在教你如何做到想要做的东西。它是一个过程,一个精神。这些引导过程的东西就是所谓的程序规则的模式。

书中用了许多的例子来诠释书名,我才疏学浅就不再举例往博客上推了,仅仅归纳一点总结而已。

如果说程序是一种法术,那么控制神奇的法术就是过程。因此我们需要一门叫做Lisp的语言来唤醒这种精神。从书中的讲解中我们发现,Lisp语言的语法并不多,要记住这些语法并不难,但要理解这些规则的含义以及如何使用这些规则,则是要上很长的时间去修炼。深入理解了这些规则便会使你成为更优秀的程序员。

在计算机科学中,这并不是要告诉人们该如何算平方根,作者也说了如果这是计算机科学的全部,那么便没什么了不起的。真正的问题是,当我们试图建立非常大的系统时,这通常是几千页长的计算机程序。

之所以能完成这种庞大的工程,是因为有一些技术,用于控制这些大型系统的复杂性。而如果控制复杂性也是这门课的重点,某种意义上来说,这也解释了什么是真正的计算机科学。

作者举了一个例子,当一名工程师正在设计一个由很多实体构成的物理系统时。担心这个系统的工程师不得不去解决容许误差和噪音的问题。而我是一个电气工程师,我可以去很容易的建立起一级或二级的放大器,你也可以设想建造一百万个放大器的级联。但是去建造这样一种东西是非常荒唐的事情,因为在建造这一百万个放大器的很长时间之前,这些组件的噪音会得到放大,以至于这整个工程没有了意义。

计算机科学负责处理理想化的组件,我们很清楚的知道我们在将这些小程序和数据块放到一起。我们不必担心误差。那意味着在建立一个庞大的程序时,这和我能建立和我能想象的没有什么不同。因为这些部分是我知道我想要的抽象的实体。

很显然工程师是非常准确的,因此相对于其他类型的工程,你可以建立约束物理系统的限制,建立噪音和近视的限制,实行建设大型软件系统的限制是我们的思想局限。所以从这一点来看,计算机科学就像是工程的一个抽象形式。用在所有的工程中的一种技术,叫做黑盒抽象。

如果说求出X的平方根可能是一个复杂的整体的一套规则,那么最终会有,比如36的平方根,得出6。而真正重要的是,如果我想计算A的平方根加上B的平方根,那么你便可以用这个规则,并且将其看作一个无需知道内容的模块。因为从我们的角度来看,黑箱内有什么并不重要,因为我们只是想要知道他们的平方根的和。这在C等各种语言中都有体现。比如给一个函数传入一个参数,我们并不需要知道这个函数的具体操作,只是需要它们的代码,当然了,这里所说的函数是别人写好的库。

书中有一个求平方根的例子,为了去做些什么,我们需要去做一个猜想,并且不断的去改进它。这里我们可以做一个黑盒,上面写着“平方根”。这里是一个过程,它本身就是关于通用的战略的总体战略第一个关键——将黑盒抽象。将原始的程序和原始的数据结合起来作出更复杂的事情,这里就是说的组合。这是通过定义程序和处理数据抽象的复合数据的技术来完成。我们要用到的是一种叫做高阶函数的东西,它的输出和输出都是自己的程序。

如果用1和3乘以2,将会得到8。但当我们考虑所谓的线性组合的总体思路时,便可以很容易的添加两件事情和别的东西相乘。例如将向量a1和向量a2,通过一些因子来扩展它们,并且得到另一个向量。我们也可以考虑有两个多项式A1和A2,可能会用2或者其他的数值来乘以这两个多项式。又或者A1和A2可能是电信号,我们可能需要组合这两个电信号并且将其放大。

这就是前面所说的通用的战略的总体战略的第二个关键——常规接口。要做到控制复杂性,就需要建立常规的接口,将约定的东西放到一起。当在谈论真正的大尺度结构时,常规接口在现世界中建模赋值的系统时非常重要。这样的系统有两个非常重要的隐喻,一个被称为OOP(面向对象编程),将我们的系统分为许多小东西,它们之间发送信息交互。另一个称为流,在那里我们就像电气工程师将拼起电气系统一样拼起大型系统。

第三个关键则是基于技术控制的复杂性来构造新的语言——也就是书中第四章所说的元语言。因为当面临一个不堪重负的复杂设计时,也许选择一个新的设计语言是控制这种复杂性的很好方式。而新设计语言的目的是突出了系统的不同方面,它可以一直某些类型的细节,而强调其他种类的详细信息。这是这本书最神奇的部分,而建立新语言的第一步就是用Lisp过程来解释Lisp自身。或许更加神秘的则是仿佛Lisp有两个巨大的车轮——应用和循环。在书中封面你将会看到,它们分别是apply和eval。在我们后面的学习过程中将学习元语言抽象,这将帮助我们构建新的语言。所谓的逻辑编程语言,就是你并不需要研究输入输出的过程,需要研究的则是它们之间的关系。

在第一章我认为非常重要的一点是lambda和define之间的关系,这在第41页中有详细描述。

(define (plus4 x) (+ x 4))

等价于

(define plus4 (lambda (x) (+ x 4)))

要时刻记住这一点,因为在后面的复杂的程序中,会有很多个define出现,并仅仅有

(define (plus x) (+ x 4))

也有

(define plus (+ 4 4))

理解以下这两段代码非常有必要。

(define A 
  (* 5 5))
(define (D)
  (* 5 5))

在这里,

A-->25
D-->compound procedure
(D)-->25
(A)-->error
(define A 
  (* 5 5))
;Value: a

A
;Value: 25

(A)
;The object 25 is not applicable.
;To continue, call RESTART with an option number:
; (RESTART 2) => Specify a procedure to use in its place.
; (RESTART 1) => Return to read-eval-print level 1.
;Start debugger? (y or n): n

(define (D)
  (* 5 5))
;Value: d

D
;Value 12: #[compound-procedure 12 d]

(D)
;Value: 25

程序员通过构造程序和表达式来构建一个法术,而这些法术在某种程度上直接通过一个过程来实现这一目标。为了有效的做到这一点,我们必须要明白所写的特定事物之间的关系,这些特定的咒语,还有我们试图控制的过程的行为。

在这里我们有lambda,有definitions,有conditionals,还有combinations。怎样去求值组合呢?但真正需要组合起符号和数字的时候,数字会对自身求值,而在代换模型中符号将会消失不见。它们不会出现,直至你需要它们。如何来看求值一个应用程序的规则呢,当求值一个组合的时候,它有几个部分——运算符和操作数。运算符会返回到程序,如果求值运算符,我们就会得到一个过程,比如+运算符。替代过程的形式参数提供的参数,形式参数在过程的声明中得到定义。这样我们就可以来求值一个新的实体,这个实体通过用代换模型来复制旧的实体。

即便是对于简单的加法乘法,无论你深入到怎样的细节中去,在一台机器中,你都会发现有更深入的细节。因此我们要学会的便是忽视细节,理解复杂问题的关键是知道什么不用看、什么不用计算还有什么不用考虑。书中关于如果通过代换模型来计算3和4的乘积和已经非常完美了,通过这个例子我们已经看到代换模型的规则,那就是当求值运算符时会得到一个过程,当求值操作数时,如果还没有做应用,则仅仅是得到一个参数。也就是说对于(* 3 (+ 1 4)),(+ 1 4)就相当于是乘法运算符的操作数,在对其求值的时候,如果还没有得到应用,其仅仅是乘法运算符的参数而已。而对于应用这个词,应用序和正则序则是非常重要的概念。

通过代换模型去命名这些事物以及这些表达式。Gerald Jay Sussman教授说,每一个巫师都会告诉你,如果你有命名的精神,你就有了权力。



感谢访问,希望对您有所帮助。 欢迎关注或收藏、评论或点赞。


为使本文得到斧正和提问,转载请注明出处:
http://blog.csdn.net/nomasp


没有更多推荐了,返回首页