Programming Basics
A-Level Computer Science Topic 11 24:06 English narration · English + 中文 subtitles burned in
Chapters
Transcript
A program must ask for a password, and keep asking until it is right.
一个程序要请用户输入密码,并且一直问下去,直到输对为止。
Simple — but which loop do you use?
听起来简单——但你该用哪种循环呢?
Programs make decisions and repeat work, and choosing the wrong structure means bugs.
程序会做判断、会重复工作,而选错了结构就意味着漏洞。
Should the test come before the body, or after?
测试应该放在循环体之前,还是之后?
Should it run a fixed number of times, or until something happens?
它该运行固定的次数,还是直到某件事发生为止?
Master selection and iteration, and you can build the logic of any program.
掌握了选择和迭代,你就能构建出任何程序的逻辑。
Every program is built from decisions and loops, wrapped in reusable subroutines.
每个程序都由判断和循环构成,再包裹进可复用的子程序里。
Today: selection, the three kinds of loop, procedures and functions, how parameters pass, and variable scope.
今天我们讲: 选择、三种循环、过程与函数、参数如何传递,以及变量的作用域。
Let's begin.
让我们开始吧。
Programming starts from a design — a program flowchart, or structured English — and your job is to turn it into pseudocode.
编程从一份设计开始——一张程序流程图,或者一段结构化英语——你的任务是把它变成伪代码。
Work through it in order.
按顺序一步一步来。
First, find the variables and their data types.
第一步,找出所有变量和它们的数据类型。
Turn each input or output box into INPUT or OUTPUT.
把每一个输入或输出的平行四边形变成 INPUT 或 OUTPUT。
Turn every decision diamond into an IF, or a CASE.
把每一个判断菱形变成 IF,或者 CASE。
Turn a loop arrow into a WHILE, a REPEAT, or a FOR.
把循环箭头变成 WHILE、REPEAT 或 FOR。
Turn process boxes into assignments and calculations.
把处理框变成赋值和计算。
Then check your work by tracing a small input by hand, and see whether the numbers come out right.
最后,用一个很小的输入手工追踪一遍,看看算出来的数对不对。
A variable holds a value that may change while the program runs.
变量存放的值在程序运行时可能改变。
A constant holds a value that never changes.
常量存放的值永远不变。
Declare a constant with the word CONSTANT and give it its value once — Pi is three point one four one five nine, forever.
用 CONSTANT 这个词声明一个常量,并且一次性给定它的值——圆周率就是三点一四一五九,永远如此。
Declare a variable with DECLARE, a colon, and its data type: Radius is a REAL, and so is Area.
用 DECLARE、一个冒号和数据类型来声明一个变量:半径是实数,面积也是实数。
Now assign.
现在来赋值。
Radius becomes five, and Area becomes Pi times Radius times Radius.
半径变成五,面积变成圆周率乘半径再乘半径。
Use constants for fixed values that recur, like Pi or MaxScore: the code reads better, and one edit changes every use.
对于反复出现的固定值,比如圆周率或者最高分,就用常量:代码更好读,而且改一处就改了所有用到的地方。
A constant is written CONSTANT Pi arrow three point one four one five nine, and the point of declaring one is that the value cannot be changed accidentally later in the program.
常量写成 CONSTANT Pi 箭头 三点一四一五九, 而声明成常量的意义在于:后面的程序里这个值不会被意外改掉。
The name you give either of them is its identifier, and a good identifier says what the thing is.
你给它们起的名字就是标识符,好的标识符能说清这个东西是什么。
Use a constant for a fixed value that recurs — Pi, MaxScore, VAT — because it makes the code clearer and, when the value has to change, it changes in exactly one place.
反复出现的固定值就用常量——Pi、MaxScore、税率—— 因为这样代码更清楚,而且要改值的时候只需要改一个地方。
Assignment uses a left arrow.
赋值用一个左箭头。
Read it from right to left: work out the expression on the right, then store the result in the name on the left.
要从右往左读:先算出右边的表达式,再把结果存进左边那个名字里。
So Total gets Total plus one.
所以,总数变成总数加一。
Take the old value, seven, add one, and store eight back in the very same box — that is how a counter counts.
取出旧的值,七,加上一,再把八存回同一个盒子里——计数器就是这样计数的。
And Average gets Sum divided by Count.
平均值变成总和除以个数。
One warning: the name on the left is a destination, not a question.
一句提醒:左边的名字是一个存放的目的地,不是一个问题。
This arrow is never a test for equality.
这个箭头绝不是在判断两边是否相等。
Expressions are built from operators.
表达式是由运算符搭起来的。
Arithmetic gives you plus, minus, times and divide — and two more you must know.
算术运算给你加、减、乘、除——还有两个你必须掌握的。
DIV is integer division, and MOD is the remainder.
DIV 是整除,MOD 是取余数。
So seven DIV two is three, because two goes into seven three whole times, and seven MOD two is one, because one is what is left over.
所以七 DIV 二等于三,因为二在七里面整整装得下三次; 七 MOD 二等于一,因为剩下的就是一。
Comparisons are equals, not-equals, less than, greater than, and the two or-equal forms.
比较运算有等于、不等于、小于、大于,以及两个带等号的形式。
And logic gives you AND, OR and NOT, which glue conditions together into a single test.
逻辑运算给你 AND、OR 和 NOT,它们把几个条件粘成一个判断。
When one expression mixes operators, precedence decides what happens first.
当一个表达式里混着好几种运算符时,优先级决定先算哪一个。
Highest of all is NOT.
最高的是 NOT。
Then multiply, divide, DIV and MOD.
接着是乘、除、整除和取余。
Then plus and minus.
然后是加和减。
Then the comparisons.
然后是各种比较。
Then AND — and lowest of all, OR.
然后是 AND——最低的是 OR。
So inside a condition the arithmetic runs first, then the comparison, then the logic, which is usually what you meant anyway.
所以在一个条件里,先算算术,再做比较,最后做逻辑判断,这通常正好就是你想要的意思。
When you are not sure, add brackets.
拿不准的时候,就加括号。
They cost nothing, they cannot be wrong, and they show the examiner exactly what you intended.
括号不花任何代价,也绝不会错,还能让阅卷老师一眼看出你的意图。
Input and output are two keywords.
输入和输出是两个关键字。
OUTPUT writes a message to the screen.
OUTPUT 把一条信息写到屏幕上。
INPUT reads a value from the user and stores it in a variable.
INPUT 从用户那里读进一个值,并把它存到一个变量里。
Always OUTPUT a prompt before you INPUT, or the user just stares at a blank screen wondering what to type.
在 INPUT 之前一定要先 OUTPUT 一句提示,不然用户只会盯着一片空白的屏幕,不知道该输入什么。
Then OUTPUT can print several items separated by commas — the text Hello, then the value sitting in Name.
OUTPUT 还可以用逗号隔开、一次打印好几项——先是"你好"这段文字,再是变量里存着的那个名字。
So run it, type Ada, and the screen says Hello, Ada.
运行它,输入 Ada,屏幕上就会显示:你好,Ada。
Many jobs are already written for you as library routines, so do not waste exam time rebuilding them.
很多常见的活儿已经作为库例程替你写好了,所以别在考试里浪费时间重造它们。
The string routines: LENGTH, LEFT, RIGHT, MID, UCASE and LCASE.
字符串例程:取长度、取左边、取右边、取中间、转大写、转小写。
The numeric routines: INT throws away the decimal part, ROUND rounds to the nearest whole number, ABS gives the size without a sign, MOD the remainder, and RANDOM a random number.
数值例程:INT 丢掉小数部分,ROUND 四舍五入到最近的整数,ABS 给出不带正负号的大小, MOD 给出余数,RANDOM 给出一个随机数。
The conversion routines: STR turns a number into a string, and VAL turns a string into a number.
转换例程:STR 把数字变成字符串,VAL 把字符串变成数字。
In the exam, use the exact names printed on the paper's reference list.
考试时,要用试卷参考表上印的那个确切名字。
A program library holds routines that have already been written, compiled and tested, and your program calls them instead of writing its own.
程序库里放的是已经写好、编译好、测试过的例程,你的程序直接调用它们,而不是自己写一遍。
If a question asks you to state three benefits, the scheme accepts these: they are already tested, so they are less likely to contain errors; they save development time; they may do things you could not write yourself, like complex statistics or graphics; they are written by experts and reused across many programs; and one with a fixed interface can be called from anywhere.
如果题目要你「说出三个好处」,评分标准接受这些: 它们已经被测试过,所以出错的可能性更小;它们节省开发时间; 它们能做你自己写不出来的事,比如复杂的统计或者图形; 它们由专家编写,并且在很多程序里被重复使用; 而且接口固定的例程可以从程序的任何地方调用。
The Paper 2 insert lists every routine you may use, with its exact name, its parameters and its return type.
Paper 2 的附页会列出你可以用的每一个例程,包括准确的名字、参数和返回类型。
Use those names.
就用附页上的名字。
The IGCSE names — UCASE, VAL, STR — score nothing here.
IGCSE 的那套名字——UCASE、VAL、STR——在这里一分都不给。
Let us run those string routines on one word.
我们拿一个单词来跑一遍这些字符串例程。
Take s equals COMPUTER — eight letters, numbered one to eight.
设字符串等于 COMPUTER——八个字母,编号一到八。
LENGTH of s counts them: eight.
取长度会数一数:八。
LEFT of s, three takes three characters from the left: C, O, M — COM.
取左边三个字符,就是 C、O、M——COM。
MID of s, four, three starts at position four and takes three: P, U, T — PUT.
取中间,从第四位开始取三个:P、U、T——PUT。
RIGHT of s, two takes the last two: E, R — ER.
取右边两个:E、R——ER。
And UCASE and LCASE only change the case, so lower-case computer comes back as capital COMPUTER.
转大写和转小写只改变字母的大小写,所以小写的 computer 会变回大写的 COMPUTER。
One more thing the insert will not spell out for you: strings are joined with the ampersand, and that operation is called concatenation.
还有一件附页不会替你说明的事:字符串是用 & 号连接的,这个操作叫做连接。
"A" ampersand "BC" gives "ABC".
"A" 和号 "BC" 得到 "ABC"。
Plus does not join strings in this pseudocode, and writing it is a lost mark.
在这套伪代码里加号不能连接字符串,写了就是丢分。
The same section is where Validation lives in an exam answer: IS_NUM tells you whether a string is a valid number before you try to convert it, which is how you check input without the program falling over on a run-time error.
同一部分也是考试答案里做验证的地方: IS_NUM 告诉你一个字符串是不是有效的数字,让你在尝试转换之前先判断, 这就是在不让程序因为运行时错误崩掉的前提下检查输入的方法。
Selection chooses which steps run.
选择决定哪些步骤会运行。
An if-then-else tests a condition once, then runs exactly one branch — adult or minor, never both.
"如果-那么-否则"把一个条件测试一次,然后只运行其中一个分支—— 成年或未成年,绝不会两个都运行。
When you test one value against many options, a chain of ifs gets messy.
当你要拿一个值去和许多选项比较时,一长串的"如果"会变得杂乱。
A case statement is cleaner: it matches the value against each option in turn — a single value, a list, or a range — and runs the first one that fits.
用一个 CASE 语句会更清爽:它把这个值依次和每个选项匹配——单个值、一个列表,或一个范围—— 并运行第一个符合的那个。
For more than two outcomes you can nest one IF inside another.
要处理两个以上的结果,你可以把一个 IF 嵌套在另一个 IF 里面。
It works, but every level indents further, and deep nesting is genuinely hard to read and easy to get wrong.
这样能跑, 但每多一层就多缩进一层,嵌套太深真的很难读,也很容易写错。
When you are testing one value against several options, a CASE is cleaner.
当你拿一个值去和好几个选项比较时,用 CASE 更清爽。
Cambridge pseudocode lets each branch guard be a single value, or a list of values separated by commas, or a range written with the word TO.
剑桥伪代码允许每个分支的条件是一个单独的值,或者用逗号隔开的一串值,或者用 TO 写出的一个范围。
OTHERWISE catches everything else, and only the first branch that matches ever runs.
OTHERWISE 兜住所有其他情况,而且只有第一个匹配上的分支会运行。
Iteration repeats a block, and there are three kinds — they differ in when the test happens.
迭代重复运行一个代码块,而它有三种——区别在于测试发生的时机。
A for loop runs a fixed number of times; use it when you know the count.
for 循环运行固定的次数; 当你知道次数时就用它。
A while loop tests before the body, so it can run zero times.
while 循环在循环体之前测试,所以它可能运行零次。
A repeat-until loop tests after the body, so it always runs at least once.
repeat-until 循环在循环体之后测试,所以它至少运行一次。
Choosing the right one is a classic exam question — ask whether the count is known, and whether the body must run at least once.
选对循环是一道经典的考题—— 问问自己:次数是否已知,以及循环体是否必须至少运行一次。
The count-controlled loop is FOR.
计数循环就是 FOR。
Write FOR, the counter, a left arrow, the start value TO the end value, then the body, then NEXT and the counter's name.
写下 FOR、计数器、一个左箭头、起始值 TO 终止值,然后是循环体, 最后是 NEXT 加上计数器的名字。
This one sets i to one and outputs it, then comes back for two, three, and on up to ten — exactly ten passes, and that count was decided before the loop even began.
这个循环把 i 设成一并输出,然后回来做二、三,一直到十——正好十次, 而且这个次数在循环开始之前就已经定下来了。
A STEP changes the size or the direction of the count, so i from ten TO one STEP minus one counts backwards.
STEP 可以改变每次跨的大小或者方向,所以 i 从十 TO 一、STEP 负一,就是倒着数。
Use FOR for a fixed number of repeats, or to visit every element of an array.
当重复次数固定,或者要访问数组里的每一个元素时,就用 FOR。
The pre-condition loop is WHILE, and it tests before every pass.
前测循环是 WHILE,它在每一次进入循环体之前都先测试。
While total is under one hundred, read a number and add it on.
当总数小于一百时,就读进一个数并把它加上去。
Let us trace it.
我们来追踪一遍。
Total starts at zero, so we go in: add forty, and total is forty.
总数从零开始,所以进得去:加四十,总数是四十。
Test again — still under a hundred, so add fifty, and total is ninety.
再测一次——还是小于一百, 于是加五十,总数是九十。
Add forty more, total is one hundred and thirty, the test fails, and the loop ends.
再加四十,总数变成一百三十,测试不通过,循环结束。
And here is the whole point of WHILE: if total had already been one hundred at the start, the body would have run zero times.
而这正是 WHILE 的关键所在:如果总数一开始就已经是一百,循环体会一次都不运行。
Notice what that means at the extreme: because the condition is tested before the body, if it is false the very first time, the body may never be performed at all.
注意这在极端情况下意味着什么:因为条件是在循环体之前判断的, 如果第一次判断就是假,那么循环体可能一次都不会被执行。
That is not a bug — it is the whole reason to choose a pre-condition loop.
这不是缺陷——这正是选择前置条件循环的全部理由。
Reading a file that turns out to be empty should do nothing, not process one imaginary line.
读一个恰好是空的文件,就应该什么都不做,而不是去处理一行并不存在的数据。
The post-condition loop is REPEAT, and it tests after the pass.
后测循环是 REPEAT,它在跑完一次之后才测试。
REPEAT, ask for the password, UNTIL the password matches the correct one.
REPEAT,请用户输入密码,UNTIL 密码和正确的那个相符。
The body runs first, so the user is always asked at least once — which is exactly right, because before you ask there is nothing to test.
循环体先运行,所以用户总是至少被问一次——这恰恰是对的, 因为在你问之前根本没有东西可以测试。
A wrong first attempt sends us round again.
第一次输错,就把我们送回去再来一遍。
A correct second attempt satisfies the UNTIL, and out we go.
第二次输对,满足了 UNTIL,我们就出去了。
So WHILE may run zero times, while REPEAT always runs at least one.
所以 WHILE 可能运行零次,而 REPEAT 总是至少运行一次。
Now the classic exam question: which loop suits each task?
现在来看这道经典考题:每个任务分别适合哪一种循环?
First, print the twelve times table.
第一,打印十二的乘法表。
The count is known in advance, twelve, so this is a FOR loop.
次数事先就知道,十二次,所以这是一个 FOR 循环。
Second, keep reading numbers until the user enters zero.
第二,不停地读数,直到用户输入零。
The count is unknown, and the very first number might already be zero, so the test must come before the body: a WHILE, which runs zero or more times.
次数不知道,而且第一个数就可能已经是零, 所以测试必须放在循环体之前:用 WHILE,它可以运行零次或更多次。
Third, ask for a password until it is correct.
第三,请用户输入密码,直到输对为止。
The count is unknown again, but you must always ask once before there is anything to test, so the test comes after the body: a REPEAT UNTIL, which runs one or more times.
次数同样不知道, 但你必须先问一次,才有东西可以测试,所以测试放在循环体之后: 用 REPEAT UNTIL,它至少运行一次。
The deciding question is always whether the body must run at least once.
决定性的问题永远是:循环体是不是必须至少运行一次。
A trace table records the value of every variable as you dry run an algorithm by hand, and it is a six-mark question on most Paper 2s.
跟踪表记录你手工执行算法时每个变量的值,在大多数 Paper 2 里这是一道六分题。
One column per variable, in the order the question gives them, then a column for the condition and a column for the output.
每个变量一列,顺序按题目给的来,然后一列写条件,一列写输出。
Here Count starts at one and Total at zero.
这里 Count 从一开始,Total 从零开始。
First pass: the condition Total less than ten is TRUE, so Total becomes zero plus one times two, which is two, and Count becomes two.
第一遍:条件 Total 小于十为真,于是 Total 变成零加一乘二,等于二,Count 变成二。
Second pass: TRUE again, Total becomes two plus two times two, which is six, and Count becomes three.
第二遍:仍然为真,Total 变成二加二乘二,等于六,Count 变成三。
Third pass: still TRUE, Total becomes six plus three times two, which is twelve, and Count becomes four.
第三遍:还是真,Total 变成六加三乘二,等于十二,Count 变成四。
Fourth pass: twelve is not less than ten, so the condition is FALSE and the loop stops.
第四遍:十二不小于十,条件为假,循环停止。
Only then does the OUTPUT line run, giving four and twelve.
这时候 OUTPUT 那一行才执行,输出四和十二。
Three rules earn the marks.
有三条规则决定得分。
Write a value only when it changes, so most cells stay blank.
只在值发生变化时才写,所以大多数格子是空的。
Start a new row each time the loop repeats.
循环每重复一次就新起一行。
And trace the algorithm as it is written, not the one you think was intended: if it never stops, say so.
而且要按写出来的算法去跟踪,不是按你以为它想表达的那个:如果它永远停不下来,就直说。
To keep code tidy, break it into small named subroutines.
为了让代码整洁,把它拆成一个个有名字的小子程序。
There are two kinds.
子程序有两种。
A procedure does an action — like printing a greeting — and returns nothing.
过程做一个动作—— 比如打印一句问候——并且不返回任何值。
A function computes a value and returns it, so you can use it inside an expression, like the square of five, plus one.
函数计算出一个值并把它返回, 这样你就能在表达式里用它,比如五的平方,再加一。
The rule: use a procedure for an action, and a function for a value.
规则是:动作用过程,值用函数。
Both are subroutines, and the definition the examiner wants for a subroutine is a self-contained block of code that performs a task and is called by name.
两者都是子程序,而考官要的「子程序」定义是: 一段自成一体的代码,完成一项任务,通过名字被调用。
Self-contained is the load-bearing word: it has its own local variables, it can be tested on its own, and the rest of the program only needs its interface.
「自成一体」是关键词:它有自己的局部变量,可以单独测试, 程序的其他部分只需要知道它的接口。
Here they are in pseudocode.
来看它们写成伪代码的样子。
A procedure starts with a header — the word PROCEDURE, its name, and its parameters in brackets with their types.
过程从一个头部开始——PROCEDURE 这个词、它的名字, 再加上括号里带类型的参数。
The body does the work, and ENDPROCEDURE closes the block.
循环体里的语句干正事,ENDPROCEDURE 把这个块收尾。
You run it with CALL Greet, Ada: the word CALL is how a procedure is invoked.
用 CALL Greet、括号里写上 Ada 来运行它:CALL 就是调用一个过程的方式。
A function's header ends with RETURNS and the type of the value it gives back, and inside the block, RETURN hands that value to the caller.
函数的头部结尾要写 RETURNS 和它送回的那个值的类型, 而在块里面,RETURN 把这个值交回给调用者。
So Square of five returns twenty-five, and result becomes twenty-five plus one — twenty-six.
所以五的平方返回二十五,结果就变成二十五加一——二十六。
When you call a subroutine, you pass it values — the arguments — into its parameters.
当你调用一个子程序时,你把一些值——也就是实参——传进它的参数里。
There are two ways.
有两种方式。
Pass by value gives the routine a copy: change it inside, and the caller's variable is untouched.
传值给子程序一份副本:在里面改动它,调用者的变量丝毫不受影响。
Pass by reference gives it a link to the caller's own variable: change it inside, and the caller changes too.
传引用给它一个通往调用者自己那个变量的链接:在里面改动它,调用者也跟着变。
Use by value for inputs you only read; use by reference when the routine must update the caller's data.
只读的输入用传值;当子程序必须更新调用者的数据时,就用传引用。
Swap is the classic case for pass by reference.
交换两个变量,是传引用最经典的用处。
Mark both parameters BYREF, so a and b are the caller's own variables and not copies.
把两个参数都标成 BYREF, 这样 a 和 b 就是调用者自己的变量,而不是副本。
Inside, declare a local temp to hold one value while you overwrite it: temp gets a, then a gets b, then b gets temp.
在里面声明一个局部的临时变量,在你覆盖一个值的时候先把它存住: 临时变量取到 a,然后 a 取到 b,然后 b 取到临时变量。
Watch it happen.
我们看着它发生。
a is three and b is eight; temp holds three; a becomes eight; and b becomes three.
a 是三,b 是八;临时变量存住三;a 变成八;b 变成三。
Swapped.
交换完成。
Written by value it would still swap the two copies — and the caller's variables would come back completely unchanged.
如果写成传值,它照样会把那两份副本交换一遍——而调用者的变量回来时一点都没变。
Finally, scope — where a name is visible.
最后是作用域——一个名字在哪里可见。
A local variable is declared inside a subroutine and exists only while it runs; nothing outside can see it.
局部变量声明在子程序内部,只在它运行时存在; 外面的任何东西都看不到它。
A global variable is declared outside, and is visible everywhere.
全局变量声明在外面,在任何地方都可见。
Prefer locals and parameters: heavy use of globals makes a program tangled and hard to test, because almost any part can change them.
优先用局部变量和参数:大量使用全局变量会让程序变得纠缠、难以测试, 因为几乎任何一部分都能改动它们。
So when should a block of code become a subroutine?
那么,一段代码什么时候该变成一个子程序呢?
Four good reasons.
有四个好理由。
When the same logic appears in more than one place — write it once, then call it many times.
当同样的逻辑出现在不止一个地方时——写一次,然后调用很多次。
When a block has a clear named purpose, because the name itself documents what the code does.
当一段代码有一个清楚的、能命名的用途时,因为名字本身就说明了这段代码在做什么。
When the program is complex, so you can break it into parts: that is decomposition.
当程序很复杂时,你可以把它拆成几部分:这就是分解。
And when you want to test a piece in isolation.
还有,当你想把一块代码隔离开来测试时。
One caution: do not make them so tiny that the call costs more than the work inside.
一句提醒:别把子程序拆得太碎,碎到调用它的开销比里面干的活还大。
Get the vocabulary exactly right, because the exam uses it precisely.
把这些术语记准,因为考试用词非常严格。
The definition is the whole PROCEDURE to ENDPROCEDURE block.
定义,指的是从 PROCEDURE 到 ENDPROCEDURE 的整个块。
The header is its first line, giving the name and the parameters.
头部,是它的第一行,给出名字和参数。
A parameter is the variable the subroutine declares to receive a value; an argument is the actual value the caller passes in — parameter inside, argument outside.
参数,是子程序声明出来接收值的那个变量;实参,是调用者真正传进去的那个值—— 参数在里面,实参在外面。
The call is the place the subroutine is invoked.
调用,是子程序被启用的那个地方。
A return value is what a function hands back.
返回值,是函数交回来的东西。
And the interface, or signature, is the name, the parameters and the return type: everything a caller must know to use it.
而接口,也叫签名,就是名字、参数和返回类型:一个调用者要用它所必须知道的全部信息。
Marks are given for efficient pseudocode, and loops are where it shows.
伪代码写得高效是有分的,而这一点最容易在循环上看出来。
First, move invariants out of the loop.
第一,把不变量搬到循环外面。
Here the same limit is recomputed on every single pass, even though nothing in it changes with the counter.
这里同一个上限每一次循环都重新算一遍, 可是它里面没有任何东西会随着计数器改变。
Compute it once, before the loop, and that work happens one time instead of a thousand.
在循环之前算一次,这份活儿就只做一次,而不是一千次。
Second, exit a loop early.
第二,尽早跳出循环。
A linear search that has already found its target should stop, not keep comparing the rest of the array.
线性查找一旦找到了目标就该停下来, 而不是继续把数组剩下的部分一个个比完。
And third, avoid redundant work: store a result and reuse it instead of recomputing.
第三,避免多余的工作:把结果存起来,重复使用,而不是重新算。
Four more habits that earn marks.
还有四个能拿分的好习惯。
Choose the right data structure: one array beats twenty separate variables when the items belong together.
选对数据结构:当一批数据本来就属于一起时, 一个数组胜过二十个各自独立的变量。
Replace deep nested IFs with a CASE when you are testing one value against many.
当你拿一个值去和很多选项比较时,用 CASE 取代层层嵌套的 IF。
Comment the intent, not the mechanics — validate the postcode tells a reader something, loop six times tells them nothing.
注释要写意图,而不是写机械动作——"检验邮政编码"告诉读者一些东西, "循环六次"什么都没告诉他。
And use meaningful names: numberOfPupils, not n.
还要用有意义的名字:写"学生人数",而不是一个字母 n。
Finally, always initialise a variable before you use it, so it never starts out holding whatever happened to be there.
最后,用一个变量之前一定要先初始化它,这样它就绝不会一开始就装着不知从哪来的东西。
If a question asks you to state three features that make pseudocode easier to understand, the three the scheme always takes are meaningful identifiers — Total, not t — indentation of the statements inside each construct, and comments that explain the purpose.
如果题目要你「说出三个让伪代码更容易理解的特征」, 评分标准一定接受的三个是:有意义的标识符——写 Total,不要写 t—— 每个构造内部语句的缩进,以及说明用途的注释。
Capitals for keywords, one statement per line and blank lines between sections are accepted too.
关键字大写、一行一条语句、段落之间空行,这些也被接受。
Comment the intent, not the mechanics: validate the postcode tells a reader something; loop six times does not.
注释要写意图,不要写机械动作:「验证邮编」能告诉读者一些东西,「循环六次」不能。
Half of Paper 2 is write pseudocode for module X, and the scheme awards a mark per feature.
Paper 2 有一半是「为模块 X 写伪代码」,评分标准是按特征逐个给分。
That matters more than it sounds: a module you did not finish still scores for every part you did get right, so write all six parts even when one of them is uncertain.
这一点比听上去重要:没写完的模块,只要写对的部分照样得分, 所以哪怕有一部分你不确定,六个部分也都要写出来。
The header first, exactly as the question describes it: FUNCTION, the name, the parameter with its type, and RETURNS with the return type.
先是头部,完全照题目描述的写:FUNCTION、名字、带类型的参数,以及 RETURNS 和返回类型。
Local declarations next: DECLARE every local variable with its type.
接着是局部声明:用 DECLARE 声明每个局部变量和它的类型。
Then initialise the counter or total to zero before the loop, because a total that was never set to zero is one of the commonest lost marks.
然后在循环之前把计数器或者总和初始化为零, 因为「总和从来没被置零」是最常见的失分之一。
The loop that visits every element: FOR Index from one to fifty for an array whose size is given.
再是遍历每个元素的循环:数组大小已给出时,用 FOR Index 从一到五十。
The condition, with the right comparison, the right boundary and the right item: IF Score of Index is greater than Limit.
然后是条件,比较符号要对、边界要对、比较的对象也要对: IF Score 的第 Index 项大于 Limit。
The update inside the branch.
分支里面是更新语句。
And the end: RETURN once, after the loop, in a function, then ENDFUNCTION, with every IF, FOR and WHILE closed.
最后是收尾:函数里 RETURN 只写一次,写在循环之后,然后 ENDFUNCTION, 并且每个 IF、FOR、WHILE 都要闭合。
The other worked example in the handout is the same six parts on a string: IsValid takes a Code and returns TRUE when it is two capital letters followed by four digits — the format A B one two three four.
讲义里另一个例题,是把这同样六个部分用在字符串上: IsValid 接收一个 Code,当它是两个大写字母后面跟四个数字时返回 TRUE—— 也就是 A B 一 二 三 四 这个格式。
Check the length first, then each character in turn, and return FALSE the moment one fails.
先检查长度,再逐个字符检查,只要有一个不符合就立刻返回 FALSE。
Three kinds of error, and the exam wants you to know what finds each one.
错误分三类,考试要你知道每一类是靠什么发现的。
A syntax error is a statement that breaks the rules of the language — a missing ENDIF, or OUTPT spelled wrong.
语法错误,是违反语言规则的语句——比如少了一个 ENDIF,或者把 OUTPUT 拼错。
The translator catches it before the program runs at all.
翻译器在程序运行之前就会抓住它。
A run-time error means the program runs, but a statement cannot be carried out: division by zero, an array index of zero or fifty-one, a loop that never ends so the program appears to freeze.
运行时错误,是程序跑起来了,但某条语句执行不了: 除以零、数组下标取到零或者五十一、循环永远不结束导致程序像卡死一样。
You only meet it while running.
它只有在运行的时候才会出现。
A logic error is the dangerous one, because the program runs to the end and the output is simply wrong: a greater-than where you needed greater-than-or-equal, or a total never set to zero.
逻辑错误是最危险的一种,因为程序完整跑完,输出却是错的: 该用大于等于的地方用了大于,或者总和从来没有置零。
Nothing reports it — you find it by testing, with a trace table and chosen test data.
没有任何东西会报告它——你只能靠测试发现,用跟踪表和挑好的测试数据。
An IDE helps with the last two: a breakpoint stops the program at a chosen line, single stepping then runs one statement at a time, and the watch window shows every variable's value at that moment, so the line where a value goes wrong is visible.
集成开发环境对后两类有帮助:断点让程序停在你选的那一行, 单步执行让它一次只走一条语句, 监视窗口显示那一刻每个变量的值,于是哪一行把值弄错了就直接看得见。
Finally, the mistakes that cost marks every year.
最后是每年都在丢分的那些错误。
Calling a function and letting the result be thrown away: a function hands a value back, so assign it or use it in the expression.
调用了函数却把返回值扔掉:函数是要交回一个值的,所以要么赋给变量,要么用在表达式里。
A length one out: six passed for a seven-element array, or the last index given where the length was wanted.
长度差一:七个元素的数组传了六,或者该给长度的地方给了最后一个下标。
Decide whether the parameter is a length or an index, then check the last element really is visited.
先想清楚这个参数是长度还是下标,然后检查最后一个元素确实被访问到了。
Closing a file inside the loop that reads it: open once, close once, after the loop.
在读文件的循环里面关文件:开一次,关一次,关在循环之后。
A construct left open: every IF needs its ENDIF, every FOR its NEXT, every WHILE its ENDWHILE, and the scheme has a mark for it.
构造没有闭合:每个 IF 要有 ENDIF,每个 FOR 要有 NEXT,每个 WHILE 要有 ENDWHILE, 而且评分标准里就有这一分。
A wrong boundary: at least means greater-than-or-equal, not greater-than, and a FOR that starts at zero for an array declared one to fifty is off from the first pass.
边界写错:「至少」是大于等于,不是大于; 数组声明成一到五十,FOR 却从零开始,那从第一遍就错了。
And half a condition: IF x equals three OR four is not a condition — each side of OR and AND must be a complete comparison.
还有半个条件:IF x 等于三 或者 四,这不是条件—— OR 和 AND 的每一边都必须是一个完整的比较。
One more: plus does not join strings in this pseudocode; ampersand does.
再补一条:在这套伪代码里,加号不能连接字符串,和号才可以。
Three marks to lock in.
三个要拿稳的分。
First, a procedure returns nothing; a function returns a value — and know pass by value versus by reference.
第一,过程不返回值,函数返回一个值——并且要懂传值与传引用的区别。
Second, choose the loop: a for loop when the count is known, a while or repeat otherwise.
第二,选对循环:次数已知用 for 循环,否则用 while 或 repeat。
Third, distinguish local from global scope, and prefer locals in reusable code.
第三,把局部作用域和全局作用域分清楚,在可复用的代码里优先用局部变量。
Nail these, and this topic is yours.
掌握这些,这个专题就是你的了。
And the definitions, which are marked against fixed wording.
还有那些定义,它们是按固定措辞给分的。
A procedure is a subroutine that carries out a task and does not return a value; it is called with CALL.
过程,是执行一项任务、不返回值的子程序;用 CALL 调用它。
A function is a subroutine that returns a single value to the point where it was called, so it can be used in an expression.
函数,是把一个值返回到调用处的子程序,所以它可以用在表达式里。
A parameter is the identifier in the header that receives a value or a reference; an argument is the value supplied in the call.
参数,是头部中接收值或者引用的那个标识符;实参,是调用时提供的那个值。
Passing by value gives the subroutine a copy, so changes inside do not affect the original; passing by reference gives the address, so they do.
传值,是把一份副本交给子程序,所以里面的改动不影响原变量; 传引用,是把地址交出去,所以里面的改动会影响原变量。
The header is the first line of the definition; the interface is what a caller must know — name, parameters and return type.
头部,是定义的第一行; 接口,是调用者必须知道的东西——名字、参数和返回类型。
A local variable exists only while the subroutine runs and only inside it; a global one can be used anywhere.
局部变量只在子程序运行期间存在,而且只能在它里面使用;全局变量在任何地方都能用。
And the three loops: count- controlled repeats a fixed number of times, pre-condition tests before each iteration so the body may never run, post-condition tests after so the body runs at least once.
还有三种循环:计数控制循环重复固定次数, 前置条件循环在每次迭代之前判断,所以循环体可能一次都不执行, 后置条件循环在之后判断,所以循环体至少执行一次。