Computer Science
Algorithm
Data Processing
Digital Life
Distributed System
Distributed System Infrastructure
Machine Learning
Operating System
Android
Linux
Tizen
Windows
iOS
Programming Language
C++
Erlang
Go
Scala
Scheme
SICP第三章总结(上)——可变量与环境 (2012)
Type System
Software Engineering
Storage
UI
Flutter
Javascript
Virtualization
Life
Life in Guangzhou (2013)
Recent Works (2013)
东京之旅 (2014)
My 2017 Year in Review (2018)
My 2020 in Review (2021)
十三年前被隔离的经历 (2022)
A Travel to Montreal (2022)
My 2022 in Review (2023)
Travel Back to China (2024)
Projects
Bard
Blog
RSS Brain
Scala2grpc
Comment Everywhere (2013)
Fetch Popular Erlang Modules by Coffee Script (2013)
Psychology
耶鲁大学心理学导论 (2012)
Thoughts
Chinese
English

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元。

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