林俊丞2020/10/28
学习内容:《计算机科学导论》第6、7章
第6章 低级程序设计语言与伪代码
6.1 计算机操作
计算机是能够存储、检索和处理数据的可编程电子设备
上述定义中的操作包括可编程的(programmable)、存储(store)、检索(restrieve)和处理(process)。即通过输入设备、控制单元和指令对数据执行动作。通过输出设备查看结果。
6.2 机器语言
机器语言(machine language):由计算机直接使用的二进制编码指令构成的语言。
计算机真正执行的程序设计指令是用机器语言编写的指令,这些指令固定在计算机的硬件中。
请注意,事实上,每条机器语言指令只能执行一个非常低级的任务,如果直接用机器语言编写程序,任务将会非常冗杂,因此,大多数程序是用高级语言编写的,然后翻译成机器语言。
6.2.1 Pep/9:一台虚拟机
虚拟机(virtual computer(machine)):为了模拟真实机器的重要特征而设计的假象机器。
机器代码因机器而异,而使用的机器又因人而异,因此我们通过虚拟机来向所有人展示使用不同机器语言的经验。本节教学所用的是由Stanley Warford设计的虚拟机————Pep/9。
1.Pep/9的基本特性
- 内存单元由65 536字节的存储空间构成。字长是2字节。
- 寄存器(register)有七个,这里重点研究其中三个:
- 程序计数器(PC),其中包含下一条即将被执行的指令的一个副本。
- 指令寄存器(IR),其中包含正在被执行的指令的一个副本。
- 累加器(A),用来存储数据和运算的结果。
- 中央处理器和存储器
回顾:二进制数和十进制数。对虚拟机而言,可用位数决定可用内存大小。
2.指令格式
如图,一条指令由两部分组成,即8位的指令说明符(instruction specifier)和(可选的)16位操作数说明符(operand specifier)。因此Pep/9的指令在长度上是1字节或3字节,取决于是否需要用操作数说明符。
注:指令说明符(指令的第一个字节)说明了要执行什么操作和如何解释操作数的位置。
操作说明符(指令的第二和第三个字节)存放的是操作数本身或者操作数的地址。
有些指令没有操作数说明符。
在Pep/9中,操作代码(operation code)(称为操作码(opcode))的长度从4位到8位不等。该部分讲解所用的操作码长度是4位,4位操作码的第5位是一个寄存器,它在我们的例子中都是0,因为例子中使用的都是A寄存器(累加器)。
3位的寻址说明符(addressing mode specifier)表示了怎样解析指令中的操作数部分。
例:
- 如果寻址模式是000,那么指令的操作数说明符中存储的就是操作数。(也就是说,在这种模式下,直接读取的操作数说明符即是操作数)这种寻址模式称为立即寻址(i)(immediate(i))。
- 如果寻址模式是001,那么操作数说明符中存储的是操作数所在的内存地址名称。这种寻址模式称为直接寻址(d)(direct(d))。
另外,其他寻址模式也存在,但本部分未作讨论。
特:没有操作数(要处理的数据)的指令称为一元指令(unary instruction),这些指令没有操作数说明符。也就是说,一元指令的长度是1字节,而不是3字节。
3.一些示例指令
0000 停止执行
终止程序。停止(stop)指令是一个一元指令,没有操作数说明符。指令说明符中最右三位将被忽略,而这三位将指示寻址模式。
1100 将字载入寄存器A中
本部分通过举例说明在不同模式下该指令的运行结果来讲解。注意该指令载入一个字,即两个字节。
1101 将字节载入寄存器A中
与上条类似,但该指令只载入一个字节。在立即寻址模式下操作数说明符的第一个字节将会被忽略。在直接寻址模式只下载入内存位置的1字节。
1110 存储寄存器A中的字
把寄存器A中的内容存储到操作数中指定的位置。注意,我们不能在这里使用立即寻址模式,因为寄存器中的内容无法被直接存储到操作数说明符中。
1111 存储寄存器A的字节
与上条类似,但只存储一个字节。同样不能使用立即寻址模式。但是注意,该条指令执行时忽略累加器的前一个字节,存储后一个字节。
0110 将操作数加到寄存器A中
与载入操作类似。
1000 从寄存器A减操作数
与加法操作类似。
Pep/9的输入/输出
Pep/9虚拟机使用ASCII字符集来表示字符,每个ASCII码都由一个字节表示。遵循了通用计算机系统的设计技术,模拟了从键盘读入字符输入并且将字符输出写到屏幕(终端窗口)中的能力。对于输入和输出(I/O),该计算机遵循的设计原则是内存映射输入/输出(memory-mapped I/O),这种方式将输入和输出设备与主存中特定的、固定的地址联系起来。在Pep/9虚拟机中,输入设备在地址FC15,输出设备在地址FC16。
6.3 一个程序实例
程序语句如下:
6.3.1 Pep/9模拟器
根据书上的引导实操:
6.3.2 另一个机器语言实例
根据书上的引导实操:
6.4 汇编语言
汇编语言(assembly language):一种低级语言,用助记码表示特定计算机的机器语言指令。
汇编器(assembler):把汇编语言程序翻译成机器代码的程序。
6.4.1 Pep/9汇编语言
部分助记码表图:
汇编器指令(assembler directive):翻译程序使用的指令。
有时也被称作伪操作(pseudo-operation):
注释(comment):为程序读者提供的解释性文字。
6.4.2 数字数据、分支、标签
分支 (branch):指出执行下一条指令的指令。
标签(label):对内存位置起的名字,可以将这个名字当作操作数。
6.4.3 汇编语言中的循环
阅读理解。
6.5 表达算法
算法(algorithm):解决方案的计划或概要,或解决问题的逻辑步骤顺序。
伪代码(pseudocode):一种表达算法的语言。
6.5.1 伪代码的功能
虽然伪代码并没有特定的语法规则,但必须要表示出下面的概念:
- 变量:出现在伪代码算法中的名字,引用的是内存中存储值的位置。
- 赋值
- 输入/输出
- 选择
- 重复
布尔表达式(boolean expression):评价为真为假的表达式。
6.5.2 执行伪代码算法
阅读理解。
6.5.3 写伪代码算法
桌面检查(desk checking):在纸上走查整个设计。
6.5.4 翻译伪代码算法
阅读理解。
6.6 测试
测试计划(test plan):说明如何测试程序的文档。
代码覆盖(明箱)测试法(code-coverage(clear-box)testing):通过执行代码中的所有语句测试程序或子程序的测试方法。
数据覆盖(暗箱)测试法(data-coverage(black-box)testing):把代码作为一个暗箱,基于所有可能的输入数据测试程序或子程序的测试方法。
测试计划实现(test-plan implementation):用测试计划中规定的测试用例验证程序是否输出了预期的结果。
第7章 问题求解与算法设计
7.1 如何解决问题
阅读理解。
7.1.1 提出问题
阅读理解。
7.1.2 寻找熟悉的情况
阅读理解。
7.1.3 分治法
阅读理解。
7.1.4 算法
解决问题的方案。
算法(algorithm):在有限的时间内用有限的数据解决问题或子问题的明确指令集合。
7.1.5 计算机问题求解过程
7.1.6 方法总结
1.分析问题
读。
2.列出主要任务
读。
3.编写其余的模块
读。
4.根据需要进行重组和改写
阅读理解。
与Polya问题求解策略相似的设计方式即为自顶向下设计,将任务分层从而解决。
7.1.7 测试算法
阅读理解。
7.2 有简单变量的算法
简单变量是那些不能被分开的变量,是存储在一个地方的一个值。
7.2.1 带有选择的算法
阅读理解。
重点:IF/ELSE IF
7.2.2 带有循环的算法
1.计数控制循环
阅读理解。
计数控制循环可以制定过程重复的次数,这个循环的机制是简单记录过程重复的次数并且在重复再次开始前检测循环是否已经结束。
这类循环有三个不同的部分,使用一个特殊的变量叫做循环控制变量(loop control variable)。第一部分是初始化:循环控制变量初始化为某个初始值;第二部分是测试:循环控制变量是否已经达到特定值?第三部分是增量:循环控制变量以1递增。
while循环被称为前测试循环(pretest loop),因为在循环开始前就测试了,如果最初条件为假,那么将不进入循环。
2.事件控制循环
循环中重复的次数是由循环体自身内发生的事件控制的循环被称为事件控制循环。当使用while语句来实现事件控制循环时,这一过程仍然分成三部分:事件必须初始化,事件必须被测试,事件必须更新。
在控制结构中执行或跳过的语句可以是简单的语句或者是复杂的语句。因此跳过或重复的语句中可以包含一个控制结构。
计数控制循环是非常简单直接的,而事件控制循环中则不太清楚。
注意将选择控制结构嵌入循环中。
嵌套结构(nested structure):控制结构嵌入另一个控制结构的结构,又称为嵌套逻辑(nested logic)。
3.平方根
抽象步骤(abstract step):细节仍未明确的算法步骤。
具体步骤(concrete step):细节完全明确的算法步骤。
7.3 复杂变量
读。
7.3.1 数组
数组是同构项目的有名集合,可以通过单个项目在集合中的位置访问它们。项目在集合中的位置叫做索引。
与数组有关的算法分为三类:搜索、排序和处理。
搜索就是搜索数组中的项,一次寻找一个特定的值。
排序就是按一定的顺序将元素放在数组中。
处理是一种捕捉短语,包含了对数组中的项所做的所有其他计算。
7.3.2 记录
记录是异构项目的有名集合,可以通过名字单独访问其中的项目。
所谓异构,就是指集合中的元素可以不必相同。
7.4 搜索算法
7.4.1 顺序搜索
阅读理解。
7.4.2 有序数组中的顺序搜索
阅读理解运用。
7.4.3 二分检索
二分检索(binary search):在有序列表中查找项目的操作,通过比较操作排除大部分检索范围。
注:
- 二分检索算法假设要检索的数组是有序的,其中每次比较操作可以找到要找的项目或把数组减少一半。
- 二分检索不是从数组开头就开始顺序前移,而是从数组中间开始。如果要搜索的项目小于数组的中间项,那么知道这个项目一定不会出现在数组的后半部分。然后再检测数组的中间项,如果要搜索的项目大于中间项,搜索将在数组的后半部分继续。如果中间项等于正在搜索的项目,搜索将停止。
7.5 排序
阅读理解。
7.5.1 选择排序
对数据按初始顺序浏览,每一遍挑出所需排序顺序的第一个排在最后一位,直到所有数据排列完毕。选择排序算法也许是最简单的,因为它与我们手动排序十分相似。
7.5.2 冒泡排序
与上条类似。阅读理解。
7.5.3 插入排序
阅读理解。
7.6 递归算法
递归(recursion):算法调用它本身的能力。
当在一个算法中使用它自己时,这样的算法被称为递归算法,也就是说,如果在某种程度上调用自己,那这个调用称为递归调用。递归就是算法调用它本身的能力,是另一种重复(循环)的控制结构。这种算法使用一个选择语句来确定是否重复算法来调用一遍或停止这一过程,而不是使用一个循环语句来执行一个算法。
每个递归算法至少有两种情况:基本情况和一般情况。
- 基本情况是答案已知的情况;
- 一般情况则是调用自身来解决问题的更小版本的解决方案。
因为一般情况下解决的是原始问题越来越小的版本,所以程序最终达到基本情况。
与每个递归问题相关的是如何衡量问题的大小。
所有递归解决方案的第一步都是确定尺寸系数。如果问题涉及的是数值,尺寸系数可能就是数值本身。如果问题涉及结构,那么尺寸系数可能就是结构尺寸。
7.6.1 子程序语句
我们可以给一段代码一个名称,然后程序另一部分的一个语句使用这个名称。遇到这个名称时,这个进程的其他部分将会终止,等待这个命名代码被执行。命名代码出现的地方被称为调用单元。
子程序的两种形式:一种是只执行特定任务的命名代码;一种是不仅执行任务,还返回给调用单元一个值(值返回子程序)。第一种形式的子程序在调用单元中用作语句,第二种则作为表达式,返回的值被用来测估表达式。
7.6.2 递归阶乘
阅读理解。
7.6.3 递归二分检索
阅读理解。
7.6.4 快速排序
基本策略是分治法。基础是递归。
7.7 几个重要思想
7.7.1 信息隐蔽
信息隐蔽(information hiding):隐蔽模块的细节以控制对这些细节的访问的做法。
7.7.2 抽象
抽象(abstraction):复杂系统的一种模型,只包括对观察者来说必需的细节。
信息隐蔽是隐藏细节的做法,抽象则是隐藏细节后的结果。
数据抽象(data abstraction):把数据的逻辑视图和它的实现分离开。
过程抽象(procedural abstraction):把动作的逻辑视图和它的实现分离开。
控制抽象(control abstraction):把控制结构的逻辑视图和它的实现分离开。
控制结构(control structure):用于改变正常的顺序控制流的语句。
7.7.3 事物命名
给数据和过程起一个名字,这些名字叫做标识符。
7.7.4 测试
读。
总结
基本完成学习任务,本次学习任务在实践的辅助下能够较好地理解完成。
问题及解决
- Pep/9的存储器存储的存储物本质究竟是什么? 解决:每个空位存储独立字节,存储器通过读/写指定位置的数据实现存/读数据。
- 直接寻址是根据什么在操作数说明符中读取地址代码的?未解决
- 用伪代码语句写算法并翻译、测试。(这是一个困难,而非问题)
- 阅读理解。** 解决: **暂未解决。