《计算机构造与解释》读书笔记(5)

news/2024/11/25 0:55:02/

1. 写在最前面

很佩服那种无论过什么节都没有过节综合征的小伙伴,在过了 7 天假期,然后又连续上班 7 天的魔鬼模式下,最近很容易力不从心。

先默默的整理节前没有写好的笔记,调整一下状态吧!

2. 流

是否存在另外一种方法,避免使用「赋值」。

在本小节中,将基于一种称为流的数据结构,流可能缓和状态模拟中的复杂性。流的使用可以模拟一些包含状态的系统,却不需要利用赋值或者变动数据。

2.1 流作为延时的表

以计算一个区间里的素数之和为例,对比一下两个程序:

  • 迭代风格的程序

    (define (sum-primes a b)(define (iter count accum)(cond (( > count b) accum)((prime? count) (iter (+ count 1) (+ count accum)))(else (iter (+ count 1) accum))))(iter a 0))
    
  • 递归风格的程序

    (define (sum-primes a )(accumulate + 0(filter prime? (enumerate-interval a b))))
    

对比以上两种方法,通过序列方式去计算从 10000 到 1000000 的区间,第二种递归方式,效率明显太低了。

注: 流是一种非常巧妙的想法,可以利用各种序列操作,但又不会带来将序列作为表去操作而引起的代价。

作为一种数据抽象,流与表完全一样。它们的不同点在于元素的求值时间。对于常规的表,其 car 和 cdr 都是在构造时求值;而对于流,其 cdr 则是在选取的时候才去求值。

注:流的实现将基于一种称为 delay 的特殊形式,对于(delay ) 的求值将不对表达式 求值,而是返回一个延时对象的对象,它可以看作是对在未来的某个时间求值 的允诺。

​ 与 delay 一起的还有一个称为 force 的过程,它以一个延时对象为参数,执行相应的求值工作。

2.1.1 流实现的行为方式

一般而言,可以将延迟求值看作一种「由需要驱动」的程序设计,其中流处理的每个阶段都仅仅活动到足够满足下一个阶段需要的程度。

2.1.2 delay 和 force 的实现

delay 必须包装一个表达式,使它可以在以后根据需要求值。

force 是简单地调用由 delay 产生的过程

注:构造 delay 时存在优化的手段,就是将构造 delay 的值进行缓存,再下次调用出现相同的调用时,直接返回值。

2.2 无穷流

可以用流去表示无穷长的序列,例如,考虑下面关于正整数的流的定义:

(define (integers-starting-from n)(cons-stream n (integers-starting-from (+ n 1))))
(define integers (integers-starting-from 1))

注:这是一个无穷长的流,但是在任何给定时刻,都能能检查到它的有穷部分。

2.2.1 隐式地定义流

描述流的另一种方式是利用延时求值隐式地定义流。举个例子,下面表达式将 ones 定义为 1 的无穷流:

(define ones (cons-stream 1 ones))

这种方式定义就像是在定义一个递归过程:这里的 ones 是一个序对,它的 car 是 1,而 cdr 是求值 ones 的一个允诺。对于其 cdr 的求值又是一个 1 和 求值 ones 的一个允诺,并这样继续下去。

2.3 流计算模式的使用

带有延迟求值的流可能成为一种功能强大的模拟工具,能提供局部状态和赋值的许多效益。

注:这种机制还能避免将赋值引入程序设计语言所带来的一些理论困难。流方法极富有启发性,因为借助于它去构造系统时,所用的模块化划分方式可以与采用赋值、围绕着状态变量组织系统的方式不同。

2.3.1 系统地将迭代操作方式表示为流过程

  • 状态可以表示为值的「没有时间的」流,而不是一组不断更新的变量。
  • 可以构造出一个流的流(称为表列的结构),其中的每个流都是前一个流的变换的结果。

2.3.2 序对的无穷流

思考 prime-sum-pairs 过程,生成所有整数序对(i,j)的流,其中有 i <= j 且 i + j 是素数。

为了处理无穷的流,需要设计另一种组合序列,以保证只要这个程序运行的时间足够长,那么最终就能得到流中的每一个元素。做到这一点的方式是采用下面的 interleave 过程:

(define (interleave s1 s2)(if (stream-null? s1)s2(cons-stream (stream-car s1)(interleave s2 (stream-cdr s1)))))

interleave 交替地从两个流中去元素,这样,即使第一个流是无穷的,第二个流里每个元素最终都能在这样交错得到的流里有自己的位置。

2.3.3 将流作为信号

可以采用流,以一种非常直接的方式为信号处理系统建模,用流的元素表示一个信号在顺序的一系列时间间隔上的值。

2.4 流和延时求值

定义反馈循环的能力依赖于 delay

注:如果没有 delay ,要求对每个信号处理部件的输入都能在产生输出之前完成求值。

2.4.1 规范求值序

可以采用一种求值模型,其中所有过程参数都自动延时,只有在实际需要它们的时候才强迫参数求值。即 规范序求值

在使用 integral (积分)的过程时,我们构造出了两类过程:

  • 常规过程
  • 要求延迟参数的过程

注:如果创建了不同种类的过程,就将迫使去创建不同种类的高阶过程

2.5 函数式程序的模块化和对象的模块化

  • 引进赋值可以增强系统的模块化,将一个大系统的状态中的某些部分封装在局部变量里。
  • 流模型不使用赋值,但是可以提供跟赋值同样的模块化能力。

2.5.1 时间的函数式程序设计观点

引进赋值和变动对象,就是为了提供一种机制,以便能模块化地构造出程序,去模拟具有状态的系统。

注:流为模拟具有内部状态的对象提供了另一种方式,可以用一个流去模拟一个变化的量,例如某个对象的内部状态,用流表示其顺序状态的时间史。

考虑重新设计一个「取款处理器」的实现:

  • 赋值版本

    (define (make-simplified-withdraw balance)(lambda (amount)(set! balance (- balance amount))balance))
    

    调用 make-simplified-withdraw 将生成出具有局部变量 balance 的对象,其值将在对这个对象的一系列调用中逐步减少。这些对象以 amount 为参数,返回一个新的余额值。

  • 流版本,以越值和一个提款流作为参数,生成账户中顺序余额的流

    (define  (stream-withdraw balance amount-stream)(cons-streambalance(stream-withdraw (- balance (stream-car amount-strem))(stream-cdr amount-stream))))
    
    stream-withdraw 实现具有良好定义的数学函数,其输出完全由输入确定。该函数其行为根本不会变化,用户看到的却是与一个改变状态的系统交互。
    

注:解释上述原因很好的方式是,用户方时态的存在,为这个系统赋予了状态特性。如果用户从自己交互问题上后退一步,以余额流的方式思考问题,而不是去看个别的交易,这个系统看上去就是无状态的了。

3. 碎碎念

断断续续的啃完了这本书的前三章,深刻的意识到,「观念的改变」从不在一朝一夕,而是需要不断的潜移默化影响自己。

再定个小目标吧,希望这个月能够把 go 新语法补充一下,实在木有时间的话,那也得学习一下泛型吖!

  • 好为人师是一种炫耀,潜意识告诉别人你比他强,这种姿态只要一出来,别人的反感就会生出来。
  • 跟蠢人交流你会有一个很明显的感觉:你说的每一句话,他都想要反驳。
  • 努力了却没看到回报 那就是你现在的努力只是在弥补你之前的懒惰

http://www.ppmy.cn/news/22830.html

相关文章

java基于springboot+vue的企业员工工资考勤系统

随着我国改革开发和国家政策的开发等一系列优惠条件的开放&#xff0c;我国的高校数量也是在不断的增加&#xff0c;每个高校都有很多的员工&#xff0c;每个员工的工资又各不相同&#xff0c;如何能够管理这些庞大的工资数据&#xff0c;是很多高校在发放工资的时候都需要面临…

介绍JVM中OOM的8种类型

title: 介绍JVM中OOM的8种类型 date: 2019-11-17 16:00:00 tags: JVM OOMOutOfMemoryError categories:JVM java.lang.OutOfMemoryError Java heap space Java应用程序仅允许使用有限的内存&#xff0c;此限制是在应用程序启动期间指定的。Java内存被分为两个不同的区域&…

在CentOS 7.7 x86_64上从python3安装pip3

线上发现一台服务器只安装python 3.7.3&#xff0c;但是没有安装pip3&#xff0c;这样就无法安装Python的各种包&#xff0c;分析是从二进制包安装的&#xff0c;并不完整。如果从python的源码包安装的话&#xff0c;默认是包含pip3的。 问题&#xff1a; 有python 3.7但是没…

第一章 Arm 架构科普解读

本文源自《书香度年华》「ARM 架构专栏」&#xff0c;是一系列由浅入深、循序渐进的文章&#xff0c;文章之间有一定的前后关联性&#xff0c;所以按顺序阅读&#xff0c;建议收藏专栏。 目录 前言 一、架构概述 1.1 冯诺依曼架构 1.2 哈佛架构 1.3 Arm 架构 二、架构图谱…

第07讲:Vue初级

一、浅试双向绑定 1.1、导入vue的js库 <script src"assets/vue.min-v2.5.16.js"></script>1.2、创建js代码段写vue代码 <script>//创建vue实例new Vue({el: #app, //将id为app的div的管理权交给vuedata: { //用户共享的数据msg: null}});</s…

SEO搜索引擎优化方式

SEO搜索引擎优化方式 SEO搜索引擎优化方式 文章目录SEO搜索引擎优化方式[TOC](文章目录)前言一、黑帽SEO1、关键字的堆叠2、隐藏文本3、门页二、白帽SEO1、 TDK2、 提高网站语义化的html标签占比3、 SSR总结前言 利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名&#…

Apache Flink 实现原理:容错机制

目录 前言 一、Flink容错 二、状态(State) 三、检查点(Checkpoint) 四、保存点(Savepoint)

01基础-算法第四版红黑树-红黑树-数据结构和算法(Java)

文章目录1 前言2 定义2.1 替换3-结点2.2 等价定义2.3 一一对应3 基础3.1 颜色表示3.2 结点表示3.2 旋转3.3 旋转后重置父结点的链接后记1 前言 目前我学习到的2种红黑树&#xff1a;一种是红黑树的标准定义&#xff0c;另外一种是《算法第4版》中的红黑树。后一种定义只比前一…