Table of Contents

  1. 一、变量的赋值
  2. 二、环境
  3. 三、面向对象编程

SICP第三章总结(上)——可变量与环境

Posted on 27 Jan 2012, tagged sicpprogramming

因为各种事情,中间隔了很长时间没有更新有关SICP的总结了。因为第三章涉及到的东西实在太多,一篇文章来总结完似乎不太现实,因此将第三章的总结分为两个部分,上半部分是有关可变的变量和环境,下半部分是流编程。我刚开始看SICP的时候,是为了学lisp,但是看到第三章才真正意识到SICP的重点是什么,看它的书名就知道,这不是一本讲解一门语言的书,而是讲有关程序构造和解释的书,对语言的构造了解多一些,不管用什么语言,都很容易用上其中的思想。在第三章,作者深入讨论了在命令式语言中我们习以为常的事情——赋值所带来的影响。虽然这不是一本讲解编程语言的书,但是由于前几篇博客都是介绍了语法,我们还是遵守这种习惯吧,继续讲解Scheme的语法吧。这本书讲的不是语言,但是语言在这本书中还是一个很重要的工具的。其实所谓Scheme的语法,只是一些保留字,方便我以后查询。

一、变量的赋值

在SICP前面的学习中很多人肯定发现了,在Scheme中,基本上所有的操作都是不改变变量原有的值。比如一个函数的作用是将一个列表中的值反转,那么它只是返回这个列表反转后的值,而不是改变这个列表。因为在数学的世界里,一个函数也好,变量也好,它所代表的值不应该总是改变的。但是在现实世界中,一些状态却总是随时间改变,如果语言支持赋值,也许在写有关真实世界的模型时就会方便一些。但是我们将会看到,这种方便所带来的一些麻烦。

首先还是先介绍一下有关变量赋值的语法:

1.set!

给一个变量赋值,语法为(set! <arg1> <arg2>),就是将arg1赋值为arg2。在Scheme中,与赋值有关的函数,基本后面都是跟一个感叹号的。

2.set-car!

给一个列表的头赋值。语法和set!类似。

3.set-cdr!

给一个列表的cdr赋值。类似于set-car。

随意乱用set-car!和set-cdr!有可能使列表的关系变得很乱,如果学过数据结构中的链表和C语言中的指针,对这个的理解应该会比较深一些。

根据SICP的一贯思想,只要有基础的功能,那么别的功能也都能实现。set!就是以前所没有的一个基础功能,而set-car!和set-cdr!都可以通过set!来实现。SICP中有具体的实现过程,这里就不再多说了。

可以改变变量的值,使得一个变量只是某段代码的替代,变成了代表指向某个存储地址。在很多情况下,并不需要对元素进行赋值,而对元素赋值,有可能使代码的实现不甚优雅,程序的效率变低,更有可能给程序造成一些隐患,这个在书中也有一些例子。那能给变量赋值的好处在哪里呢?就是我们可以用时间来思考程序中的数据了,像在真实世界中一样,一些值可以随着时间的改变而改变,这样可以更好的模拟真实世界。为了可以使变量的改变更好的被解释器所处理,引入了“环境”的概念。

二、环境

环境是一个比较关键的解释模型。前面所介绍的解释模型是直接替换函数的代码和参数,但是由于变量变成了可以改变的,所以以前的模型已经不适用了。环境的模型可以解释很多东西,包括类似于我们在其它语言中学过的局部变量。通过环境,我们可以实现面向对象编程,声明一个类,用这个类来声明很多互不关联的实体。所谓环境,可以把它的结构想象成一个序列,这在书中讲的都很详细了,我曾经写了好几次,想解释一下环境模型,但是写简单了不可能解释得清楚,写复杂了又远远不如原书中所讲的好,所以就只好作罢。

虽然由于我水平有限不能总结太多关于环境的东西,但这个真的是一个很重要的模型,也是Scheme解释器工作的真实原理,多看原书就会有比较深的理解。

三、面向对象编程

不知道SICP成书的时候有没有“面向对象”这个词语。而书中的英文我也不知道怎么翻译好,但是根据它的思想,我索性就写做面向对象编程好了。说到这里也稍微闲聊一下关于面向对象编程吧。很多语言号称是支持面向对象编程,Java更是强制把所有的东西都封装成类。但是我觉得,如果一个好的程序员,用什么语言,都会有模块化的思想的,而让编程语言强制规定这些,反而限制了程序员的自由。也许是因为Scheme的面向对象更透明更神奇的原因,让我突然想到了柏拉图世界中的观点,即每种物体在理想世界中都有一个标准,真实世界中的物体都是依靠这个标准而来的。而类的声明就像是这个“元物体”,而通过类新建的对象就像是真实世界中的物体。

好了,废话少说,这里我们看一下Scheme是怎样实现面向对象的模块化设计的。其实这篇文章所介绍的新东西只有上面两节,这里只是一种思想。

不要认为一个类很神奇,其实它们也都是用代码来实现的。通过那些只有逻辑功能的代码,我们就可以创造一个类。比如用下面的代码,我们可以创造一个银行账户的类:

(define (make-account balance)
    (define (withdraw amount)
        (if (>= balance amount)
            (begin (set! balance (- balance amount))
                balance)
            "Insufficient funds"))
    (define (deposit amount)
        (set! balance (+ balance amount))
            balance)
    (define (dispatch m)
        (cond ((eq? m 'withdraw) withdraw)
            ((eq? m 'deposit) deposit)
            (else (error "Unknown request -- MAKE-ACCOUNT"
                m))))
    dispatch)
(define A1 (make-account 100))

即可生成一个初始有100元、名为A1的银行账户,

((A1 'dispath) 100)

即可向其中存入100元。

不需要额外的知识,我们只要仔细的研读上面的代码,就可以知道这种面向对象的方法是怎样实现的。从此我们可以看出,除了我们的思想,不需要额外的东西,即可实现面向对象的编程。

comments powered by Disqus