阅读 12

第九章 Caché 命令大全 FOR 命令

第九章 Caché 命令大全 FOR 命令

重复执行代码块,在每个循环开始时进行测试。

重点

  1. 无参数FOR无限循环,块内做退出。
  2. goto , try-catch 都会退出FOR循环。
  3. FOR循环可以以枚举的方式循环,逗号分割
  4. 不同区间,多次循环写法。

大纲

FOR var=forparameter { code }
F var=forparameter { code }

FOR var=forparameter1,forparameter2,... { code }
F var=forparameter1,forparameter2,... { code }
复制代码

其中,forParameter可以是:

expr
start:increment
start:increment:end
复制代码

参数

  • var 可选-由FOR命令初始化的局部变量或实例变量。通常,这是一个数字计数器,每次执行代码块时都会递增。
  • expr 可选-执行代码块之前分配给var的值。可以是单个值或逗号分隔的值列表。
  • start 可选-第一次执行代码块之前分配给var的数值。与增量和(可选)End一起使用,以控制for循环的多次迭代。
  • increment 可选-用于在for循环的每次迭代后递增(或递减)var的数值。
  • end 可选-用于终止for循环的数值。当var递增到等于或大于end的值时,循环结束。
  • code 用大括号括起来的CachéObjectScript命令块。

描述

FOR是面向块的命令。通常,它由一个计数器和一个用大括号括起来的可执行代码块组成。此代码块的执行次数由计数器确定,计数器在每个循环的顶部进行测试。较少见的是,for命令不指定递增计数器。它可以是无参数的(无限循环直到退出),也可以指定一个表达式作为其参数(循环一次)。

for命令有两种基本形式:

FOR无参数

不带参数的for无限执行循环代码块,直到由代码块内的命令退出。Caché重复花括号内的命令,直到遇到退出循环的QuitReturnGOTO命令。以下示例在x=3时退出循环:

/// d ##class(PHA.TEST.Command).TestFor()
ClassMethod TestFor()
{
	SET x=8
	FOR { 
		WRITE "Running loop x=",x,!
		SET x=x-1
		QUIT:x=3
	}
	WRITE "Next command after FOR code block"
}
复制代码
DHC-APP>d ##class(PHA.TEST.Command).TestFor()
Running loop x=8
Running loop x=7
Running loop x=6
Running loop x=5
Running loop x=4
Next command after FOR code block
复制代码

当然,错误也会中断for循环,如下面的示例所示。此for循环由CATCH块捕获的被零除错误退出:

/// d ##class(PHA.TEST.Command).TestForCatch()
ClassMethod TestForCatch()
{
	TRY {
		SET x=8
		FOR { 
			SET y=4/x
			WRITE "Running loop 4/",x,"=",y,!
			SET x=x-1
		}
		WRITE "FOR代码块之后的下一个命令"
	}
	CATCH exp {
		WRITE !,"异常处理模块",!
		IF 1=exp.%IsA("%Exception.SystemException") {
			WRITE "Name: ",$ZCVT(exp.Name,"O","HTML"),!
			WRITE "Location: ",exp.Location,!
			WRITE "Code: ",exp.Code
		}
		ELSE {WRITE "意外的异常类型",! }
		RETURN
	}
}
复制代码
DHC-APP> d ##class(PHA.TEST.Command).TestForCatch()
Running loop 4/8=.5
Running loop 4/7=.5714285714285714286
Running loop 4/6=.6666666666666666667
Running loop 4/5=.8
Running loop 4/4=1
Running loop 4/3=1.333333333333333333
Running loop 4/2=2
Running loop 4/1=4
 
异常处理模块
Name: <DIVIDE>
Location: zTestForCatch+4^PHA.TEST.Command.1
Code: 18
复制代码

FOR有参数

FOR的操作取决于使用的参数形式:

FOR var=expr 执行代码块一次,将var设置为expr的值。对于var=expr1,expr2...,exprN 执行代码块N次,将每个循环的var设置为expr的每个连续值。

FOR var=start:increment 无限执行代码块,除非退出。在第一次迭代中,Caché将var设置为start的值。每次执行FOR命令都会将变量值递增指定的增量值。Caché重复执行,直到在代码块中遇到QUITRETURNGOTO命令。

FOR VAR=START:INCREMENT:ENDvar设置为START的值。然后,Caché根据下表中描述的条件执行代码块:

  • 增量为正 如果Start>End,则不要执行代码块。当var等于或大于end时,或者如果Caché遇到QUITRETURNGOTO命令,则停止循环。
  • 增量为负 如果start<end,则不执行代码块。当var等于或小于end时,或者如果Caché遇到QUITRETURNGOTO命令,则停止循环。

Caché在开始执行循环时计算开始值、增量值和结束值。在循环内对这些值所做的任何更改都将被忽略,并且不会影响循环执行的次数。

当循环终止时,var包含一个反映上一次执行循环产生的增量的值。但是,var的增量永远不会超过end中指定的最大值。

for循环可以包含多个逗号分隔的for参数参数,但只能包含一个var参数。有效语法如下:

FOR var=start1:increment1:end1,start2:increment2:end2
复制代码

参数

var

var参数可以是简单的局部变量、带下标的局部变量或实例变量(如i%property)。for命令初始化(或设置)此变量;var不需要在for命令之前定义。

  • 使用循环计数器语法时,var是保存for循环的当前计数器值的局部变量。它最初包含由Start指定的数值。然后使用每次重复for循环的增量重新计算它。

  • 使用var=expr语法时,本地变量初始化为expr值。

expr

在执行LOOP命令之前,Caché分配给var的值。expr的值可以指定为文字或任何有效表达式。expr可以是单个值,也可以是逗号分隔的值列表。如果是单个值,则Caché执行一次for循环,将该变量值提供给代码块。如果是逗号分隔的值列表,则Caché执行for循环的次数与值的次数相同,每个循环都将var等同于该值,并将该var值提供给代码块

start

数值Caché在for循环的第一次迭代时分配给varstart的值可以指定为文字或任何有效表达式;Caché计算其数值的start非数字起始值的计算结果为0。

increment

for循环的每次迭代之后,Caché用来递增var的数值。如果指定START,则它是必需的。增量的值可以指定为文字或任何有效表达式;Caché计算其数值的增量。增量可以是整数,也可以是分数;可以是正数(递增),也可以是负数(递减)。

值为0或非数字增量值会导致for循环无限重复,除非退出。即使End=0,也是如此。但是,如果START大于END,则不执行循环,并且忽略增量0。

end

Caché用于终止for循环的数值。当var等于(或超过)该值时,最后一次执行for循环,然后终止。end的值可以指定为文字或任何有效表达式;Caché为其数值计算end

如果start=end,则for循环执行一次。如果start大于end(并且增量为正数),则不会执行for循环。

code

用大括号括起来的一个或多个CachéObjectScript命令块。此可执行代码块可以根据需要包含多个命令、标签、注释、换行符、缩进和空格。当for命令结束时,在右花括号之后继续执行下一个命令。

左大括号或右大括号可以出现在其自己的代码行上,也可以与命令显示在同一行上。左大括号或右大括号甚至可能出现在第1列中(但不建议这样做)。建议的编程实践是缩进大括号以指示嵌套代码块的开始和结束。左花括号前后不需要空格。右大括号之前不需要空格,包括无参数命令后的大括号。大括号只有一个空格要求:右大括号必须用空格、制表符或行回车符与后面的命令分隔。

退出For循环

可以通过发出QUITRETURNCONTINUEGOTO命令退出for循环:

  • QUIT退出当前的块结构。因此,在for块中退出会导致Caché在for块之后的下一行开始执行。QUIT只退出当前的FOR块;如果FOR块嵌套在另一个FOR块(或任何其他块结构)中,则发出QUIT命令会将内部FOR块退出到外部块结构。

FOR块(和其他一些块结构)中的退出行为与不在块结构中时的退出行为不同。退出其中一个块结构会退出当前例程,而不仅仅是当前代码块。

仅当退出出现在FOR块中时,退出才会退出FOR块。如果for循环调用子例程,则在该子例程中发出QUIT将终止该子例程,而不是调用它的for循环。

  • 无论当前例程是否从for块结构中发出,Return都会退出该例程。
  • CONTINUE 退出当前的for循环。它会导致执行立即跳回for命令。然后,for命令递增并评估其参数,并基于该评估确定是否重新执行代码块循环。因此,CONTINUE命令对执行的影响与到达代码块的右大括号完全相同。
  • GOTO可以通过将控制转移到FOR代码块之外来退出当前FOR块结构。For循环不会由在For代码块内转移控制的GOTO终止。

示例

无参数FOR

在下面的示例(演示的无参数)中,反复提示用户输入一个数字,然后通过do命令将该数字传递给Calc子例程。当用户输入空字符串(按Enter键而不输入数字)时,for循环终止,这会导致执行QUIT命令。


/// d ##class(PHA.TEST.Command).TestForArgumentless()
ClassMethod TestForArgumentless()
{
Mainloop
	FOR {
		READ !,"数字: ",num 
		QUIT:num="" 
		DO Calc(num) 
	}
Calc(a)
	WRITE !,"平方的数字是 ",a*a
	QUIT
}
复制代码
DHC-APP>d ##class(PHA.TEST.Command).TestForArgumentless()
 
数字: 9
平方的数字是 81
数字: 5
平方的数字是 25
数字:
复制代码

使用 FOR var=expr

当指定var=expr时,Caché执行for循环的次数与expr中逗号分隔值的次数一样多。expr中的值可以是文字或任何有效表达式。如果指定表达式,则它的计算结果必须为单个值。

在下面的示例中,for命令执行一次代码块,其中num的值为4。它将写入数字12:

/// d ##class(PHA.TEST.Command).TestForLoop()
ClassMethod TestForLoop()
{
Loop
	SET val=4
	FOR num=val {
		WRITE num*3,! 
	}
	WRITE "FOR代码块之后的下一个命令"
}
复制代码
DHC-APP>d ##class(PHA.TEST.Command).TestForLoop()
12
FOR代码块之后的下一个命令
复制代码

在下面的示例中,for命令执行一次代码块,其中alpha(7)的值为“ABCDEFG”

/// d ##class(PHA.TEST.Command).TestForLoop1()
ClassMethod TestForLoop1()
{
Loop
	SET val="abc"
	FOR alpha(7)=val_"defg" {
		WRITE alpha(7),!
	}
	WRITE "FOR代码块之后的下一个命令"
}
复制代码
DHC-APP>d ##class(PHA.TEST.Command).TestForLoop1()
abcdefg
FOR代码块之后的下一个命令
复制代码

在下面的示例中,for命令执行代码块八次,将每个连续的完全数提供给代码块:

/// d ##class(PHA.TEST.Command).TestForNum()
ClassMethod TestForNum()
{
	FOR pnum=6, 28, 496, 8128, 33550336, 8589869056, 137438691328, 2305843008139952128 {
		WRITE "Perfect number ", pnum
		SET rp = $REVERSE(pnum)
		IF 54 = $ASCII(rp, 1) {
			WRITE " 6结尾",! 
		}
		ELSEIF 56 = $ASCII(rp, 1),50 = $ASCII(rp, 2) {
			WRITE " 28结尾",! 
		}
		ELSE {
			WRITE " 未知数",! 
		}
	}
}
复制代码
DHC-APP>d ##class(PHA.TEST.Command).TestForNum()
Perfect number 6 6结尾
Perfect number 28 28结尾
Perfect number 496 6结尾
Perfect number 8128 28结尾
Perfect number 33550336 6结尾
Perfect number 8589869056 6结尾
Perfect number 137438691328 28结尾
Perfect number 2305843008139952128 28结尾
复制代码

使用 FOR var=start:increment:end

参数START、INCREMENT和END分别指定起始值、增量值和结束值。这三个都是用数字评估的。它们可以是整数或实数、正数或负数。如果提供字符串值,它们将在循环开始时转换为它们的数值等效值。

当Caché第一次进入循环时,它会将起始值分配给var,并将var值与结束值进行比较。如果变量值小于结束值(如果是负增量值,则大于该值),则Caché执行循环命令。然后,它使用增量值更新var值。(如果使用负增量,则var值会递减。)

循环继续执行,直到var值的增量超过结束值(或者直到Caché遇到QUITRETURNGOTO)。在这一点上,为了防止var超过end,Caché取消变量赋值并结束循环执行。如果增量导致var值等于结束值,则Caché最后一次执行for循环,然后终止该循环。

以下代码重复执行WRITE命令,以按顺序输出字符串1中的所有字符,最后一个字符除外。因为结束值被指定为len-1,所以不输出最后一个字符。这是因为测试是在循环的顶部执行的,当变量值(Index)超过(而不仅仅是匹配)终结值(len-1)时,循环就会终止。

/// d ##class(PHA.TEST.Command).TestForString()
ClassMethod TestForString()
{
	SET string1 = "123 Primrose Path"
	SET len = $LENGTH(string1)
	FOR index = 1 : 1 : len - 1 {
		WRITE $EXTRACT(string1,index),!
	}
}

复制代码
DHC-APP>d ##class(PHA.TEST.Command).TestForString()
1
2
3
 
P
r
i
m
r
o
s
e
 
P
a
t
 
复制代码

使用 FOR var=start:increment

在这种形式的for命令中,没有结束值;循环必须包含QUITRETURNGOTO命令才能终止循环。

起始值和增量值以数字计算。它们可以是整数或实数、正数或负数。如果提供了字符串值,它们将在循环开始时转换为它们的数值等效值。Caché在开始执行循环时计算起始值和增量值。在循环内对这些值所做的任何更改都将被忽略。

当Caché第一次进入循环时,它会将起始值分配给var并执行循环命令。然后,它使用增量值更新var值。(如果使用负增量,则var值会递减。)。循环继续执行,直到Caché在循环中遇到QUITRETURNGOTO

下面的示例使用START:INCREMENT语法返回长度小于三位数的所有7的倍数:

/// d ##class(PHA.TEST.Command).TestForSeven()
ClassMethod TestForSeven()
{
	FOR i(1)=0:7 {
		QUIT:$LENGTH(i(1))=3
		WRITE "multiple of 7 = ",i(1),! 
	}
}
复制代码
DHC-APP>d ##class(PHA.TEST.Command).TestForSeven()
multiple of 7 = 0
multiple of 7 = 7
multiple of 7 = 14
multiple of 7 = 21
multiple of 7 = 28
multiple of 7 = 35
multiple of 7 = 42
multiple of 7 = 49
multiple of 7 = 56
multiple of 7 = 63
multiple of 7 = 70
multiple of 7 = 77
multiple of 7 = 84
multiple of 7 = 91
multiple of 7 = 98
 
DHC-APP>
复制代码

下面的示例使用START:INCREMENT语法计算用户提供的一系列数字的平均值。包含后置条件退出是为了在用户输入空字符串(即,在没有输入值的情况下按Enter键)时终止循环的执行。当后置条件表达式(num=“”)测试为真时,Caché将执行Quit并终止循环。

循环计数器(i变量)用于跟踪输入了多少个数字。i被初始化为0,因为计数器递增发生在用户输入数字之后。当用户输入NULL时,Caché终止循环。循环结束后,set命令引用i(作为局部变量)来计算平均值。

/// d ##class(PHA.TEST.Command).TestForAverage()
ClassMethod TestForAverage()
{
	SET sum=0
	FOR i=0:1 {
		READ !,"Number: ",num 
		QUIT:num="" 
		SET sum=sum+num 
	}
	SET average=sum/i
	w !,average,!
}

复制代码
DHC-APP>d ##class(PHA.TEST.Command).TestForAverage()
 
Number: 9
Number: 6
Number: 3
Number: 2
Number:
5
复制代码

使用 FOR 多值参数

一个for命令只能包含一个var=参数,但可以包含多个以逗号分隔的列表指定的forParameter参数。例如,语法var=expr1,expr2,expr3将导致代码块执行三次,每次执行的变量值不同。

/// d ##class(PHA.TEST.Command).TestForMulti()
ClassMethod TestForMulti()
{
Mainloop
	SET y="beta"
	FOR x=y,1:1:10 {
		DO Test
	}
	QUIT
Test
	WRITE !,"Running test number ",x
	QUIT
}
复制代码
DHC-APP>d ##class(PHA.TEST.Command).TestForMulti()
 
Running test number beta
Running test number 1
Running test number 2
Running test number 3
Running test number 4
Running test number 5
Running test number 6
Running test number 7
Running test number 8
Running test number 9
Running test number 10
复制代码

下面的示例是一个采样程序,它包含三个带有start:increment:end语法的forParameter参数。它将i设置为1,然后将1到10的个位数递增1;第二个for参数获取I值10,并将其递增10到100;第三个for参数获取i值100,并将其递增100到1000。请注意,此示例重复10和100值:

/// d ##class(PHA.TEST.Command).TestForMultii()
ClassMethod TestForMultii()
{
	FOR i=1:1:10,i:10:100,i:100:1000 {WRITE i,!}
}
复制代码
DHC-APP>d ##class(PHA.TEST.Command).TestForMultii()
1
2
3
4
5
6
7
8
9
10
10
20
30
40
50
60
70
80
90
100
100
200
300
400
500
600
700
800
900
1000
复制代码

下面的示例执行与上一个示例相同的操作,而不重复10和100值:

/// d ##class(PHA.TEST.Command).TestForMultiDiff()
ClassMethod TestForMultiDiff()
{
	FOR i=1:1:9,i+1:10:99,i+10:100:1000 {WRITE i,!}
}

复制代码
DHC-APP>d ##class(PHA.TEST.Command).TestForMultiDiff()
1
2
3
4
5
6
7
8
9
10
20
30
40
50
60
70
80
90
100
200
300
400
500
600
700
800
900
1000
 
复制代码

递增无参数FOR

无参数的FORFOR VAR=START:INCREMENT表单的操作相同。唯一的区别是它不提供跟踪循环执行次数的方法。

下面的示例显示如何使用无参数FOR重写前面的循环计数器示例。赋值i=i+1替换循环计数器。

/// d ##class(PHA.TEST.Command).TestForArgumentlessFOR()
ClassMethod TestForArgumentlessFOR()
{
	SET sum=0
	SET i=0
	FOR {
		READ !,"Number: ",num QUIT:num="" 
		SET sum=sum+num,i=i+1
	}
	SET average=sum/i
	WRITE !!,"Average is: ",average
	QUIT
}
复制代码
DHC-APP>d ##class(PHA.TEST.Command).TestForArgumentlessFOR()
 
Number: 5
Number: 4
Number: 3
Number:
 
Average is: 4
复制代码

注意

FORNEW

NEW命令会影响var。在for循环体中发出无参数的new命令或独占的new命令(不专门排除var)可能会导致在新的帧上下文中未定义var

不包含varnew命令对for循环执行没有影响,如下例所示:

abc()
	SET a=1,b=1,c=8
	FOR i=a:b:c {
		 WRITE !,"count is ",i
		 NEW a,c
		 WRITE " loop"
		 NEW (i)
		 WRITE " again"
	}
复制代码

在如上程序在NEW a,cw ac 会直接提示未定义。

DHC-APP>d ^PHA.TEST.Command
 
count is 1 loop again
count is 2 loop again
count is 3 loop again
count is 4 loop again
count is 5 loop again
count is 6 loop again
count is 7 loop again
count is 8 loop again
复制代码

FOR 和 Watchpoints

对于for,对观察点的使用是有限的。如果为FOR命令的控制(INDEX)变量建立观察点,则Caché仅在对每个FOR命令参数进行初始评估时才触发特定的观察点操作。此限制是出于性能考虑。

下面的示例包含受监视变量x的三种for命令参数:范围,具有初始值、增量和限制(终值);单个值;以及具有初始值、增量和无限制的范围。

x具有初始值1、20和50时会发生中断。

DHC-APP>ZBREAK *x
DHC-APP>FOR x=1:1:10,20,50:2 {SET t=x QUIT:x>69}
 
FOR x=1:1:10,20,50:2 {SET t=x QUIT:x>69}
^
<BREAK>
DHC-APP 2f0>w
 
a=1
b=1
c=8
i=8
t=70
x=1
DHC-APP 2f0>g
 
FOR x=1:1:10,20,50:2 {SET t=x QUIT:x>69}
^
<BREAK>
DHC-APP 2f0>w
 
a=1
b=1
c=8
i=8
t=10
x=20
DHC-APP 2f0>g
 
FOR x=1:1:10,20,50:2 {SET t=x QUIT:x>69}
^
<BREAK>
DHC-APP 2f0>w
 
a=1
b=1
c=8
i=8
t=20
x=50
DHC-APP 2f0>g
 
DHC-APP>
复制代码