[例9-10] 写出下面程序的运行结果。
'EXAMPLE 10
a=100
b=50
FOR i=1 TO 2
p=i MOD2+1
ON p GOSUB add,subtract
NEXT i
END
add:PRINT a+b;
RETURN
subtract:PRINTa-b;
RETURN
程序的运行结果为:
150 50
本题中的主程序是一个FOR循环,共循环两次,子程序1和子程序2的标号分别为add和subtract,给定的条件是p=i MOD 2+1,在循环执行过程中,P依次被赋值为1和2,在执行ON GOSUB语句时,分别执行add t和subtract这两个子程序,先后打印输出150和50。
当用户需要在程序运行的过程中,通过键盘来干预程序的执行,让计算机完成一个特定的操作时,可以使用ON KEY(n)GOSUB〈子程序入口〉
功能:在程序中指定一个键,设置事件陷阱。
说明:
(1)格式的n是一个数值表达式,代表一个键。n的值设定的键的对应关系见表9-1所示。
表9-1 KDY(n)中n与键的对应关系
|
n的值 |
键 名 |
|
1~10 |
功能键F1~10 |
|
11 |
方向键↑ |
|
12 |
方向键← |
|
13 |
方向键→ |
|
14 |
方向键↓ |
(2)ON KEY(n)GOSUB语句的作用是:在程序中指定一个键,如果在程序执行过程中用户按下该键,程序就会中断原来的操作过程,转而执行事先设置的一个子程序,执行完这个子程序后再返回原来的主程序继续执行。这种功能称为“事件捕捉”,按一个特定的键就是一个“事件”。“捕捉”到此“事件”就转子程序处理。“事件捕捉”功能又称“陷阱”。这样的子程序称“事件捕捉子程序”或“陷阱子程序”。
[例9-11] 编写一个程序,在程序开始运行后,只要按一次F5键,屏幕上就显示当时的时间。
程序如下:
'EXAMPLE 11
ON KEY(n)GOSUB t
KEY(5) ON
DO
LOOP UNTIL INKEY$<>""
END
KEY(3)OFF
t:PRINT"时间:";TIME$
RETURN
程序中第一行设n值为5,也就是指定键盘上的功能键F5为“陷阱键”。在程序运行过程中任何时候按下F5键,就转去执行陷阱子程序。该子程序输出当前时间,每按一次F5键,显示一次。程序第4行的作用的等待键盘上的一个操作,如键盘上无任何输入,循环不断运行,如要程序终止,则可按下F5键以外的任何键。
程序运行结果如下(按3次F5键):
时间:20:20:56
时间:20:21:03
时间:20:21:12
按回车键后程序结束。
在Quick BASIC中用SUB语句定义子程序过程。
格式:
SUB〈过程名〉[(〈参数表〉)][STATIC]
[〈语句块〉]
END SUB
功能:定义一个SUB子过程。
说明:
(1)〈过程名〉即子程序过程的名字,是一个不超过40个字符的字符串,不能有数据类型后缀。一个程序只能有一个惟一的过程名。
(2)〈参数表〉是主程序调用本过程时传送给本过程的一个或多个简单变量名、数组名,各变量名之间用逗号分隔。〈参数表〉指明了传送给过程的变量个数和类型,其格式为:
〈变量1〉[()][AS〈类型〉][,〈变量2〉[()][AS〈类型〉]]…
这里的〈变量〉是一个Quick BASIC变量名,如果是数组变量,要在数组名后面加上一对小括号。
〈类型〉是对应〈变量〉的数据类型,可以是INTEGER、LONG、SINGLE、DOUBLE、STRING或用户定义数据类型。
〈参数表〉中的参数是形式参数,不能用定长字符串变量或定长字符串数组作为形式参数。不过可以在CALL语句中用简单定长字符串变量作为形式参数,在调用SUB过程之前,Quick BASIC把它转换为变长字符串变量。
(3)STATIC定义过程中局部变量在内存中的存储方式。如果使用STATIC属性,则过程中的局部变量是STATIC(静态)的,即在每次调用过程时,各局部变量的值保持不变;如果省略STATIC,则局部变量主默认为自动的,即在每次调用过程时,局部变量初始化为0或空字符串。
(4)SUB过程以SUB开始,以END SUB结束。当程序遇到END SUB时,将退出过程并立即返回到调用语句的下面语句。
(5)〈语句块〉是Quick BASIC的程序段,语句块中可以用一个或多个EXIT SIB语句从过程中退出。
(6)在过程定义中不能使用SUB…END SUB、FUNCTION…END FUNCTION 、DEF FN…END DEF、COMMON、DECLARE、DIM SHARED、OPTION BASE和TYPE…END TYPE语句。
在SUB过程中不能定义SUB过程、FUNCTION过程、DEF FN函数。即在过程内不能嵌套过程定义或DEF FN函数,但是一个过程可以调用另一个过程或DEF FN函数。
要执行一个过程,必须调用该过程,SUB过程调用可以作为独立的基本语句。调用SUB过程的方式有两种:一种是把过程名放在一个CALL语句中;另一种是把过程名本身作为一个语句来使用。
格式:
[CALL]〈过程名〉[(〈变元表〉)]
功能:把控制传送到SUB过程。
说明:
(1)〈过程名〉是需调用的SUB过程名。
(2)〈变元表〉是传送给SUB过程的变量或常量,各变元之间用逗号分隔。用数组名后跟空括号指定数组参数。
(3)如果省略CALL关键字,也要同时去掉包括变元表的小括号,这时要在调用前用DECLARE语句说明。如果在Quick BASIC环境中保存它,Quick BASIC将自动产生DECLARE语句。
下面是两种调用方式的区别:
DECLARE SUB proce(first%,second$) '说明过程语句
…
CALL proce(first%,second$) '用CALL调用时的语句
…
proce first%,second$ '省略CALL时的调用语句
[例9-12] 用SUB子程序计算4!+5!+6!的值。
问题分析:要计算s=4!+5!+6!,先要分别计算出4!,5!和6!。由于3个求阶乘的运算过程完全相同,因此可以用子程序来计算任意阶乘m!,每次调用子程序前给m一个值,在子程序中其所求结果放入total变量中,返回主程序后tot变量接收total的值。这样3次调用子程序便可求得s。
主程序为:
'EXAMPLE 12
DECLARE SUB fact(m!,total!)
CLS
a=4:b=5:c=6
CALL fact(a,tot)
s=tot
CALL fact(b,tot)
s=s+tot
PRINT a;"!+";b;"!+";c;"!=";s
END
子程序为:
SUB fact(m,total) '计算阶乘子程序
total=1
FOR i=1 TO m
total=total*i
NEXT i
END SUB
程序运行结果为:
4!+5!+6!=864
在Quick BASIC中,定义的过程要在主程序中用说明过程语句DECLARE说明。
格式:
DECLARE{FUNCTION丨SUB}〈过程名〉[([〈参数表〉])]
功能:说明被调用的一个FUNCTIONA或SUB过程名,并且检查各参数的数据类型。
说明:
(1)〈过程名〉是程序中的FUNCTION或SUB过程名。
(2)〈参数表〉是主程序调用过程时给传送给过程的一个或多个变量,其格式为:
〈变量1〉[()][AS〈类型〉][,〈变量2〉[()][AS〈类型〉]]…
〈变量〉是一个Quick BASIC变量名。
〈类型〉是对应〈变量〉的数据类型,可以是INTEGER、LONG、SINGLE、DOUBLE、STRING或用户定义数据类型。即任何允许的数据类型。
(3)DECLARE语句中的参数表主要是对传送过程的变元进行类型检查。
(4)DECLARE语句必须和在主程序中第一个可执行语句之前。作为约定DECLARE语句出现在程序注释语句之后,常数及变量说明符之前。
(5)在主程序中,如果省略DECLARE语句,在Quick BASIC编辑环境中,当把程序存盘时,将自动在主程序的开始入加入属于本过程的DECLARE语句。
由于DECLARE语句会在Quick BASIC的编辑环境中自动产生,所以用户不用专门输入。
但是,维护参数表的工作仍要由用户自己完成,如果修改子程序中的参数表,也必须修改DECLARE语句中的参数表,使之相互匹配。
调用过程时可以把数传递给过程,也可以把过程中的数据传递回来。在调用过程中,要考虑调用程序和被调用程序之间的数据是如何传递的。通常在编制一个子程序时,要考虑它需要输入哪些量,进行处理后输出哪些量。正确地提供一个子程序的输入数据和正确地引用其输出数据,是使用子程序的关键问题,也就是调用程序和被调用程序之间的数据传递。
在调用一个过程时必须完成形式参数与实际参数的结合,即把实际参数传送给形式参数,然后实际参数执行调用的过程。在Quick BASIC中,通常把形式参数叫做参数,把实际参数叫做变元。
变元(实际参数)是在调用SUB或FUNCTION过程时传送给SUB或FUNCTION过程的常量、变量或表达式。参数(形式参数)是出现在SUB、FUNCTION或DECLARE语句中的变量名是接收传送给子程序值的变量。
参数与变元的对应关系为:
实际参数
调用过程:CALL testsub( a%,b!,"Test", 16.8)
定义过程:SUB testsub( r%,s!, t$, x)STATIC
形式参数
在定义过程中,形式参数为实际参数保留位置,当调用过程时,实际参数被插入形式参数中各变量处,第一个形式参数接收第一个实际参数的值,第二个形式参数接收第二个实际参数的值,…。
实参表和形参表中对应的变量名不必相同,但是变量的个数必须相等,并且对应变量的类型必须相同。
形参表中的各个变量之间用逗号分隔,表中的变量是:
(1)后面跟有左、右圆括号的数组名:若括号内有数字,一般表示数组的维数。
(2)除定长字符串之外的合法变量名。即在形参表中只能用如x$或x AS STRING之类的变长字符串作为形式参数,不能用如x AS STRING*10之类的定长字符串作为形式参数。但定长字符串可以作为实际参数传递给过程。
实参表可由常量、表达式、有效的变量名、数组名(后跟左、右括号)组成,变元表中各变元用逗号分隔。例如,下面是具有参数表的一个子程序定义的语句行:
SUBtestsub(i%,aa(),re AS rectype,cc$)
其中:i%是整型参数、aa是一个单精度数组、re是rectype类型的记录、cc$是一个字符串。
用下面的程序调用过程testsub,并把4个变元传送给相应的参数。
TYPE rectype '定义用户类型
xm AS STRING*12
dz AS LONG
END TYPE
DIM re AS rectype '定义一个rectypt类型的变量re
CALL testsub(i%,cj(),re,"Kaifeng")
传递参数的方式有两种。如果CALL语句中的变元(实际参数)为变量,就是传址调用;如果CALL语句中的变元(实际参数)是常量或表达式,就是传值调用。
传址调用或称按地址传递,是指形式参数与实际参数使用相同的内存地址单元。所以,如果形式参数发生变化,实际参数也随着改变。在传址调用时,实际参数必须是变量,绝不能是常量或表达式。
传址调用会把过程的执行结果带回调用的程序,因此,在返回程序时实际参数的内容可能已经发生了变化。
[例9-13] 阅读下面的程序,分析输出结果,理解传址调用。
主程序为:
'EXAMPLE 13
DECLARE SUB prod(x!,y!,z!)
CLS
a=5:b=3
PRINT "主程序调用前的变量值a,b,c",a,b,c
END
子程序为:
SUB prod(x!,y!,z!)
PRINT"子程序运算前的变量值x,y,z",x,y,z
z=x*y
PRINT"子程序中运算后的变量值x,y,z",x,y,z
END SUB
程序的运行结果为
主程序调用前的变量值a,b,c 5 3 0
子程序中运算前的变量值x,y,z 5 3 0
子程序运算后的变量值x,y,z 5 3 15
主程序调用后的变量值a,b,c 5 3 15
主程序CALL语句中的变量a、b、c与子程序中相对应的变量x、y、z使用相同的内存