第七章 疯狂Caché 命令(二)
流程控制命令
为了建立任何代码的逻辑,必须有流控制;有条件地执行或绕过代码块,或重复执行代码块。为此,ObjectScript支持以下命令:
IF
,ELSEIF
,ELSE
FOR
WHILE
,DO WHILE
有条件执行
要基于布尔(TRUE/FALSE)测试有条件地执行代码块,可以使用if命令。(可以使用后置条件表达式执行单个CachéObjectScript命令的条件执行。)
IF
将表达式作为参数,并计算该表达式为TRUE或FALSE。如果为true,则执行表达式后面的代码块;如果为false,则不执行该代码块。最常见的情况是用推荐值1和0表示这些值。但是,Caché对任意值执行条件执行,如果其计算结果为0(零),则将其计算为false,如果计算结果为非零值,则计算为True。
可以将多个IF
布尔测试表达式指定为逗号分隔的列表。这些测试按照从左到右的顺序作为一系列逻辑与测试进行评估。因此,当IF的所有测试表达式都计算为TRUE时,IF
的计算结果为TRUE。当IF的其中一个测试表达式的计算结果为False时,IF
的计算结果为False;不计算其余的测试表达式。
代码通常出现在包含多个命令的代码块中。代码块只是花括号中包含的一行或多行代码;代码块之前和代码块内可以有换行符。包含多个命令的块。
IF
, ELSEIF
, ELSE
IF
结构允许计算多个条件,并指定根据这些条件运行哪些代码。与简单命令相反,构造由一个或多个命令关键字、它们的条件表达式和代码块组成。IF
结构由以下部分组成:
- 一个带有一个或多个条件表达式的
if
子句。 - 任意数量的
ELSEIF
子句,每个子句都有一个或多个条件表达式。ELSEIF
子句是可选的;可以有多个ELSEIF子
句。 - 最多只有一个
ELSE
子句,没有条件表达式。ELSE
子句是可选的。
以下是IF
构造的示例:
/// d ##class(PHA.TEST.ObjectScript).TestIf()
ClassMethod TestIf()
{
READ "Enter the number of equal-length sides in the polygon: ",x
IF x=1 {WRITE !,"It's so far away that it looks like a point"}
ELSEIF x=2 {WRITE !,"I think that's a line, not a polygon"}
ELSEIF x=3 {WRITE !,"It's an equalateral triangle"}
ELSEIF x=4 {WRITE !,"It's a square"}
ELSE {WRITE !,"It's a polygon with ",x," number of sides" }
WRITE !,"Finished the IF test"
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestIf()
Enter the number of equal-length sides in the polygon: 1
It's so far away that it looks like a point
Finished the IF test
FOR
可以使用for
构造来重复代码段。可以基于数值或字符串值创建for
循环。
通常,For
根据数值控制变量的值执行代码块零次或多次,该数值控制变量在代码中的每个循环开始时递增或递减。当控制变量达到其结束值时,控制将退出for
循环;如果没有结束值,则循环将一直执行,直到遇到QUIT
命令。当控制退出循环时,控制变量将保持其上次执行的循环的值。
数字for
循环的形式为:
FOR ControlVariable = StartValue:IncrementAmount:EndValue {
// code block content
}
所有值都可以是正值或负值;等号和冒号周围允许有空格,但不是必需的。对于分配给变量的每个值,将重复FOR后面的代码块。
例如,以下for
循环将执行五次:
WRITE "The first five multiples of 3 are:",!
FOR multiple = 3:3:15 {
WRITE multiple,!
}
The first five multiples of 3 are:
3
6
9
12
15
还可以使用变量来确定终结值。在下面的示例中,变量指定循环的迭代次数:
/// d ##class(PHA.TEST.ObjectScript).TestFor()
ClassMethod TestFor()
{
SET howmany = 4
WRITE "The first ",howmany," multiples of 3 are "
FOR multiple = 1:1:howmany {
WRITE (multiple*3),", "
IF multiple = (howmany - 1) {
WRITE "and "
}
IF multiple = howmany {
WRITE "and that's it!"
}
}
QUIT
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestFor()
The first 4 multiples of 3 are 3, 6, 9, and 12, and that's it!
由于此示例使用控制变量Multiple
来确定3的倍数,因此它显示表达式Multiple*3
。它还使用if
命令在最后一个倍数之前插入“and”
。
注意:本例中的if
命令提供了一个很好的示例,说明了ObjectScript中优先顺序的含义(优先顺序总是从左到右,运算符之间没有层次结构)。如果if
表达式简单地为“Multiple=howany-1”
,没有任何圆括号或圆括号作为一个整体,那么表达式的第一部分“Multiple=howany”
将被求值为false(0)
;然后表达式作为一个整体将等于“0-1”
,即-1
,这意味着表达式将被求值为true
(除了循环中的最后一次迭代之外,对于每种情况都插入“and”
)。
for
的参数也可以是设置为值列表的变量;在这种情况下,代码块将对分配给该变量的列表中的每一项重复。
/// d ##class(PHA.TEST.ObjectScript).TestChoice()
ClassMethod TestChoice()
{
FOR b = "John", "Paul", "George", "Ringo" {
WRITE !, "Was ", b, " the leader? "
READ choice
}
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestChoice()
Was John the leader? John
Was Paul the leader? Ringo
Was George the leader? 1
Was Ringo the leader? 2
通过在特定情况下触发的代码块中放置一个QUIT
,可以指定不带结束值的FORE
的数字形式,从而终止FOR
。此方法提供了已发生的迭代次数的计数器,并允许使用不基于计数器的值的条件来控制。例如,下面的循环使用其计数器通知用户进行了多少次测试:
/// d ##class(PHA.TEST.ObjectScript).TestForBlock()
ClassMethod TestForBlock()
{
FOR i = 1:1 {
READ !, "Capital of MA? ", a
IF a = "Boston" {
WRITE "...did it in ", i, " tries"
QUIT
}
}
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestForBlock()
Capital of MA? a
Capital of MA? a
Capital of MA? Boston...did it in 3 tries
如果不需要计数器,则可以使用无参数:使用其计数器通知用户进行了多少次测试:
/// d ##class(PHA.TEST.ObjectScript).TestDeadFor()
ClassMethod TestDeadFor()
{
FOR {
READ !, "Know what? ", wh
QUIT:(wh = "No!")
WRITE " That's what!"
}
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestDeadFor()
Know what? 1 That's what!
Know what? 2 That's what!
Know what? 3 That's what!
Know what? 4 That's what!
Know what? 5 That's what!
Know what? No!
WHILE
和 DO WHILE
两个相关的流控制命令是WHILE和DO WHILE
命令,每个命令循环遍历代码块并根据条件终止。这两个命令在评估条件时有所不同:While
评估整个代码块之前的条件,do While
评估块之后的条件。与for
一样,代码块内的QUIT
将终止循环。
这两个命令的语法为:
DO {code} WHILE condition
WHILE condition {code}
下面的示例将斐波纳契序列中的值显示两次,直到用户指定的值-第一次使用DO WHILE
,然后使用WHILE
:
fibonacci() PUBLIC { // generate Fibonacci sequences
READ !, "Generate Fibonacci sequence up to where? ", upto
SET t1 = 1, t2 = 1, fib = 1
WRITE !
DO {
WRITE fib," " set fib = t1 + t2, t1 = t2, t2 = fib
}
WHILE ( fib '> upto )
SET t1 = 1, t2 = 1, fib = 1
WRITE !
WHILE ( fib '> upto ) {
WRITE fib," "
SET fib = t1 + t2, t1 = t2, t2 = fib
}
}
While
、Do While
和For
之间的区别在于, While
必须在执行循环之前测试控制表达式的值,但Do While
必须在执行循环之后测试值,而For
可以在循环内的任何位置测试它。这意味着,如果代码块有两个部分,其中第二个部分的执行取决于表达式的求值,则for
构造最合适;否则,选择取决于表达式求值应该在代码块之前还是之后。
I/O
命令
ObjectScript输入/输出命令提供了将数据传入和传出 Caché的基本功能。
Write
命令
CachéObjectScript支持四个命令将文字和变量值写入(显示)到当前输出设备:
WRITE
命令ZWRITE
命令ZZDUMP
命令ZZWRITE
命令
无参数显示命令
- 无参数
WRITE
显示每个定义的局部变量的名称和值,每行一个变量。它列出了公有变量和私有变量。它列出了公有变量和私有变量。它不列出全局变量、进程私有全局变量或特殊变量。它按排序顺序列出变量。它以下标树顺序列出下标变量。
它将所有数据值显示为由双引号字符分隔的引号字符串,规范数字和对象引用除外。它将分配了对象引用(OREF)值的变量显示为Variable=<Object Reference>[OREF]
。它将%LIST
格式值或位字符串值以其编码形式显示为带引号的字符串。因为这些编码的表单可能包含非打印字符,所以%
列表或位串可能显示为空字符串。
Write
不显示某些非打印字符;不显示任何占位符或空格来表示这些非打印字符。WRITE
执行控制字符(如换行符或退格符)。
- 无参数
ZWRITE
在功能上与无参数WRITE相同。 - 无参数
ZZDUMP
是生成<语法>
错误的无效命令。 - 无参数
ZZWRITE
是返回空字符串的无操作。
显示带有参数的命令
下表列出了这四个命令的参数形式的功能。所有四个命令都可以采用单个参数或逗号分隔的参数列表。所有四个命令都可以将局部、全局或进程私有变量、文字、表达式或特殊变量作为参数:
下表还列出了%Library.Utility.FormatString()
方法的默认返回值。FormatString()
方法与ZZWRITE
最相似,不同之处在于它不将%val=
作为返回值的一部分列出,并且它只返回对象引用(OREF)标识符。FormatString()
允许将变量设置为ZWRITE/ZZWRITE
格式的返回值。
描述 | WRITE |
ZWRITE |
ZZDUMP |
ZZWRITE |
FormatString() |
---|---|---|---|---|---|
每个值都在单独的行上? | NO | YES | YES (每行16个字符) | YES | 只有一个输入值 |
确定的变量名称? | NO | YES | NO | 由%val= 表示 |
NO |
未定义的变量导致<UNDEFINED> 错误? |
YES | NO(已跳过,未返回变量名) | YES | YES | YES |
所有四个命令都计算表达式并以规范形式返回数字。
描述|WRITE |
ZWRITE |
ZZDUMP |
ZZWRITE |
FormatString() |
---|---|---|---|---|
十六进制表示法? | NO | NO | YES | NO |
带引号的字符串以区别于数字? | NO | YES | NO | YES (字符串文字返回为%val=“value” ) |
显示的下标节点? | NO | YES | NO | NO |
显示另一个命名空间(扩展的全局引用)中的全局变量? | YES | YES (显示的扩展全局引用语法) | YES | YES |
显示非打印字符? | NO, 未显示;已执行控制字符 | YES, 显示为$c(N) |
YES,显示为十六进制 | YES, 显示为$c(N) |
列表值格式 | 编码字符串 | $lb(Val) 格式 |
编码字符串 | $lb(Val) 格式 |
%Status 格式 |
包含编码列表的字符串 | 包含$lb(Val) 格式列表的字符串,附加/*...*/ COMMENT指定错误和消息。 |
包含编码列表的字符串 | 包含$lb(Val) 格式列表的字符串,附加/*...*/ COMMENT指定错误和消息。 |
位串格式 | 编码字符串 |
bit()/注释清单1位。例如: %val=
bit(2…4,6)/` |
编码字符串 |
bit()/注释清单1位。例如: %val=
bit(2…4,6)/` |
对象引用(OREF )格式 |
仅OREF``|<对象引用> [OREF]格式的OREF。列出的一般信息、属性值等详细信息。列出的所有子节点 |
仅OREF | <对象引用>[OREF]格式的OREF。列出的一般信息、属性值等详细信息。 | 仅OREF,带引号的字符串 |
所有这些命令都将JSON动态数组和JSON动态对象作为OREF值返回。要返回JSON内容,必须使用%ToJSON()
,如下例所示:
/// d ##class(PHA.TEST.ObjectScript).TestWhiteJson()
ClassMethod TestWhiteJson()
{
SET jobj={"name":"Fred","city":"Bedrock"}
w jobj.%ToJSON(),!
WRITE "JSON object reference:",!
ZWRITE jobj
WRITE !!,"JSON object value:",!
ZWRITE jobj.%ToJSON()
w "",!
ZZWRITE jobj.%ToJSON()
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestWhiteJson()
{"name":"Fred","city":"Bedrock"}
JSON object reference:
jobj=<OBJECT REFERENCE>[1@%Library.DynamicObject]
+----------------- general information ---------------
| oref value: 1
| class name: %Library.DynamicObject
| reference count: 2
+----------------- attribute values ------------------
| (none)
+-----------------------------------------------------
JSON object value:
"{""name"":""Fred"",""city"":""Bedrock""}"
%val="{""name"":""Fred"",""city"":""Bedrock""}"
READ
读取命令允许接受并存储最终用户通过当前输入设备输入的输入。READ
命令可以具有以下任何参数:
READ format, string, variable
其中Format
控制用户输入区域在屏幕上的显示位置,字符串将出现在输入提示之前,Variable
将存储输入数据。
以下格式代码用于控制用户输入区域:
格式代码 | 效果 |
---|---|
! |
新起一行。 |
# |
新起一页。在终端上,它清除当前屏幕并从新屏幕的顶部开始。 |
?n
| 位于第n列位置的位置,其中n是正整数。
/// d ##class(PHA.TEST.ObjectScript).TestWhiteAll()
ClassMethod TestWhiteAll()
{
w "这是,!",!
w "这是,#",#
w "这是,?10",?10
w "这是,?20",?20
w "这是,?30",?30
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestWhiteAll()
这是,!
这是,#
这是,?10 这是,?20 这是,?30
OPEN
, USE
, CLOSE
对于更复杂的设备处理,Caché提供了丰富的选择。简而言之,可以使用OPEN
命令获取打开设备的所有权;使用USE
命令指定当前设备;使用CLOSE
命令关闭打开设备。
转载:https://blog.csdn.net/yaoxin521123/article/details/106169415