AS Level 计算机科学 (9618) 学习笔记:程序设计(第 11 章与第 12 章)
未来的程序员你好!欢迎来到计算机科学的核心:**程序设计**。本章将带领你从算法(第 9 章)和数据结构(第 10 章)的理论世界,迈向将这些想法转化为实际工作方案的实践技能。
我们将学习编写代码的基本构件、如何高效组织程序,以及规划、测试和维护稳健软件的关键步骤。
别担心,如果你觉得写代码听起来很可怕——我们会将一切拆解,一步步讲解。掌握这些技能对于 Paper 2 至关重要,在考试中你将使用伪代码来解决各种实际问题!
11. 程序设计
11.1 程序设计基础
本节涵盖了我们向计算机传达指令所需遵循的核心规则和工具。
变量、常量和数据类型
把计算机内存想象成一个巨大的储物柜。
- 变量 (Variable): 一个命名的存储位置,其值在程序执行期间可以改变。
- 常量 (Constant): 一个命名的存储位置,其值是固定不变的。使用常量能让代码更清晰、更安全(防止意外修改关键值)。
在声明变量或常量时,必须指定其数据类型。这告诉计算机需要处理什么类型的信息以及分配多少存储空间。
AS Level 常见数据类型(及其伪代码表示):
- INTEGER: 整数(例如:5, -100)。适用于计数器或分数。
- REAL: 带有小数部分的数字(例如:3.14, 0.5)。
- CHAR: 单个字符、数字或符号(例如:'A', '7', '$')。
- STRING: 字符序列(例如:"Hello World")。
- BOOLEAN: 仅有两个值:TRUE 或 FALSE。非常适合用作标志位或条件判断。
声明与赋值:
我们在程序或模块的开头声明常量和变量。
-
变量声明:
DECLARE StudentName : STRING -
常量声明:
CONSTANT PI = 3.14 -
赋值(赋予数值):
StudentName ← "Alex"
易错点警示: 切勿混淆赋值(←)与比较(=)。在伪代码中,← 表示将值*存入*变量中。
输入、输出与表达式
程序必须能与用户交互(输入)并显示结果(输出)。
-
输入:
INPUT Age(从键盘读取数据并将其存储在变量 Age 中)。 -
输出:
OUTPUT "Your age is ", Age(在控制台上显示文本和 Age 的值)。
表达式 将数值、变量和运算符(算术或逻辑)组合在一起,以产生一个最终结果。
-
算术运算符:
+,-,*,/,DIV(整除),MOD(取余)。 -
逻辑运算符:
AND,OR,NOT。
你知道吗? 使用内置函数(如计算平方根 SQRT(X))和库例程(预先编写好的代码片段)可以节省大量时间,并使代码更可靠,因为这些函数已经过测试且被证明是正确的。
11.2 程序结构(控制结构)
任何算法的三个基本构件是顺序、选择和迭代。我们使用控制结构来实现选择和迭代。
选择(决策)
选择允许程序根据条件(布尔表达式)来决定执行路径。
1. IF...THEN...ELSE(及嵌套 IF)
- 用于在两个或多个结果之间进行简单选择时。
-
伪代码示例:
IF Score > 75 THEN
OUTPUT "Distinction"
ELSE
OUTPUT "Pass"
ENDIF
2. CASE 结构
- 当根据单个变量的值有许多互斥的结果时使用。这比使用多层嵌套 IF 语句更简洁。
-
伪代码示例:
CASE DayOfWeek OF
1: OUTPUT "Monday"
5: OUTPUT "Friday"
OTHERWISE OUTPUT "Weekend or other day"
ENDCASE
迭代(循环/重复)
迭代允许一段代码被重复执行。选择正确的循环结构对于高效编码至关重要。
1. 计数控制循环(FOR 循环)
- 用于明确知道循环需要运行多少次的情况。
-
伪代码示例:
FOR Counter ← 1 TO 10
2. 前置条件循环(WHILE 循环)
- 在执行循环体之前检查条件。循环可能执行零次或多次。
- 用于终止条件取决于外部因素(如用户输入或文件状态)的情况。
-
伪代码示例:
WHILE InputValue <> -1 DO
// 执行循环体
ENDWHILE
3. 后置条件循环(REPEAT UNTIL 循环)
- 在执行循环体之后检查条件。该循环保证至少执行一次。
- 常用于确保输入有效(反复提示用户直到输入合法数据)。
-
伪代码示例:
REPEAT
INPUT Password
UNTIL Password = "secret"
如何选择循环结构:
如果问题需要明确的重复次数(如输入 5 个分数),请使用 FOR 循环。如果必须保证至少运行一次(如输入校验),请使用 REPEAT UNTIL。如果循环可能根本不需要运行(如处理一个空文件中的记录),请使用 WHILE。
11.3 结构化程序设计(过程与函数)
结构化程序设计 涉及将大型程序拆解为更小的、独立的模块。这使得代码更易于编写、调试和维护。这些模块被称为子程序:过程(Procedure)和函数(Function)。
过程(执行特定任务的子程序)
过程是一个已命名的代码块,它执行特定任务,但不一定向主程序返回数值。
- 适用场景: 涉及输入/输出的任务,或直接修改变量(如将数据保存到文件、打印菜单)。
-
语法: 使用
PROCEDURE定义,并直接通过其名称调用。
函数(计算并返回结果的子程序)
函数是一个已命名的代码块,它执行计算并将结果返回到调用它的表达式中。
- 适用场景: 数学运算、字符串处理或条件判断(如计算税额、获取名字长度)。
-
语法: 使用
FUNCTION定义,并用于表达式中,返回的值会替换掉调用处。
参数与实参
为了使过程和函数更加灵活,我们通过参数 (Parameters) 传递数据。
- 参数 (Parameter): 定义在过程/函数标题中,用于接收数据的标识符。
- 实参 (Argument): 调用过程/函数时传递的实际数值或变量。
参数有两种传递方式:
1. 按值传递 (Pass By Value)
- 传递的是实参的一个拷贝。
- 子程序内部对参数的任何修改都不会影响主程序中的原始变量。
- 类比: 给某人一份笔记的复印件。他们在复印件上乱涂乱画,你的原始笔记依然完好无损。
2. 按引用传递 (Pass By Reference)
- 传递的是实参的内存地址(引用)。
- 子程序内部对参数的任何修改都会影响主程序中的原始变量。
- 类比: 把储物柜的钥匙交给别人。他们可以永久改变里面的内容。
- 过程标题: 定义模块的第一行(如
PROCEDURE PrintName(ByValue N)) - 函数接口: 定义函数的名称、参数和返回类型。
- 实参: 调用函数/过程时传入的实际数据。
12. 软件开发
编写代码(第 11 章)只是工作的一部分。第 12 章着眼于构建大型、可靠的软件系统所需的结构和纪律。
12.1 程序开发生命周期 (PLC)
程序开发生命周期 (PLC) 是一套结构化的框架,描述了开发、维护并最终淘汰软件系统所需的步骤。其目的是确保质量、管理复杂性并满足用户需求。
PLC 阶段
- 分析 (Analysis): 定义问题并识别用户需求。(系统需要做什么?)
- 设计 (Design): 规划解决方案的结构、算法和界面。(系统将如何做?)
- 编码/实现 (Coding): 编写程序代码。
- 测试 (Testing): 检查程序错误并确保其满足需求。
- 维护 (Maintenance): 在部署后修复 Bug 并进行更新。
开发生命周期模型
不同类型的程序需要不同的方法。
1. 瀑布模型 (Waterfall Model)
- 原则: 每个阶段必须在下一个阶段开始前完成,严格地向下流动。
- 优点: 简单、高度结构化,易于管理小型、需求明确的项目。
- 缺点: 非常不灵活;如果后期需求变更,回溯的成本很高且难度大。
2. 迭代模型 (Iterative Model)
- 原则: 项目在小的、重复的周期(迭代)中开发。及早创建工作原型并随时间优化。
- 优点: 允许及早获得用户反馈,易于适应变化的需求。
- 缺点: 如果管理不当,可能导致“范围蔓延”(项目无限扩大)。
3. 快速应用开发 (RAD)
- 原则: 通过持续的用户参与快速创建原型,减少规划时间。
- 优点: 对于用户界面至关重要且需求相对灵活的项目,开发速度非常快。
- 缺点: 不适合极度复杂、大规模的系统,或对安全性要求极高的系统(如飞机控制软件)。
12.2 程序设计
设计阶段(PLC 的第 2 阶段)使用工具在编写任何代码之前规划程序结构。
结构图 (Structure Charts)
结构图 是一种层级图,用于自顶向下设计 (Top-down design),将问题分解为更小的、可管理的子任务(模块、过程或函数)。
- 目的: 展示程序的整体结构以及模块之间的关系。
- 核心特征: 明确显示模块之间传递的参数/数据(圆圈代表数据,菱形箭头代表控制标志)。
优点: 鼓励模块化,有助于确保模块独立性,从而使测试更容易。
状态转换图 (State-Transition Diagrams)
状态转换图 用于记录在不同状态下运行的算法。
- 目的: 用于建模当前动作依赖于之前输入历史的系统(如交通灯、自动售货机、简单的登录系统)。
- 结构: 圆圈代表状态,箭头代表转换(导致从一种状态变为另一种状态的动作或输入)。
12.3 程序测试与维护
如果程序无法正常工作,它就是无用的。本节讨论如何查找、预防和修复错误。
错误类型
错误(或缺陷/Bug)分为三大类:
-
语法错误 (Syntax Error): 违反了编程语言的语法或规则。翻译器(编译器/解释器)会捕捉到这些错误。例子:将
WHILE拼写为WHIL。 - 逻辑错误 (Logic Error): 程序运行成功,但输出结果错误,因为算法本身不正确。翻译器无法捕捉这些错误。例子:本该相乘却写成了加法。
- 运行错误 (Run-time Error): 程序在执行过程中发生,导致程序崩溃或意外停止。例子:除以零,或尝试访问数组中不存在的索引。
测试策略与数据
测试策略 定义了测试的总体方法,而测试计划是一个详细说明测试内容、方式和所用数据的具体文档。
必须选择合适的测试数据:
- 正常数据 (Normal Data): 预期输入的合理数据。例如:如果范围是 1 到 100,使用 50。
- 极端/边界数据 (Extreme/Boundary Data): 处于可接受范围边界的数据。这是最容易隐藏错误的地方!例如:如果范围是 1 到 100,使用 1 和 100。
- 异常数据 (Abnormal Data): 旨在破坏程序的无效数据。例如:输入“Apple”或 101。
测试方法
- 预演 (Dry Run): 程序员使用追踪表手动逐步执行代码。
- 走查 (Walkthrough): 开发团队手动一行一行地检查代码。
- 白盒测试 (White-Box Testing): 基于对程序内部结构和代码路径的了解进行测试,确保每一行代码都至少被执行一次。
- 黑盒测试 (Black-Box Testing): 仅基于规范和预期输出进行测试,无需了解内部代码结构。
- 集成测试 (Integration Testing): 将各个模块组合在一起测试,确保交互正确。
- 存根 (Stub): 测试中使用的临时占位模块,用于模拟尚未编写的模块功能。
程序维护
- 纠正性维护 (Corrective): 修复系统运行中发现的 Bug。
- 适应性维护 (Adaptive): 修改系统以适应环境变化(如新的操作系统)。
- 完善性维护 (Perfective): 提升软件性能、效率或可维护性。