chapter6 低级程序设计语言与伪代码
- 学习内容:从“什么是计算机系统”变成“如何使用计算机系统”
- 计算机时能够存储、检索、处理数据的可编程电子设备。
- 可编程的:数据和操作数据存储在相同的地方
- 存储、检索和处理是计算机能对数据执行的动作
- 概念:由计算机直接使用的二进制编码指令构成的语言
- 特点:
- 计算机必须参考的命令的真实清单并不存在。CPU把这个清单嵌入了自己的设计。
- 每条机器语言指令只能执行一个非常低级的任务。
- 大多数程序是用高级语言编写的,然后翻译成机器语言。
Pep/9:一台虚拟机
- 概念:为了模拟真实机器的重要特征而设计的假想机器。
- 基本特征
- 内存单元由65536字节的存储空间构成。
- 有7个寄存器(CPU中算数/逻辑单元的一小块存储区域),重点研究三个
- 程序计数器(PC),其中包含下一条即将被执行的指令的地址。
- 指令寄存器(IR),其中包含正在被执行的指令的一个副本。
- 累加器(A),用来存储数据和运算的结果。
- 注意,存储器中的地址本身并不存储存储器中,它们只是其中独立字节的名字。
- 指令格式
- 指令说明符(8位):说明要执行什么操作和如何解释操作数的位置。
- 操作数说明符(16位):存放操作数本身或者操作数的地址,有些指令没有操作数说明符
- 寻址模式说明符(三位):表示怎样解析指令中的操作数部分。
- 立即寻址000:操作说明符中存储的就是操作数
- 直接寻址001:操作说明符中存储的是操作数所在的内存地址名称
- 寻址模式说明符(三位):表示怎样解析指令中的操作数部分。
- 无操作说明符的指令称为一元指令。
- 一些示例指令
- 0000停止执行:一元指令
- 1100将字载入寄存器A(累加器)中
- 立即寻址:寄存器原来的内容将被覆盖
- 直接寻址:当一个地址用来指定一个操作数时,该字的最左边的1字节就是给定的地址。操作数的内容并未改变。Q:操作数说明符中为何能出现“F”,这种情况下一个字长到底有几个数?
- 1101将字节载入寄存器A中:只载入1字节而不是2字节,立即寻址载入第二个字节,直接寻址载入第一个字节。
- 1110存储寄存器A中的字:把寄存器A中的内容存储到操作数中指定的位置。在存储操作码中使用立即寻址模式是非法的。Q:所以存入操作数说明符的是内容的地址?
- 1111存储寄存器A中的字节:只存储1字节。
- 0110将操作数加到寄存器A中。
- 1000从寄存器A减操作数。
- Pep/9的输入和输出
- Pep/9系统模拟了从键盘读入字符输入并且将字符输出写到屏幕(终端窗口)中的能力。
- 设计原则:内存映射输入/输出,将输入和输出设备与主存中特定的、固定的地址联系起来。
- 使用ASCⅡ字符集来表示字符。
Pep/9模拟器
- 程序代码每个字节之间用空格隔开,以ZZ结束程序
- 在机器语言程序执行之前,它必须被载入存储器。
- 装入程序:构建>装入
运行程序:构建>执行
另一个机器语言实例
输入:LDBA 1101 FC 15
输出:LDSR 1111 FC 16
- 概念:
- 汇编语言:一种低级语言,用助记码表示特定计算机的机器语言指令
- 汇编器:把汇编语言程序翻译成机器代码的程序
Pep/9汇编语言
- 格式:操作数用0x和十六进制表示,接下来是逗号,最后是寻址模式(由字母i(立即寻址)或字母d(直接寻址)说明)
- 汇编器指令(伪操作);翻译程序使用的指令
- 程序代码使用Pep/9模拟器的源代码窗口进行编写。Pep/9汇编器会忽略在分号后面的任何字符,这就允许我们在指令旁边添加注释(comment)
- 构建>汇编:汇编器使目标代码和汇编代码一样,包含内存地址和目标代码程序的正式版本
构建>运行:汇编并运行
运行源代码 - 程序使用立即寻址模式来载入字符,这也是我们用机器语言直接编写同样程序时使用的方法
数字数据、分支、标签
- 可以使用额外的用来展示输入和输出数字和整个字符串的指令。
- 分支命令:指出执行下一条指令的指令
- 额外的汇编语言指令:
- DECI:十进制输入,不支持立即寻址
- DECO:输出特定的十进制数字,支持两种寻址模式
- STRO:用来打印完整的的一串字符串
- 分支指令BR:无条件转移,使操作数中的存储器地址输入程序计数器,,达到位置的跳跃,中断程序的正常性线流
- BRLT:累加器小于0时导致程序分支
- BREQ:累加器等于0时导致程序分支
- CPWA:对比累加器中的数值和操作数。它将累加器的和减去操作数,并存储累加器的结果,可用于判断BRLT或BREQ指令?
- 在汇编语言中,数据所使用的内存通常在程序之前进行保存。
- 标签:放在每一行的开头,之后跟着一个冒号,一旦被标上标签,该位置的数据或指令就可以通过标签进行引用(感觉这个比标准定义更好理解)
汇编语言中的循环
表达算法- 算法:解决方案的计划或概要,或解决问题的逻辑步骤顺序
- 伪代码:一种表达算法的语言
伪代码的功能
- 伪代码没有特定的语法规则,但必须要表示出下面的概念
- 变量:名字要能反映出它存放的值在算法中的角色
- 赋值:把值放入变量的方法
- 方式:1.eg:Set sum to 8
2.eg:sum<--8
- 方式:1.eg:Set sum to 8
- 输入:Get/Input/Read
输出:Display/write/print - 伪代码是写给人们看的,使用一致的单词是一种好习惯
- 选择:选择执行或跳过某项操作,或者在两项操作之间进行选择。选择结构使用括号中的条件决定执行哪项操作。
- 使用缩进对语句进行分组,符号“//"用于加注释,它不是算法的一部分。
- if-then-else:在两种操作中选择。
- if-then:用于执行操作或跳过。
- 重复
- 使用重复结构可以重复执行指令,在WHILE旁边的圆括号中的表达式用于判断,成立则执行缩进中的语句。
- WHILE和IF旁边的括号里的表达式是布尔表达式,结果可为真或假。
- 将WHILE/IF/ELSE大写是因为这些语句通常直接使用在很多的编程语言中,在计算领域中它们有特殊的含义。
执行伪代码算法
- 策略:问问题并推迟细节(首先给任务一个名称,然后再补充细节来完成这个任务)
- 在没有测试之前,算法都不算完成。
- 桌面检查:在纸上走查整个设计。
翻译伪代码算法
- 如何翻译伪代码算法取决于我们将算法翻译成哪种语言。
- 在每一个代码块中都要给第一个指令命名,然后确定哪一块应该被执行。
- 根据程序原先的目的,来判断它直观的正确性:设计和实现一个测试计划。
- 测试计划:一个文档,说明了要全面测试程序需要运行的次数以及运行程序使用的数据,每套输入的数据称为测试用例,还要列出选择这套数据和数据值的原因,以及每套数据预期的输出。
- 设计用例:代码覆盖测试法(明箱测试法):程序中的每条语句都能被执行到
数据覆盖测试法(暗箱测试法):确保包括允许使用的数据的边界值
常用的测试法是结合这两种方法 - 测试计划实现:运行测试计划中列出的所有测试用例,并记录运行结果
- 在测试计划中,我们应该尝试边界值,但确保它们的和不超出-2^15 -1到+2^15 -1这个范围。why这俩数?
==Chapter7 问题求解与算法设计=
- 重点:算法,即它们在解决问题、开发策略、采用和测试的技术中的作用
- George Polya:“如何解决它”列表:理解问题>设计方案>执行方案>回顾
提出问题
寻找熟悉的情况
分治法:
- 概念:把一项任务分成若干个子任务,而子任务可以继续划分为子任务,如此进行下去,直到每个子任务都是可以实现的为止。
算法:
在有限的时间内用有限的数据解决问题或子问题的明确指令集合
计算机问题求解过程:
分析和说明阶段/算法开发阶段/实现阶段/维护阶段
方法总结
- 分析问题
- 列出主要任务:主任务的列表称为主模块,用自然语言或伪代码在主模块中重述问题,用任务名把问题分解成功能区块。如果主模块太长,可以引入一些控制结构
- 编写其余模块:不断细化每个模块,直到每条语句都是具体步骤为止
- 根据需要进行重组和改写
测试算法
有简单变量的算法- 简单变量是那些不能被分开的变量,是存储在一个地方的值
带有选择的算法
带有循环的算法
- 计数控制循环:可以指定过程重复的次数
- 事件控制循环:循环中重复的次数是由循环体自身内发生的事件控制的循环
- 嵌套结构:控制结构嵌入另一个控制结构的结构
- 平方根
- 猜测数:旧的猜测数和平方数除以旧的猜测数之间的平均数作为新的猜测数
- 抽象步骤:细节仍未明确的算法步骤
- 具体步骤:细节完全明确的算法步骤
数组
- 数组是同构项目的有名集合,可以通过单个项目在集合中的位置访问它们
- 与数组有关的算法:搜索、排序、处理
记录
- 记录时异构项目的有名集合,可以通过名字单独访问其中的项目
顺序搜索
- 当我们发现了元素或者是查找所有的元素后都没有找到匹配项就停止
- 布尔操作符:AND/OR/NOT
- AND:同真则真
- OR:有真则真
- NOT:改变表达式的值
有序数组中的顺序搜索
- 使用变量length来知道数组中的元素的值,而不需要特定元素的数量,length比数组中元素的数目要小
- 我们在算法中使用变量index而不是position或place
二分检索
- 在有序列表中查找项目的操作,通过比较操作排除大部分检索范围
- 我们并不总用二分检索,因为为了中间项的索引,每个比较操作都需要更多的计算。此外,数组必须是有序的
选择排序
- 简单但有缺陷,它需要两个完整列表(数组)的空间
冒泡排序
- 与选择排序相比,只是在查找最小值时采用了不同的方法
- 冒泡排序法是非常慢的排序方法,但可以用来判断数组是否有序
插入排序
- 类似于冒泡排序
- 递归:算法调用它本身的能力
- 基本情况:答案已知
- 一般情况:调用自身来解决问题的更小版本的解决方案
- 每次递归调用后,问题都应该减小,每次执行算法提供给算法的数据值必须是不同的
- 所有递归解决方案的第一步都是确定尺寸系数
子程序语句
- 给一段代码一个名称,在程序运行时遇到这个名称时,这个进程的其他部分会终止,等待这个命名代码被执行,命名代码出现的地方被称为调用单元
- 子程序的形式
- 只执行特定任务的命名代码,在调用单元中用作语句
- 不仅执行任务,还返回给调用单元一个值,用作表达式,返回的值被用来评估表达式
递归阶乘
- 0的阶乘是1,尺寸系数就是要计算阶乘的数
- 无限递归:参数是负数时,子程序将不断地调用自身,直到系统耗尽了内存为止
递归二分检索
- 递归算法必须从非递归算法中调用
-基本策略:分治法、递归
几个重要思想信息隐蔽:隐蔽模块的细节以控制对这些细节的访问的做法,即进行高层设计时不能见到低层的细节
抽象
- 复杂系统的一种模型,只包括对观察者来说必需的细节
- 数据抽象:把数据的逻辑视图和它的实现分离开
- 过程抽象:把动作的逻辑视图和……
- 控制抽象:把控制的……
- 控制结构:用于改变正常的顺序控制流的语句