第五章 瘋狂Caché 運算符和表達式(五)
間接尋址
CachéObjectScript間接操作符(@
)允許間接爲變量賦值。間接尋址是一種通過數據字段的內容提供部分或全部命令行、命令或命令參數的動態運行時替換的技術。Caché在執行相關命令之前執行替換。
儘管與其他方式相比,間接尋址方式可以更通用的編碼,但它從來不是必需的。始終可以通過其他方式複製間接的效果,例如使用XECUTE
命令。
只有在提供明顯優勢的情況下才應該使用間接尋址。間接尋址可能會影響性能,因爲Caché在運行時而不是在編譯階段執行所需的求值。此外,如果使用複雜的間接尋址,請確保清楚地記錄代碼。間接尋址有時很難破譯。
間接尋址由間接運算符(@)指定,除下標間接尋址外,採用以下形式:
@variable
其中variable標識要從中獲取替換值的變量。變量可以是數組節點。
下面的例程說明了間接尋址查看其右側的整個變量值。
IndirectionExample
SET x = "ProcA"
SET x(3) = "ProcB"
; The next line will do ProcB, NOT ProcA(3)
DO @x(3)
QUIT
ProcA(var)
WRITE !,"At ProcA"
QUIT
ProcB(var)
WRITE !,"At ProcB"
QUIT
At ProcB
Caché識別五種類型的間接尋址:
- Name indirection
- Pattern indirection
- Argument indirection
- Subscript indirection
- $TEXT argument indirection
執行哪種類型的間接尋址取決於@
變量出現的上下文。下面將分別描述每種類型的間接連接。
間接尋址符不能與點語法一起使用。這是因爲點語法是在編譯時解析的,而不是在運行時解析的。
名稱間接
在NAME INDIRECT中,間接值爲變量名、行標籤或例程名。在執行命令之前,Caché將變量的內容替換爲預期的名稱。
名稱間接只能訪問公共變量。
使用INDIRECT引用命名變量時,間接值必須是完整的全局或局部變量名,包括任何必要的下標。
在下面的示例中,Caché將變量B
設置爲值6
。
/// d ##class(PHA.TEST.ObjectScript).TestNameIndirection()
ClassMethod TestNameIndirection()
{
s Y = "B"
w Y,!
s @Y = 6
i Y'=@Y w "不相等!",!
w @Y,!
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestNameIndirection()
B
不相等!
6
重要提示:如果調用嘗試使用間接方式獲取或設置對象屬性值,可能會導致錯誤。不要使用此類調用,因爲它們試圖繞過屬性訪問器方法(<PropertyName>Get
和<PropertyName>Set
)。相反,可以使用$CLASSMETHOD
、$METHOD
和$PROPERTY
)函數。
使用間接線標籤引用線標籤時,間接線標籤的值必須是語法上有效的線標籤。在下面的示例中,Caché將D
設置爲:
- 如果
N
的值爲1
,則爲標籤FIG
的值。 - 如果
N
的值爲2
,則標籤的值爲GO
。 Stop
在所有其他情況下的值。
Caché將控制傳遞給該標籤,該標籤的值被指定給D。
B SET D = $SELECT(N = 1:"FIG",N = 2:"GO",1:"STOP")
; ...
LV GOTO @D
當使用間接地址引用例程名稱時,間接值必須是語法上有效的例程名稱。在下面的示例中,在do
命令上使用name indirect來提供適當的過程名稱。在執行時,變量loc
的內容將替換預期名稱:
Start
READ !,"Enter choice (1, 2, or 3): ",num
SET loc = "Choice"_num
DO @loc
RETURN
Choice1()
; ...
Choice2()
; ...
Choice3()
; ...
```java
名稱間接只能替換Name值。以下示例中的第二個`set`命令因上下文原因返回錯誤消息。在計算等號右側的表達式時,Caché將`@var1`解釋爲對變量名的間接引用,而不是數字值。
```java
SET var1 = "5"
SET x = @var1*6
可以按如下方式重新轉換該示例以正確執行:
SET var1 = "var2",var2 = 5
SET x = @var1*6
模式間接
模式間接是間接的一種特殊形式。間接運算符替換模式匹配。間接值必須是有效模式。當希望選擇多個模式匹配,然後將它們用作模式匹配時,模式匹配間接性特別有用。
在下面的示例中,間接碼與模式匹配一起使用,以檢查有效的美國郵政編碼(ZIP)。這樣的代碼可以採用五位數(Nnnnn
)或九位數(Nnnnnnnn
)形式。
第一個set命令
設置五位數表單的模式。第二個set命令
設置九位數表單的模式。僅當後置條件表達式($length(Zip)=10
)的計算結果爲true(非零)
時,纔會執行第二個set命令
,這僅在用戶輸入9位數形式時纔會發生。
/// d ##class(PHA.TEST.ObjectScript).TestPattern()
ClassMethod TestPattern()
{
d GetZip
GetZip()
SET pat = "5N"
READ !,"Enter your ZIP code (5 or 9 digits): ",zip
SET:($LENGTH(zip)=10) pat = "5N1""-""4N"
IF zip'?@pat {
WRITE !,"Invalid ZIP code"
DO GetZip()
}
}
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestPattern()
Enter your ZIP code (5 or 9 digits): 1
Invalid ZIP code
Enter your ZIP code (5 or 9 digits): 55555
DHC-APP>d ##class(PHA.TEST.ObjectScript).TestPattern()
Enter your ZIP code (5 or 9 digits): 99999999
Invalid ZIP code
Enter your ZIP code (5 or 9 digits): 55555-4444
將間接與模式匹配結合使用是本地化應用程序中使用的模式的便捷方法。在這種情況下,可以將模式存儲在單獨的變量中,然後在實際的模式測試期間間接引用它們。(這也是名稱間接的示例)。要移植這樣的應用程序,只需修改模式變量本身。
參數簡介
在參數間接中,間接計算結果爲一個或多個命令參數。相比之下,名稱間接只適用於參數的一部分。
要說明這一區別,請將下面的示例與名稱間接下給出的示例進行比較。
Start
READ !,"Enter choice (1, 2, or 3): ",num
SET loc = "Choice"_num
DO @loc
RETURN
Choice1()
w "1",!
; ...
Choice2()
w "2",!
; ...
Choice3()
w "3",!
; ...
Enter choice (1, 2, or 3): 11
DHC-APP>d Start^PHA.MOB.TEST
Enter choice (1, 2, or 3): 22
DHC-APP>d Start^PHA.MOB.TEST
Enter choice (1, 2, or 3): 22
DHC-APP>d Start^PHA.MOB.TEST
在本例中,@loc
是間接參數的一個示例,因爲它提供了參數的完整形式(即,LABEL^ROUTINE
)。
在名稱間接示例中,@loc
是名稱間接的示例,因爲它只提供參數的一部分(標籤名稱,假定其入口點在當前例程中,而不是單獨的例程中)。
在下面的示例中,第二個set
命令是一個名稱間接示例(只是參數的一部分,變量的名稱),而第三個set
命令是一個參數間接示例(整個參數)。
Argument
SET a = "var1",b = "var2 = 3*4"
SET @a = 5*6
SET @b
WRITE "a = ",a,!
WRITE "b = ",b,!
w @a
DHC-APP>d Argument^PHA.MOB.TEST
a = var1
b = var2 = 3*4
30
下標間接
下標間接尋址是名稱間接尋址的擴展形式。在下標間接中,間接值必須是局部或全局數組節點的名稱。下標間接與其他形式的間接在句法上是不同的。下標間接符使用以下格式的兩個間接運算符:
@array@(subscript)
假設有一個名爲^client
的全局數組,其中第一級節點包含客戶的名稱,第二級節點包含客戶的街道地址,第三級節點包含客戶的城市、州和郵政編碼。要寫出數組中第一條記錄的三個節點,可以使用以下形式的WRITE
命令:
WRITE !,^client(1),!,^client(1,1),!,^client(1,1,1)
執行此命令時,輸出以下內容:
John Jones
42 Arnold St.
Boston, MA 02745
要寫出一定範圍的記錄(比如前10條),可以修改代碼,以便在for
循環中執行寫操作。例如:
FOR i = 1:1:10 {
WRITE !,^client(i),!,^client(i,1),!,^client(i,1,1)
}
當for
循環執行時,變量i
遞增1
,並用於選擇要輸出的下一條記錄。
雖然比前面的示例更一般化,但這仍然是非常專業的代碼,因爲它顯式指定了數組名稱和要輸出的記錄數。
若要將此代碼轉換爲更通用的形式,從而允許用戶列出在三個節點級別中存儲名稱、街道和城市信息的任何數組(全局或本地)中的一系列記錄,可以使用下標間接法,如下面的示例所示:
Start
READ !,"Output Name, Street, and City info.",!
READ !,"Name of array to access: ",name
READ !,"Global or local (G or L): ",gl
READ !,"Start with record number: ",start
READ !,"End with record number: ",end
IF (gl["L")!(gl["l") {SET array = name}
ELSEIF (gl["G")!(gl["g") {SET array = "^"_name}
SET x = 1,y = 1
FOR i = start:1:end {DO Output}
RETURN
Output()
WRITE !,@array@(i)
WRITE !,@array@(i,x)
WRITE !,@array@(i,x,y)
QUIT
輸出子例程中的寫入命令使用下標間接引用請求的數組和請求的記錄範圍。
在計算下標間接時,如果間接實例引用無下標的全局或局部變量,則間接的值是變量名稱和第二個間接運算符右側的所有字符(包括括號)。
對於局部變量,最大下標級別數爲255。對於全局變量,最大下標級別數取決於下標,並且可能大於255,如使用Caché全局參數中的全局結構中所述。試圖使用間接方式填充下標級別超過255個的局部變量會導致<語法>
錯誤。
類參數可用作下標間接尋址的基數,與局部或全局變量用作基數的方式相同。例如,可以使用具有以下語法的類參數執行下標間接尋址:
SET @..#myparam@(x,y) = "stringval"
$TEXT參數間接
顧名思義,$TEXT
參數間接只允許在$TEXT
函數參數的上下文中使用。間接地址的值必須是有效的$TEXT
參數。
使用$TEXT
參數間接主要是爲了方便,以避免產生相同結果的多種形式的間接。例如,如果局部變量LINE
包含條目引用“START^MENU
”,則可以對行標籤和例程名稱使用NAME INDIRECT來獲取該行的文本,如下所示:
SET LINETEXT = $TEXT(@$PIECE(LINE,"^",1)^@$PIECE(LINE,"^",2))
可以使用$TEXT參數間接以更簡單的方式產生相同的結果,如下所示:
SET LINETEXT = $TEXT(@LINE)
注意:間接引用在例程中更加方便,在類方法中會出錯。