AS Level 计算机科学 (9618) 复习笔记:程序设计 (第 11 及 12 章)

你好,未来的程序员!欢迎来到计算机科学的核心:程序设计 (Programming)。本章将带领大家从算法 (第 9 章) 和数据结构 (第 10 章) 的理论世界,迈向将这些构思转化为实际解决方案的实践技巧。
我们将学习编写代码的基本构件、如何有效地组织程序,以及规划、测试和维护稳健软件所需的关键步骤。
如果觉得写代码很困难,请不用担心,我们会将所有内容拆解,一步步教学。掌握这些技能对于 Paper 2 至关重要,届时你将需要运用伪代码 (Pseudocode) 来解决问题!


11. 程序设计

11.1 程序设计基础

本节涵盖了我们用来向计算机发送指令的核心规则与工具。

变量、常量与数据类型

把计算机内存想象成一个巨大的储物柜。

变量 (Variable): 一个已命名的存储位置,其值在程序执行期间可以改变
常量 (Constant): 一个已命名的存储位置,其值是固定的,在执行期间不能更改。使用常量能让代码更清晰、更安全 (防止意外更改关键数值)。

声明变量或常量时,必须指定其数据类型 (Data Type)。这告诉计算机预期接收什么类型的信息,以及需要分配多少空间。

AS Level 常见的数据类型 (及其伪代码表示):

INTEGER: 整数 (例如:5, -100)。常用于计数器或分数。
REAL: 包含小数部分的数值 (例如:3.14, 0.5)。
CHAR: 单一字母、数字或符号 (例如:'A', '7', '$')。
STRING: 字符序列 (例如:"Hello World")。
BOOLEAN: 仅包含两个值:TRUEFALSE非常适合用于标志 (flags) 或条件判断。

声明与赋值 (Declaration and Assignment):

我们在程序或模块的开头声明常量和变量。

声明 (变量): DECLARE StudentName : STRING
声明 (常量): CONSTANT PI = 3.14
赋值 (给予数值): StudentName ← "Alex"

常见误区: 切勿混淆赋值 () 与比较 (=)。在伪代码中, 是将数值“放入”变量中。

输入、输出与表达式

程序必须与用户交互 (输入) 并显示结果 (输出)。

输入: INPUT Age (从键盘读取数据并存入变量 Age)。
输出: OUTPUT "Your age is ", Age (在控制台显示文本及 Age 的值)。

表达式 (Expression) 结合了数值、变量与运算符 (算术或逻辑) 来产生一个单一结果。

算术运算符: +, -, *, /, DIV (整除), MOD (取余数)。
逻辑运算符: AND, OR, NOT

内置函数 (Built-in Functions)

使用内置函数库例程 (Library Routines) 可为程序员节省时间,并确保代码的可靠性。

字符串函数:
LENGTH(String1) – 返回字符串中的字符数量。
SUBSTRING(String1, Start, Length) / MID(String1, Start, Length) – 截取子字符串。
LCASE(String1)UCASE(String1) – 转换为小写或大写。
ASC(Char1) – 返回字符的 ASCII 数值。
CHR(Integer1) – 返回对应 ASCII 数值的字符。
日期函数: DATE, DAY, MONTH, YEAR – 截取日期组成部分或当前日历日期。

要点 11.1: 使用前务必先声明变量和常量,选择合适的数据类型,并了解如何使用算术/逻辑运算符以及内置字符串/日期函数。

11.2 结构 (控制结构)

任何算法的三个基本构件是顺序 (Sequence)、选择 (Selection) 和迭代 (Iteration)。我们使用控制结构来实现选择和迭代。

选择 (决策制定)

选择结构允许程序根据条件 (布尔表达式) 选择执行路径。

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 STEP 1
OUTPUT Counter
NEXT Counter

2. 前置条件循环 (WHILE 循环)

在执行循环主体之前先检查条件。循环可能会执行零次或多次。
当终止条件取决于外部因素 (如用户输入或文件状态) 时使用。
伪代码范例:
WHILE InputValue <> -1 DO
// 循环主体执行
ENDWHILE

3. 后置条件循环 (REPEAT UNTIL 循环)

在执行循环主体之后检查条件。该循环保证至少执行一次。
常考用于确保有效输入 (不断提示用户直至输入可接受的数据)。
伪代码范例:
REPEAT
INPUT Password
UNTIL Password = "secret"

如何合理选择循环:

如果问题需要重复特定次数 (例如输入 5 个分数),使用 FOR 循环。如果必须确保至少执行一次 (例如输入验证),使用 REPEAT UNTIL。如果循环可能一次都不需要执行 (例如处理空文件中的记录),则使用 WHILE

要点 11.2: 选择结构根据决策控制流程 (IF/CASE)。迭代结构控制重复 (FOR, WHILE, REPEAT)。请务必了解何时使用哪种循环!

11.3 结构化程序设计 (过程与函数)

结构化程序设计涉及将大型程序分解为较小、独立的模块。这使得代码编写、调试及维护更容易。这些模块称为子程序 (Subroutines):过程 (Procedures) 和函数 (Functions)。

过程 (做某事的子程序)

过程是一个已命名的代码块,用于执行特定任务,但不直接向表达式返回值

适合用途: 涉及输入/输出或直接修改变量的任务 (例如将数据存储到文件,或打印菜单)。
语法: 使用 PROCEDURE 定义,并通过名称直接调用。

范例:输出欢迎消息的过程。

函数 (计算并返回值的子程序)

函数是一个已命名的代码块,用于计算数值并将该值返回给调用它的表达式。

适合用途: 数学计算、字符串处理或检查条件 (例如计算税款,或查找名称长度)。
语法: 使用 FUNCTION 定义,并在表达式中使用,返回值会取代函数调用的位置。

范例:Area ← CalculateArea(5, 10)

参数与实参

为了让过程和函数更灵活,我们通过参数 (Parameters) 传递数据。

参数 (Parameter): 定义在过程/函数标头中,用于接收数据的标识符。
实参 (Argument): 调用过程/函数时实际传递的数值或变量。

参数传递方式分为两种:

1. 按值传递 (Pass By Value)

传递的是实参的副本。
在子程序内部对参数所作的任何更改,都不会影响主程序中的原始变量 (实参)。
比喻: 给某人一份你的笔记复印件。他们可以在复印件上乱画,但你的原始笔记保持原样。

2. 按引用传递 (Pass By Reference)

传递的是实参的内存地址 (引用)。
在子程序内部对参数所作的任何更改,都会影响主程序中的原始变量 (实参)。
比喻: 给某人你储物柜的真实钥匙。他们可以永久更改里面的内容。

术语速查:
过程标头 (Procedure Header): 定义模块的第一行代码 (例如:PROCEDURE PrintName(ByValue N : STRING))
函数接口 (Function Interface): 定义函数的名称、参数及返回类型。
实参 (Argument): 调用函数/过程时传递的实际数据。
要点 11.3: 过程执行任务;函数计算并返回值。按值传递保护原始数据;按引用传递允许子程序修改原始数据。

12. 软件开发

编写代码 (第 11 章) 只是工作的一部分。第 12 章探讨构建大型、可靠软件系统所需的结构与纪律。

12.1 程序开发生命周期 (PLC)

程序开发生命周期 (Program Development Life Cycle, PLC) 是一套结构化的框架,描述了开发、维护及最终弃用软件系统所需的步骤。其目的是确保质量、管理复杂性并满足用户需求。

PLC 阶段

1. 分析 (Analysis): 定义问题并识别用户需求。(系统需要做什么?)
2. 设计 (Design): 规划解决方案结构、算法及接口。(系统将如何实现?)
3. 编码/实现 (Coding / Implementation): 编写程序代码。
4. 测试 (Testing): 检查程序错误并确保其满足要求。
5. 维护 (Maintenance): 在部署后修复程序错误并进行更新。

开发生命周期模型

不同类型的程序需要不同的处理方法。

1. 瀑布模型 (Waterfall Model)

原则: 每个阶段必须在下一个阶段开始前完成,严格向下流动。
优点: 简单、结构高度化,易于管理小型且目标明确的项目。
缺点: 非常缺乏弹性;如果后期需求变更,回溯成本极高且困难。

2. 迭代模型 (Iterative Model)

原则: 项目在小型、重复的循环 (迭代) 中开发。早期建立可运作的原型并随时间精炼。
优点: 允许早期用户反馈,并能轻松适应变更的需求。
缺点: 若管理不当,可能导致“范围蔓延”(项目无止境地扩大)。

3. 快速应用程序开发 (RAD)

原则: 通过持续的用户参与快速建立原型,减少规划时间。
优点: 对于用户界面至关重要且需求稍具弹性的项目,开发速度非常快。
缺点: 不适用于极其复杂、大型的系统,或需要严格保安/安全性的情况 (如航空控制软件)。

要点 12.1: PLC 指导软件创作。瀑布模型刻板;迭代模型与 RAD 则具弹性,包含多次设计、编码与测试循环。

12.2 程序设计

设计阶段 (PLC 第 2 阶段) 使用工具在编写任何一行代码前规划程序结构。

结构图 (Structure Charts)

结构图是一种层次化图表,用于自顶向下设计 (top-down design),将问题分解为更小、易于管理的子任务 (模块、过程或函数)。

目的: 显示程序的整体结构以及各模块间的关系。
关键特征: 明确显示模块间传递的参数/数据 (数据箭头用圆形表示,控制标志/状态则用菱形表示)。

优点: 促进模块化并有助于确保模块彼此独立,使测试变得更容易。

状态转移图 (State-Transition Diagrams)

状态转移图记录了在不同状态 (条件) 下运行的算法。

目的: 用于建模当前动作取决于过去输入历史的系统 (例如交通灯、自动售货机、简单登录系统)。
结构: 圆形表示状态 (States) (条件),箭头表示转移 (Transitions) (引起状态改变的动作或输入)。

范例:咖啡机的状态机可能包括“等待硬币”、“已投入硬币”、“正在制作咖啡”等状态。

要点 12.2: 结构图定义了模块间的层次与数据流。状态转移图则对行为随当前状态改变的系统进行建模。

12.3 程序测试与维护

无法正确运作的程序是毫无用处的。本节探讨如何发现、预防及修复错误。

错误类型

错误 (或故障/程序缺陷) 主要分为三类:

1. 语法错误 (Syntax Error): 违反编程语言的语法规则。翻译器 (编译器/解释器) 会捕捉到这些错误。范例:将 WHILE 误拼为 WHIL
2. 逻辑错误 (Logic Error): 程序成功执行,但产生错误的输出,因为算法逻辑不正确。翻译器无法捕捉这些错误。范例:应使用乘法的地方却误用了加法。
3. 运行时错误 (Run-time Error): 在程序执行时发生,导致程序崩溃或异常停止。范例:除以零,或尝试访问不存在的数组索引。

测试策略与数据

测试策略 (Test Strategy) 定义了整体的测试方针,而测试计划 (Test Plan) 则是一份具体文件,详细说明将测试什么内容、如何测试以及使用什么数据。

你必须选择合适的测试数据 (Test Data)

正常数据 (Normal Data): 预期输入的数据 (处于可接受范围之内)。例如:若范围是 1 到 100,使用 50。
极端/边界数据 (Extreme/Boundary Data): 处于可接受范围临界点的数据。这是最容易隐藏错误的地方!例如:若范围是 1 到 100,使用 1 和 100。
异常数据 (Abnormal Data): 意图让程序崩溃的无效数据 (例如格式错误、超出范围)。例如:若预期输入 1 到 100 的数字,却输入 "Apple" 或 101。

测试方法

这些方法有助于发现代码中的缺陷:

预演 (Dry Run): 使用追踪表手动逐行执行代码 (通常由程序员在纸上完成)。
走查 (Walkthrough): 由作者带领开发团队,手动逐行检查代码。
白盒测试 (White-Box Testing): 基于了解程序内部结构与代码路径进行测试。确保每一行代码至少执行一次 (通常由开发者进行)。
黑盒测试 (Black-Box Testing): 仅基于规格说明与预期输出进行测试,不考虑内部代码结构 (通常由独立测试团队进行)。
集成测试 (Integration Testing): 测试各个别模块共同运作时,相互通信是否正确。
Alpha 测试: 发布前由内部测试人员进行测试。
Beta 测试: 发布前由真实用户在现实环境中进行测试。
验收测试 (Acceptance Testing): 客户或终端用户确认系统满足所有合同需求的正式测试。
存根 (Stub): 测试期间使用的临时占位模块,用以模拟尚未编写的模块功能。

程序维护

软件在部署后必须持续进行维护。

纠正性维护 (Corrective Maintenance): 修复系统投入使用后发现的缺陷与错误。
适应性维护 (Adaptive Maintenance): 修改系统以适应环境变化 (例如新的操作系统、新税法)。
完善性维护 (Perfective Maintenance): 提升软件的性能、效率或可维护性 (例如优化慢速代码、改善用户界面)。

要点 12.3: 测试必须严谨,包括正常、极端及异常数据。逻辑错误最难发现。维护至关重要,分为纠正性、适应性及完善性三类。