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: 仅有两个值:TRUEFALSE非常适合用作标志位或条件判断。

声明与赋值:

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

  • 变量声明: 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.1 节要点: 使用前务必声明变量和常量,选择合适的数据类型,并理解输入、处理(表达式)和输出之间的区别。

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.2 节要点: 选择结构基于决策(IF/CASE)控制流程。迭代结构控制重复(FOR, WHILE, REPEAT)。一定要知道何时使用哪种循环!

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

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

过程(执行特定任务的子程序)

过程是一个已命名的代码块,它执行特定任务,但不一定向主程序返回数值

  • 适用场景: 涉及输入/输出的任务,或直接修改变量(如将数据保存到文件、打印菜单)。
  • 语法: 使用 PROCEDURE 定义,并直接通过其名称调用。
函数(计算并返回结果的子程序)

函数是一个已命名的代码块,它执行计算并将结果返回到调用它的表达式中。

  • 适用场景: 数学运算、字符串处理或条件判断(如计算税额、获取名字长度)。
  • 语法: 使用 FUNCTION 定义,并用于表达式中,返回的值会替换掉调用处。
参数与实参

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

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

参数有两种传递方式:

1. 按值传递 (Pass By Value)

  • 传递的是实参的一个拷贝。
  • 子程序内部对参数的任何修改都不会影响主程序中的原始变量。
  • 类比: 给某人一份笔记的复印件。他们在复印件上乱涂乱画,你的原始笔记依然完好无损。

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

  • 传递的是实参的内存地址(引用)。
  • 子程序内部对参数的任何修改都会影响主程序中的原始变量。
  • 类比: 把储物柜的钥匙交给别人。他们可以永久改变里面的内容。
术语表:
  • 过程标题: 定义模块的第一行(如 PROCEDURE PrintName(ByValue N)
  • 函数接口: 定义函数的名称、参数和返回类型。
  • 实参: 调用函数/过程时传入的实际数据。
第 11.3 节要点: 过程执行任务;函数计算并返回值。按值传递保护原始数据;按引用传递允许子程序修改原始数据。

12. 软件开发

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

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

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

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

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

1. 瀑布模型 (Waterfall Model)

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

2. 迭代模型 (Iterative Model)

  • 原则: 项目在小的、重复的周期(迭代)中开发。及早创建工作原型并随时间优化。
  • 优点: 允许及早获得用户反馈,易于适应变化的需求。
  • 缺点: 如果管理不当,可能导致“范围蔓延”(项目无限扩大)。

3. 快速应用开发 (RAD)

  • 原则: 通过持续的用户参与快速创建原型,减少规划时间。
  • 优点: 对于用户界面至关重要且需求相对灵活的项目,开发速度非常快。
  • 缺点: 不适合极度复杂、大规模的系统,或对安全性要求极高的系统(如飞机控制软件)。
第 12.1 节要点: PLC 指导软件创建。瀑布模型是刚性的;迭代模型和 RAD 是灵活的,涉及设计、编码和测试的反复循环。

12.2 程序设计

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

结构图 (Structure Charts)

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

  • 目的: 展示程序的整体结构以及模块之间的关系。
  • 核心特征: 明确显示模块之间传递的参数/数据(圆圈代表数据,菱形箭头代表控制标志)。

优点: 鼓励模块化,有助于确保模块独立性,从而使测试更容易。

状态转换图 (State-Transition Diagrams)

状态转换图 用于记录在不同状态下运行的算法。

  • 目的: 用于建模当前动作依赖于之前输入历史的系统(如交通灯、自动售货机、简单的登录系统)。
  • 结构: 圆圈代表状态,箭头代表转换(导致从一种状态变为另一种状态的动作或输入)。
第 12.2 节要点: 结构图定义了模块间的层级和数据流。状态转换图模拟根据当前状态改变行为的系统。

12.3 程序测试与维护

如果程序无法正常工作,它就是无用的。本节讨论如何查找、预防和修复错误。

错误类型

错误(或缺陷/Bug)分为三大类:

  1. 语法错误 (Syntax Error): 违反了编程语言的语法或规则。翻译器(编译器/解释器)会捕捉到这些错误。例子:将 WHILE 拼写为 WHIL
  2. 逻辑错误 (Logic Error): 程序运行成功,但输出结果错误,因为算法本身不正确。翻译器无法捕捉这些错误。例子:本该相乘却写成了加法。
  3. 运行错误 (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): 提升软件性能、效率或可维护性。
第 12.3 节要点: 测试必须严格,包括正常、极端和异常数据。逻辑错误最难发现。维护分为纠正性、适应性和完善性三类。