Routines
下面显示了一个名为 demoroutine
的示例routines
,它是用 Caché ObjectScript
编写的。此示例使我们有机会查看一些常用命令、运算符和函数,并了解代码在例程中的组织方式。
; this is demoroutine
write "Use one of the following entry points:"
write !,"random"
write !,"input"
write !,"interesting"
quit
//this procedure can be called from outside the routine
// d ^demoroutine
// do interesting^demoroutine
random() public {
set rand=$RANDOM(10)+1 ; rand is an integer in the range 1-10
write "Your random number: "_rand
set name=$$getnumbername(rand)
write !, "Name of this number: "_name
}
//this procedure can be called from outside the routine
input() public {
read "Enter a number from 1 to 10: ", input
set name=$$getnumbername(input)
write !, "Name of this number: "_name
}
//this procedure can be called only from within this routine
getnumbername(number) {
set name=$CASE(number,1:"one",2:"two",3:"three",
4:"four",5:"five",6:"six",7:"seven",8:"eight",
9:"nine",10:"ten",:"other")
quit name
}
/* write some interesting values
this procedure can be called from outside the routine
*/
interesting() public {
write "Today's date: "_$ZDATE($HOROLOG,3)
write !,"Your installed version: "_$ZVERSION
write !,"Your username: "_$USERNAME
write !,"Your security roles: "_$ROLES
}
routines
名称不必包含在routines
中。作为约定俗成,一般将routines 名称
作为routines
开头的注释或routines
中的第一个标签。在 Studio
中查看routines
时,routines 名称
将显示在显示该例程的选项卡上。标签
(也称为入口点
):该例程具有多个标签:random,input,getnumbername和interesting。标签用于指示过程(如本例中所示)、函数和子例程的起点。$HOROLOG
、$ZVERSION
、$USERNAME
和$ROLES
是系统变量(在 Caché
中称为特殊变量)。大多数特殊变量都包含 Caché
操作环境的各个方面、当前处理状态等的值。SAMPLES>do ^demoroutine
Use one of the following entry points:
random
input
SAMPLES>
SAMPLES>do input^demoroutine
Enter a number from 1 to 10: -7
Name of this number: other
SAMPLES>do interesting^demoroutine
Today's date: 2010-11-30
Your installed version: Cache for Windows (x86-32) 2010.2 (Build 454U) Sun Oct 24 2010 17:14:03 EDT
Your username: UnknownUser
Your security roles: %All
SAMPLES>
过程(Procedures)、函数(Functions)、subroutines
在 Caché ObjectScript
例程中,标签定义了以下代码单元之一的起点:
Procedures
)(可选返回值)。过程中定义的变量是该过程的私有变量,这意味着它们对其他代码不可用。对于函数和子routines,情况并非如此。Functions
)(返回一个值)。子routines
(不返回值)。InterSystems
建议您使用过程,因为这简化了控制变量范围的任务。但是,在现有代码中,您可能还会看到函数和子例程,并且能够识别它们非常有用。以下列表显示了所有这些形式的代码的外观。语法定义如下
label(args) scopekeyword {
zero or more lines of code
QUIT returnvalue //可选
}
label
是过程的标识符。
args
是可选的逗号分隔参数列表。即使没有参数,也必须包括括号。
可选的作用域关键字是以下之一(不区分大小写):
Public
,则该过程是公共的,可以在例程本身之外调用。 Private
,则该过程是私有的,只能由同一例程中的其他代码调用。如果尝试从另一个例程访问该过程,则会发生
错误。returnvalue
是要返回的可选单值。若要返回值,必须使用 QUIT
命令。如果不想返回值,可以省略 QUIT
命令,因为大括号表示该过程的结束。
过程可以将变量声明为公共变量,尽管这种做法不被视为现代方法。为此,请在紧挨作用域关键字之前的方括号中包括一个逗号分隔的变量名称列表。
label(args) scopekeyword
zero or more lines of code
QUIT optionalreturnvalue
args
是可选的逗号分隔参数列表。即使没有参数,也必须包括括号。
可选的作用域关键字是“公共”
label(args) scopekeyword
zero or more lines of code
QUIT
args
是可选的逗号分隔参数列表。如果没有参数,则括号是可选的。
可选的作用域关键字是“公共”
描述 | Routines | 子routines | 函数 | 过程 |
---|---|---|---|---|
可以接受参数 | 不 | 是的 | 是的 | 是的 |
可以返回值 | 不 | 不 | 是的 | 是的 |
可以在例程外部调用(默认情况下) | 是的 | 是的 | 是的 | 不 |
其中定义的变量在代码完成执行后可用 | 是的 | 是的 | 是的 | 取决于变量的性质 |
变量
在 Caché ObjectScript
中,有两种类型的变量,根据它们保存数据的方式进行分类:
变量的名称遵循以下规则:
Caché还支持一种称为百分比变量的特殊变量;这些不太常见。百分比变量的名称以百分比字符 (%) 开头。百分比变量的特殊之处在于它们始终是公开的;也就是说,它们对进程中的所有代码都是可见的。这包括调用堆栈中的所有方法和所有过程。
定义百分比变量时,请使用以下规则:
Caché ObjectScript 中的变量是弱动态类型的。它们是动态类型的,因为您不必为变量声明类型,并且变量可以采用任何合法值 , 即任何合法的文本值或任何合法的 Caché ObjectScript 表达式。它们是弱类型的,因为用法决定了如何评估它们。
Caché ObjectScript 中的合法文本值具有以下形式之一:
根据上下文,字符串可以被视为数字,反之亦然。同样,在某些情况下,值可能被解释为布尔值(真或假)值;任何计算结果为零的东西都被视为假的;其他任何事情都被视为真实。
创建类时,可以为属性、方法的参数等指定类型。Caché 类机制会像您预期的那样强制实施这些类型
变量值的长度有限制。如果在安装中启用了长字符串,则限制为 3,641,144 个字符。如果未启用长字符串,则限制为 32,767 个字符。
通常使用 SET
命令定义变量。如前所述,定义全局变量时,这会立即影响数据库。
仅当终止全局变量时,它才会变得未定义(这意味着通过 KILL 命令将其删除)。这也会立即影响数据库。
局部变量可以通过以下三种方式之一变得未定义:
若要确定是否定义了变量,请使用 $DATA 函数。
Caché
提供了另一种机制来使您能够控制变量的作用域:NEW
命令。此命令的参数是逗号分隔列表中的一个或多个变量名称。变量必须是公共变量,不能是全局变量。
此命令为变量建立一个新的有限上下文(可能已存在,也可能不存在)
多维数组
在 Caché ObjectScript
中,任何变量都可以是 Caché 多维数组
(也称为数组)。多维数组通常用于保存一组以某种方式相关的值。Caché ObjectScript
提供了命令和函数。
您可以直接使用多维数组,也可以不直接使用多维数组,具体取决于您使用的系统类和您自己的首选项。Caché
提供了一个基于类的替代方案,当您需要一个容器来存储相关值集时,可以使用它。
set myarray(1)="value A"
set myarray(2)="value B"
set myarray(3)="value C"
Set myarray(1,1,1)="grandchild of value A"
set myarray("notes to self","2 Dec 2010")="hello world"
zwrite myarray
set ^myglobal(1)="value A"
set ^myglobal(2)="value B"
set ^myglobal(3)="value C"
运算符
Caché ObjectScript
中的运算符优先级严格地从左到右;在表达式中,操作按其出现的顺序执行。可以在表达式中使用显式括号来强制在执行其他操作之前执行某些操作。
通常,即使不是严格需要括号,也可以使用括号。这样做对其他程序员(以及以后的你自己)很有用,因为它使代码的意图更清晰。
Caché ObjectScript
为常见活动提供了以下运算符:
Caché ObjectScript 还包括在某些语言中没有等效项的运算符。最重要的有如下:
模式匹配运算符 (?) 测试其左操作数中的字符是否在其右操作数中使用模式。您可以指定模式发生的次数、指定替代模式、指定模式嵌套等。
二进制包含运算符 ([) 返回 1(true)或 0(false),具体取决于右操作数中的字符序列是否为左操作数的子字符串。
运算符 (]) 后面的二进制文件测试在 ASCII 排序规则序列中,左操作数中的字符是否位于右操作数中的字符之后。
运算符 (]]) 之后的二进制排序测试左操作数是否在数字下标排序规则序列中的右操作数之后排序。
间接寻址运算符 (@) 允许您对部分或全部命令参数、变量名称、下标列表或模式执行动态运行时替换。Caché 在执行关联的命令之前执行替换。
命令
本节概述了您最有可能在 Caché ObjectScript 中使用和看到的命令。这些命令包括与其他语言中的命令相似的命令,以及在其他语言中没有等效命令的其他命令。
命令的名称不区分大小写,尽管按照惯例,它们在运行文本中以全部大写形式显示。
Caché ObjectScript
提供了用于执行熟悉任务的命令,如下所示:
要定义变量,请使用 SET
,如前所述。
要删除变量,请使用 KILL
,如前所述。
要控制逻辑流,请使用以下命令:
要捕获错误,请使用 TRY 和 CATCH,它们协同工作。
若要写入值,请使用 WRITE。这会将值写入当前设备(例如,终端或文件)。
如果不使用参数,此命令将写入所有局部变量的值。
此命令可以使用一小组用于定位输出的格式控制代码字符。在现有代码中,您可能会看到感叹号,它开始一个新行。
要从当前设备(例如终端)读取值,请使用 READ。
要使用主设备以外的设备,请使用以下命令:
若要控制并发性,请使用 LOCK
。请注意,Caché 锁管理系统与其他语言中的类似系统不同。
在多个进程可能访问同一变量或其他项的情况下,可以使用此命令。
要管理事务,请使用 TSTART、TCOMMIT、TROLLBACK 和相关命令。
对于调试,请使用 ZBREAK 和相关命令。
若要暂停执行,请使用 HANG
。
在 Caché ObjectScript
中,您可以通过以下方式使用多维数组:
要定义节点,请使用 SET
命令。
要删除单个节点或所有节点,请使用KILL
命令。
删除整个多维数组:
kill myarray
kill myarray("2 Dec 2010")
SAMPLES>ZWRITE ^myarray
^myarray(1)="value A"
^myarray(2)="value B"
^myarray(3)="value C"
]
MERGE
。例如,以下命令将整个内存中数组(sourcearray)复制到新的全局(^mytestglobal)中: MERGE ^mytestglobal=sourcearray
特殊变量
本节介绍一些 Caché 特殊变量。这些变量的名称不区分大小写。
一些特殊变量提供有关运行代码的环境的信息。其中包括:
$HOROLOG
,其中包含操作系统给出的当前进程的日期和时间。请参阅本章后面的“日期和时间值”。$USERNAME
和$ROLES
,其中包含有关当前正在使用的用户名以及该用户所属的角色的信息。$ZVERSION
,其中包含一个字符串,用于标识当前正在运行的 Caché 版本。$JOB
、$ZTIMEZONE
、$IO
和$ZDEVICE
。$STACK
、$TLEVEL
、$NAMESPACE
和$ZERROR
。特殊变量$SYSTEM
提供对大量实用工具方法的与语言无关的访问。
特殊变量 $SYSTEM
是%SYSTEM package
,其中包含提供满足各种需求的类方法的类。引用方法的习惯方式%SYSTEM
是构建一个使用$SYSTEM
变量的引用。例如,以下命令在%SYSTEM.OBJ
中执行 SetFlags()
方法。
DO $SYSTEM.OBJ.SetFlags("ck")
DO ##class(%SYSTEM.OBJ).SetFlags("ck")
DO $System.OBJ.SetFlags("ck")
DO $SYSTEM.OBJ.SetFlags("ck")
DO $system.OBJ.SetFlags("ck")
SAMPLES>d $system.OBJ.Help()
'Do $system.OBJ.Help(method)' will display a full description of an individual method.
Methods of the class: %SYSTEM.OBJ
CloseObjects()
Deprecated function, to close objects let them go out of scope.
Compile(classes,qspec,&errorlog,recurse)
Compile a class.
CompileAll(qspec,&errorlog)
Compile all classes within this namespace
....
SAMPLES>d $system.OBJ.Help("Compile")
Description of the method:class Compile:%SYSTEM.OBJ
Compile(classes:%String="",qspec:%String="",&errorlog:%String,recurse:%Boolean=0)
Compile a class.
<p>Compiles the class <var>classes</var>, which can be a single class, a comma separated list,
a subscripted array of class names, or include wild cards. If <var>recurse</var> is true then
do not output the intial 'compiling' message or the compile report as this is being called inside
another compile loop.<br>
<var>qspec</var> is a list of flags or qualifiers which can be displayed with
'Do $system.OBJ.ShowQualifiers()'
and 'Do $system.OBJ.ShowFlags()
锁定和并发控制
任何多进程系统的一个重要特征是并发控制,能够防止不同的进程同时更改数据的特定元素,从而导致损坏。因此,Caché ObjectScript 提供了一个锁管理系统。
基本的锁定机制是 LOCK
命令。此命令的目的是延迟一个进程中的活动,直到另一个进程发出可以继续的信号。
重要的是要了解,锁本身并不阻止其他进程修改关联的数据;也就是说,Caché不强制执行单边锁定。锁定仅按约定工作:它要求相互竞争的进程都使用相同的锁定名称实现锁定。
可以使用 LOCK 命令创建锁(替换进程以前欠的所有锁)、添加锁、删除特定锁以及删除进程拥有的所有锁。
出于此简单讨论的目的,LOCK 命令使用以下参数:
下面描述了一个常见的锁定方案:进程 A 发出 LOCK 命令,Caché 尝试创建锁定。如果进程 B 已具有具有给定锁名称的锁,则进程 A 将暂停。具体来说,进程 A 中的 LOCK 命令不返回,并且不能执行连续的代码行。当进程 B 释放锁时,进程 A 中的 LOCK 命令最终返回并继续执行。
在许多情况下,系统会自动在内部使用 LOCK 命令,例如当您使用持久性对象(本书稍后将讨论)或使用某些 Caché SQL 命令时。
Caché 维护一个系统范围的内存中表,该表记录所有当前锁以及拥有这些锁的进程。此表(锁表)可通过管理门户访问,你可以在其中查看锁并(在极少数情况下,如果需要)将其删除。请注意,任何给定的进程都可以拥有多个具有不同锁名称的锁(甚至是具有相同锁名称的多个锁)。
当进程结束时,系统会自动释放该进程拥有的所有锁。因此,通常不需要通过管理门户删除锁,除非出现应用程序错误。
锁定表不能超过可以指定的固定大小。有关信息,请参阅《Caché 监视指南》中的“监视锁”。因此,锁定表可能会填满,因此不可能再有锁定。如果发生这种情况,Caché 会将以下消息写入 cconsole.log 文件:
LOCK TABLE FULL
填充锁表通常不被认为是应用程序错误;Caché 还提供了一个锁队列,进程会等到有空间将其锁添加到锁表中。
但是,如果两个进程各自对另一个进程已锁定的变量断言增量锁定,则这是一个称为死锁的情况,并且被视为应用程序编程错误。
锁定数组时,可以锁定整个数组或数组中的一个或多个节点。锁定阵列节点时,将阻止其他进程锁定从属于该节点的任何节点。其他进程也被阻止锁定锁定节点的直接祖先。
创建锁时,可以指定锁类型代码的组合,这些代码控制锁的性质。本节讨论锁类型的一些关键概念。
根据锁的类型,可以创建具有相同锁名的多个锁。这些锁可以由同一进程或不同进程拥有,同样取决于锁的类型。锁定表显示所有这些信息。
任何锁要么是独占的(默认的),要么是共享的。这些类型具有以下意义:
通常,独占锁的用途是指示您打算修改某个值,并且其他进程不应尝试读取或修改该值。共享锁的目的是指示您打算读取某个值,并且其他进程不应尝试修改该值。但是,他们可以读取值。
任何锁定也是非升级的(默认的)或升级的。升级锁的目的是为了更容易地管理大量锁,这些锁会消耗内存并增加填充锁表的机会。在锁定同一阵列的多个节点时,可以使用升级锁定。对于升级锁,如果给定进程在给定数组的同级节点上创建了超过特定数量(默认为 1000 个)的锁,Caché
将删除所有单独的锁名,并在父级别用新锁替换它们。例如,您可能有 1000 个格式为 ^MyGlobal(“sales”,“EU”,salesdate)
的锁,其中 salesdate
表示日期。当同一进程尝试创建此形式的另一个锁(并且这些锁都在升级)时,Caché
会删除所有这些锁,并用名称为 ^MyGlobal(“sales”,“EU”)
的锁替换它们。锁定表维护此新锁的锁定计数。此锁定计数当前为 1001,但当您添加相同形式的其他锁定名称时,锁定表会增加锁定名称 ^MyGlobal(“sales”,“EU”)
的锁定计数。同样,当您删除相同形式的锁名称时,锁表会递减此锁计数。
系统函数
Caché ObjectScript
中一些最常用的系统函数。
这些函数的名称不区分大小写
。
Caché
类库还提供了大量实用工具方法,您可以像使用函数一样使用这些方法。
在给定一些输入的情况下,您可以使用以下函数来选择一个值:
$CASE
将给定的测试表达式与一组比较值进行比较,然后返回与匹配的比较值关联的返回值。例如:SAMPLES>set myvar=1
SAMPLES>write $CASE(myvar,0:"zero",1:"one",:"other")
one
$SELECT
检查一组表达式并返回与第一个 true 表达式关联的返回值。例如:SAMPLES>set myvar=1
SAMPLES>write $SELECT(myvar=0:"branch A",1=1:"branch B")
branch B
可以使用以下函数来测试变量或变量节点是否存在。
$DATA
函数。 $GET
函数。Caché ObjectScript 提供了一种原生列表格式。您可以使用以下函数来创建和使用这些列表:
$LISTBUILD
返回一种称为列表的特殊字符串。有时这称为$LIST
格式,以将此类列表与其他类型的列表(例如逗号分隔的列表)区分开来。$LIST
列表的唯一受支持的方法是使用 Caché ObjectScript
列表函数。此类列表的内部结构未记录在案,如有更改,恕不另行通知。$LIST
返回列表元素或可用于替换列表元素。$LISTLENGTH
返回列表中元素的数目。$LISTFIND
返回给定元素在给定列表中的位置。注意
:
系统类 %Library.List
等效于$LISTBUILD
返回的列表。也就是说,当类具有 %Library.List
类型的属性时,可以使用此处命名的函数来处理该属性。可以通过此类的短名称 %List
来引用此类。
Caché ObjectScript 还具有一组广泛的函数,用于高效使用字符串:
$EXTRACT
使用字符计数返回或替换子字符串。$FIND
按值查找子字符串,并返回一个整数,指定其在字符串中的结束位置。$JUSTIFY
返回一个右对齐的字符串,在左侧用空格填充。$ZCONVERT
将字符串从一种形式转换为另一种形式。它支持大小写转换(到大写、小写或标题大小写)和编码转换(在各种字符编码样式之间)。$TRANSLATE
通过执行逐个字符替换来修改给定的字符串。$REPLACE
在字符串中逐个字符串地执行替换,并返回一个新字符串。$PIECE
从字符分隔的字符串(通常称为分段字符串)返回子字符串。许多较旧的应用程序使用字符分隔的字符串作为包含相关值的便捷格式,每个值都是较大字符串中的子字符串。大字符串充当记录,子字符串是其字段。$LENGTH
返回指定字符串中的字符数或指定字符串中的分隔子字符串数,具体取决于所使用的参数。您可以使用以下函数将多维数组作为一个整体来处理:
$ORDER
允许您按顺序访问多维数组中的每个节点。$QUERY
使您能够访问数组中的每个节点和子节点,在子节点上上下移动。$DATA
可以指示给定节点是否存在以及给定节点是否具有子节点。$GET
获取给定节点的值,否则获取默认值。有时,在创建字符串时,需要包含无法键入的字符。对于这些,您可以使用$CHAR
。
给定一个整数,$CHAR
返回相应的 ASCII
或Unicode
字符。常见用途:
$CHAR(9)
是一个选项卡。$CHAR(10)
是一个换行符。$CHAR(13)
是一个回车符。$CHAR(13,10)
是回车符和换行符对。函数$ASCII
返回给定字符的 ASCII
值。
在现有代码中,您可能会看到$ZU(n)
、$ZUTIL(n)
、$ZU(n,n)
等项,其中 n
是整数。这些是$ZU
函数,这些函数现已弃用,不再记录。它们仍然可用,但鼓励用户将它们替换为Caché
类库中执行等效操作的方法和属性。
日期和时间值
本节简要概述了 Caché ObjectScript 中的日期和时间值。
若要访问当前进程的日期和时间,请使用$HOROLOG
特殊变量。因此,在许多 Caché
应用程序中,日期和时间都以此变量使用的格式存储和传输。此格式通常称为$H
格式或$HOROLOG
格式。
$HOROLOG
从操作系统检索日期和时间,因此始终位于本地时区。
Caché
类库包括数据类型类,用于以更常见的格式(如 ODBC)表示日期,许多应用程序都使用这些类而不是$H
格式。
Caché
还提供了$ZTIMESTAMP
特殊变量,该变量以$H
格式将当前日期和时间作为协调世界时值。这是一个全球时间和日期标准;此值很可能与本地时间(和日期)值不同。
Caché ObjectScript 包含用于转换日期和时间值的函数。
$H
格式的日期,函数 $ZDATE
返回一个字符串,该字符串以指定格式表示日期。$H
格式的日期和时间,函数 $ZDATETIME
返回一个字符串,该字符串以指定格式表示日期和时间。SAMPLES>WRITE $ZDATETIME($HOROLOG,3)
2010-12-03 14:55:48
$ZTIME
和$ZTIMEH
时间从$H
格式转换为$H
格式。$H
格式是一对以逗号分隔的数字。例如:54321,12345
$NOW()
))提供分数部分。使用宏和包含文件
如前所述,您可以定义宏,并在以后的同一例程中使用它们。更常见的是,您可以在包含文件中定义它们。包含文件是 Studio 中扩展名为 .inc
的文档。
#define
指令或其他预处理器指令。例如:#define mymacro "hello world"
#Include
指令。例如:#Include myincludefile
$$$macroname
//或者
$$$macroname(arguments)
预处理器指令记录在使用 Caché ObjectScript 的“ObjectScript 宏和宏预处理器”中。
调用routines 和 subroutines
若要执行例程,请使用 DO
命令,如下所示.
do ^routinename
若要执行过程、函数或 subroutines
(不访问其返回值),请使用以下命令:
do label^routinename
do label^routinename(arguments)
若要执行过程、函数或子例程并引用其返回值,请使用格式为 $$label^routinename
或 $$label^routinename(参数)
的表达式。例如:
set myvariable=$$label^routinename(arguments)
在所有情况下,如果标签位于同一routines
中,则可以省略插入记号和例程名称。例如:
do label
do label(arguments)
set myvariable=$$label(arguments)
按值或按引用传递变量
调用代码时,可以通过值或引用将变量值传递给该代码。在大多数情况下,这些变量是没有下标的局部变量,因此本节首先讨论这些变量。
与其他编程语言一样,Caché
有一个包含每个局部变量值的内存位置。变量的名称充当内存位置的地址。
将不带下标的局部变量传递给过程、函数或子例程时,将按值传递该变量。这意味着系统会复制该值,以便原始值不受影响。若要改为传递内存地址,请在参数列表中紧挨变量名称之前放置一个句点。例如:
do ^myroutine(.myarg)