子程序(subroutine)是一段有名字、只做一件事的代码,这样你可以多次使用而不用重写。参数(parameter)是它所需要的有名字的输入,而函数还会返回一个答案。
本课属于程序结构模块。它接在局部与全局作用域之后,因为参数正是替代“伸手去拿全局变量”的干净做法。
我该怎样设计子程序?
按顺序做四个决定。
- **给任务起名。**用能说明作用的名字,例如
HireFee。 - **列出输入。**子程序必须知道什么?每一项都变成一个带类型的参数。
- **决定输出。**如果调用者需要答案,就写带
RETURNS和类型的 FUNCTION;如果只是做一件事,就写 PROCEDURE。 - **让函数体自成一体。**内部只使用参数和局部变量。
例题
某虚构社区礼堂按小时收费。预订 5 小时或以上,总价可减 10%。请写一个返回费用的函数。
输入:小时数(INTEGER)和每小时费率(REAL)。输出:费用(REAL)。
FUNCTION HireFee(Hours : INTEGER, Rate : REAL) RETURNS REAL
DECLARE Fee : REAL
Fee ← Hours * Rate
IF Hours >= 5 THEN
Fee ← Fee * 0.9
ENDIF
RETURN Fee
ENDFUNCTION
OUTPUT HireFee(6, 20.00)
OUTPUT HireFee(3, 20.00)
追踪第一次调用 HireFee(6, 20.00):
| 行 | Hours | Rate | Fee |
|---|---|---|---|
| 调用 | 6 | 20.00 | 未设置 |
| Fee ← Hours * Rate | 6 | 20.00 | 120.00 |
| IF Hours >= 5 为 TRUE | 6 | 20.00 | 120.00 × 0.9 = 108.00 |
| RETURN | 6 | 20.00 | 返回 108.00 |
第二次调用中 Hours = 3,条件为 FALSE,函数返回 3 × 20.00 = 60.00。两个输出分别是 108.00 和 60.00。
作为对照,同一个函数的 Python 写法:
def hire_fee(hours, rate):
fee = hours * rate
if hours >= 5:
fee = fee * 0.9
return fee
最常见的错误是什么?
**错误答案:**预订是 4 小时、每小时 RM25,却调用了
HireFee(25, 4)。第一个实参按位置传给
Hours,第二个传给Rate。这里Hours变成了 25,于是折扣生效,函数返回 25 × 4 × 0.9 = 90,而不是正确的 4 × 25 = 100。
程序不会崩溃,所以这种错误很难发现。正确的调用是 HireFee(4, 25.00),得到 100.00。写调用时,请按顺序把每个实参和对应的形参核对一遍。
第二个错误是在本来只负责计算的函数里放 INPUT 或 OUTPUT,这在把逻辑与输入输出分开中讲。
自测
1. 在 HireFee(Hours : INTEGER, Rate : REAL) 中,哪些是形参?在 HireFee(8, 15.00) 中,实参是什么?
显示答案
形参:Hours 和 Rate。实参:8 和 15.00。
2. 写出 HireFee(8, 15.00) 返回的值。
显示答案
8 × 15.00 = 120.00。Hours 为 8,不小于 5,所以享受折扣:120.00 × 0.9 = 108.00。
3. 写一个过程 Greet,接收 STRING 参数 Name,输出 “Hello, ” 加上名字。并写出用虚构名字 Aina 调用它的语句。
显示答案
PROCEDURE Greet(Name : STRING)
OUTPUT "Hello, " & Name
ENDPROCEDURE
CALL Greet("Aina")
输出是 Hello, Aina。因为不需要返回任何值,所以用过程。
接下来学什么?
函数用 RETURN 交回答案。想准确看清哪个值在什么时候返回,请继续学习追踪返回值。你可以在安全 Python 推理沙盒里运行 HireFee,也可以在受限伪代码追踪训练器里逐步追踪。
如果设计参数列表是你最容易卡住的地方,我们的一对一线上 Computer Science 补习会从你手上的作业出发来练习。