设为首页 加入收藏

TOP

SICP:赋值和局部状态(Python实现)(一)
2023-07-23 13:45:40 】 浏览:103
Tags:SICP Python 实现

示例代码我已经上传到了GitHub仓库:SICP-Python(包括本书其它章节的示例代码),感兴趣的童鞋可以前往查看。

即使在变化中,它也丝毫未变。

——赫拉克利特

吾犹昔人,非昔人也。

——僧肇

前面我们介绍了组成程序的各种基本元素,看到了如何把基本过程和基本数据组合起来,构造出复合的实体。不过对于设计程序而言,这些手段还不够,我们还需要一些能够帮助我们构造起模块化(modular)的大型系统的策略。所谓模块化,也即使这些系统能够“自然地”划分为一些内聚(coherent)的部分,使这些部分可以分别进行开发和维护。

在哲学上,组织程序的方式与我们对被模拟系统的认识息息相关。接下来我们要研究两种特色很鲜明的组织策略,它们源自于对于系统结构的两种非常不同的“世界观”(world views)。

  • 第一种策略将注意力集中在对象(objects)上,将一个大型系统看成不同对象的集合,它们的状态和行为可能随着时间不断变化。

  • 另一种组织策略将注意力集中在流过系统的信息流(streams of information)上,非常像EE工程师观察一个信号处理系统。

这两种策略都对程序设计提出了具有重要意义的语言要求。对于对象途径而言,我们必须关注对象可以怎样变化而又保持其标识(identity)。这将迫使我们抛弃前面说讲过的计算的代换模型,转向更机械式的,理论上也更不容易把握的计算的环境模型(environment model)。在处理对象、变化和标识时,各种困难的根源在于我们需要在这一计算模型中与时间搏斗,如果引入并发后还将变得更糟糕。流方式将我们的模型中的模拟时间与求值过程中的事件发生顺序进行解耦,我们将通过一种称为延时求值(lazy eva luation)的技术做到这一点。

本节我们先介绍第一种对象世界观。

3.1.1 局部状态变量

在对象世界观里,我们想让计算对象具有随着时间变化的状态,而这就需要让每个计算对象有自己的一些局部状态变量。现在让我们来对一个银行账户支取现金的情况做一个模拟。我们将用一个过程withdraw完成此事,它有一个参数amount表示支取的现金量。如果余额足够则withdraw返回支取之后账户里剩余的款额,否则返回消息Insufficient funds(金额不足)。假设开始时账户有100元钱,在不断使用withdraw的过程中我们可能得到下面的响应序列:

withdraw(25) # 70
withdraw(25) # 50
withdraw(60) # "In sufficient funds"
withdraw(15) # 35

在这里可以看到表达式widthdraw(25)求值了两次,但它产生的值却不同,这是过程的一种新的行为方式。之前我们看到的过程都可以看做是一些可计算的数学函数的描述,两次调用一个同一个过程,总会产生出相同的结果。

为了实现withdraw,我们可以用一个全局变量balance表示账户里的现金金额,并将withdraw定义为一个访问balance的过程。下面是balancewidthdraw的定义:

balance = 100
def withdraw(amount):
    global balance
    if balance > amount:
        balance = balance - amount
        return balance
    else: 
        return "Insufficient funds"

虽然withdraw能像我们期望的那样工作,变量balance却表现出一个问题。如上所示,balance是定义在全局环境中的一个名字,因此可以被任何过程检查或修改。我们希望将balance做成withdraw内部的东西,因为这将使withdraw成为唯一能直接访问balance的过程,而其他过程只能间接地(通过对withdraw的调用)访问balance。这样才能准确地模拟有关的概念:balance是一个只有withdraw使用的局部状态变量,用于保存账户状态的变化轨迹。

我们可以通过下面的方式重写出withdraw,使balance成为它内部的东西:

def new_withdraw():
    balance = 100
    def inner(amount):
        nonlocal balance
        if balance > amount:
            balance = balance - amount
            return balance
        else:
            return "Insufficient funds"
    return inner

W = new_withdraw()
print(W(25)) # 70
print(W(25)) # 50
print(W(60)) # "In sufficient funds"
print(W(15)) # 35

这里的做法是用创建起一个包含局部变量balance的环境,并使它初始值为100。在这个环境里,我们创建了一个过程inner,它以amount作为一个参数,其行为就像是前面的withdraw过程。这样最终返回的过程就是new_withdraw,它的行为方式就像是withdraw,但其中的变量确实其他任何过程都不能访问的。用程序设计语言的行话,我们说变量balance被称为是封装new_withedraw过程里面。

将赋值语句与局部变量相结合,形成了一种具有一般性的程序设计技术,我们将一直使用这种技术区构造带有局部状态的计算对象。但这一技术也带来了麻烦,我们之前在代换模型中说,应用(apply)一个过程应该解释为在将过程的形式参数用对应的值取代之后再求值这一过程。但现在出现了麻烦,一旦在语言中引进了赋值,代换就不再适合作为过程应用的模型了(我们将在3.1.3节中看到其中的原因)。我们需要为过程应用开发一个新模型,这一模型将在3.2节中介绍。现在我们要首先检查new_withdraw所提出的问题的几种变形。

下面过程make_withdraw能创建出一种“提款处理器”。make_withdraw的形式参数balance描述了有关账户的初始余额值。

def make_withdraw(balance):
    def withdraw(amount):
        nonlocal balance
        if balance > amount:
            balance = balance - amount
            return balance
        else:
            return "Insufficient funds"
    return withdraw

下面用make_withdraw创建了两个对象:

W1 = make_withdraw(100)
W2 = make_withdraw(100)
print(W1(50)) # 50
print(W2(70)) # 30
print(W2(40)) # Insufficient funds
print(W1(40)) # 10

我们可以看到,W1W2是相互完全独立的对象,每一个都有自己的局部状态变量balance,从一个对象提款与另一个毫无关系。

我们还可以创建出除了提款还能够存入款项的对象,这样就可以表示简单的银行账户了。下面是一个过程,它返回一个具有给点初始余额的“银行

首页 上一页 1 2 3 4 5 下一页 尾页 1/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Python实现人脸识别,对视频跟踪.. 下一篇odoo context上下文用法总结

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目