軟體工程師手冊/語言詞典/PLI
這是維基百科條目。
PL/I 是一種完整的過程式語言。
程式的入口點指定為OPTIONS(MAIN),在過程語句中,例如
tree: procedure options (main);
沒有列特性。
賦值示例
a = b*(c-d);
在這個數值賦值語句中,d 從 c 中減去,結果乘以 b 的值,然後儲存到變數 a 中。
s = t || u;
在這個字串賦值示例中,字串 u 連線到字串 t 的末尾,組合字串儲存到字串變數 s 中。
注意:使用編譯時選項,連線運算子|| 可以替換為另一對符號,以相容現有的 PL/I 程式,通常使用!!。
每個註釋以/* 開頭,以 */ 結尾。註釋可以跨越多行。
/*
* this is a long comment.
*/
宣告不是強制性的,儘管大多數 PL/I 編譯器提供一個編譯器選項來強制宣告,而一些子集編譯器則要求宣告每個變數。
要將 i 宣告為二進位制
declare i fixed binary;
要將 i 宣告為二進位制並將其初始值設定為 0
declare i fixed binary initial (0);
要將變數宣告為十進位制,
declare D fixed decimal;
declare E fixed decimal (10,4);
第一個宣告是儲存在十進位制格式中的整數 D,第二個是能夠儲存十位數字且小數點後有 4 位數字的變數 E。要初始化 E,請編寫
declare E fixed decimal (10,4) initial (123456.7891);
請注意,諸如 0.1 之類的值是精確儲存的。
要定義一個浮點變數
declare f float;
使用單精度硬體;和雙精度
declare float decimal (15);
和擴充套件精度
declare float decimal (18);
(PC 的典型宣告)。數字 15 和 18 表示將儲存的小數位數。(雖然值實際上以二進位制形式儲存,但小數精度比以二進位制形式指定精度更容易記住。)要指定一個複數變數,只需在宣告中包含單詞 COMPLEX,因此
DECLARE C FLOAT DECIMAL (15) COMPLEX;
C = 5.2+8i;
賦值包括複數常數 5.2+8i,其中 5.2 是實部,8 是虛部。
基數(二進位制或十進位制)、範圍(定點或浮點)和模式(實數或複數)的算術資料屬性可以按任何順序出現,精度屬性(例如 (15))不能是第一個屬性。
以下定義是等效的,最後一個除外。
declare number fixed binary (15) real;
declare number binary fixed (15) real;
declare number binary fixed real (15);
declare number binary fixed (15); /* because real is the default value of mode */
declare number (15) binary fixed real; /* defines 15 numbers with default precision */
要宣告一個具有圖片屬性的變數
declare p picture ('9999v.999');
將變數 P 描述為具有 7 位十進位制數字,小數點後有三位數字。小數點儲存在儲存器中。字母 V 指示實際小數點的位置。對於歐洲,規範可以包含逗號
declare p picture ('9999v,999');
要將變數 S 宣告為字元字串
declare S character(10);
字串 S 可以精確儲存 10 個字元。
要將變數 V 宣告為能夠儲存最多 100 個字元的字元字串
declare V character (100) varying;
簡單的賦值
V= 'abc';
V 包含三個字元 abc。
V = 'friendly';
V 包含 8 個字元 friendly。
PL/I 也允許位字串
declare b bit(10);
允許儲存長度為 10 的位字串。賦值語句
b = '1011110010'b;
按給定順序儲存位。此類位字串可以像字元字串一樣進行搜尋等。
可以宣告資料結構或聚合
declare 1 name,
2 first_name character (10),
2 surname character (15);
陣列透過上述方法之一以及維度資訊來宣告
declare x(10) float;
指定了一個包含十個元素的陣列,x(1) 到 x(10)。預設下界為 1。要指定不同的下界
declare y(0:15) float;
聲明瞭一個包含 16 個元素的陣列 Y(0) 到 Y(15),其中元素為浮點型。
多維陣列透過逗號分隔維度來宣告
declare M ( 2 , 4:5 ) fixed binary (15)
定義了一個包含 4 個元素的二維陣列:M (1, 4)、M (1, 5)、M (2, 4)、M (2, 5)
為了處理以兩位數年份和四位數年份給出的日期,這些日期可能在同一個程式中混合使用,可以使用 DATE 屬性。
declare d date ('YY'); [more to come]
關鍵字 DECLARE 和 INITIAL 可以縮寫為 DCL 和 INIT,許多屬性也可以縮寫。
| 屬性 | 縮寫 |
|---|---|
BINARY
|
BIN
|
DECIMAL
|
DEC
|
CHARACTER
|
CHAR
|
VARYING
|
VAR
|
PICTURE
|
PIC
|
POINTER
|
PTR
|
put list ('Hello world!');
put skip list ('Hello world!');
第一個示例將 Hello world! 寫入標準輸出(螢幕或印表機或檔案)。這些單詞在該行上任何已有的輸出之後列印。第二個示例在列印之前開始新的一行(單詞 SKIP)。
描述方法/函式/過程是如何宣告和實現的。以下示例是一個包含函式的完整 PL/I 程式。
/* A program to calculate the area of a triangle, */
/* given two sides and an included angle. */
d: procedure options (main);
declare (b, c, angle) float;
put list ('Today is ', date());
put skip list ('Please type the lengths of two sides, and an angle');
get list (b, c, angle);
put skip list ('The two sides are', b, c, 'and the angle is', angle, 'degrees');
put list ('Area of triangle=', area(b, c, angle));
area: procedure (b, c, angle) returns (float);
declare (b, c, angle) float;
return (b*c*sin(angle)/2);
end area;
end d;
上述程式包含了一些典型的列表定向形式的輸入和輸出語句。第一個PUT語句列印日期。第二個請求讀者輸入三個值,表示某個三角形的引數。第三個回顯輸入作為確認。最後一個PUT語句包含一個函式引用area(b, c, angle),它呼叫了該函式。計算完成後,當前的PUT語句列印該面積。函式AREA的定義包括一個PROCEDURE語句,該語句列出了從呼叫程式所需的引數,即b、c和angle。這些引數在下一條語句(DECLARE)中定義。PROCEDURE語句還說明了要返回給呼叫程式的結果型別,即浮點值。這由RETURNS (FLOAT)表示。RETURN語句包含用於計算三角形面積的公式。然後它將控制權返回給呼叫程式。RETURN語句通常包含變數的名稱,但也可以包含表示式(如本例所示)。
PL/I 變數的作用域限制在宣告它們的程式中。作用域擴充套件到該程式中包含的任何程式(當然,除了任何程式中出現相同名稱的新宣告)。
如果未宣告變數,則該變數的作用域為整個程式。同樣,如果宣告(對於同名變數)出現在從屬(使用變數)的一個或多個塊中,則作用域將從包含宣告的塊中排除。
PL/I 有兩種型別的塊。一種是用於子程式和函式的PROCEDURE塊。另一種是BEGIN塊(BEGIN; ... END;)。BEGIN塊的行為類似於PROCEDURE塊,但BEGIN塊沒有引數,也不必有標籤。它以正常的順序執行進入,而PROCEDURE塊只能透過執行CALL語句或函式引用來執行。
考慮條件語句的兩個示例
if a = b then c = d;
if a > b then c = d; else c = e;
第一個示例說明了一個可選執行的語句c = d;
第二個示例顯示了兩個可選執行的語句,第一個在a > b時執行,第二個在a <= b時執行;
當需要有條件地執行多個語句時,使用語句括號do和end。
if a > b then
do; p = q; r = s; end;
select 組提供多分支評估,它可以用以下兩種語法方案之一編寫
SELECT ( var_or_expr ); WHEN ( val_1 ) unit_1; .... OTHERWISE unit_other; END;SELECT ; WHEN ( expr_1 ) unit_1; .... OTHERWISE unit_other; END;
方案型別 1 的 select 組具有以下形式
SELECT ( var_or_expr );
WHEN ( val_1 ) unit_1;
....
WHEN ( val_n ) unit_n;
OTHERWISE unit_other;
END;
var_or_expr 和 val_I(I = 1..n)可以是變數或表示式。
unit_I(I = 1..n)和unit_other可以是單個語句或 do 組。
select 組的評估方式為(虛擬碼)
Evaluate var_or_expr and store it to the variable TEMP. For I = 1 to n: Evaluate val_I, if the result equals TEMP: Execute unit_I and leave the select-group. Execute unit_other.
OTHERWISE 子句(可以縮寫為OTHER)是可選的,但如果省略此子句並且沒有任何val_I等於TEMP,則會發生執行時錯誤。
WHEN 子句可以包含多個值,例如
SELECT ( my_char );
WHEN ( '0' , '2' , '4' , '6' , '8' )
put skip list ( 'character is an even digit' );
WHEN ( '1' , '3' , '5' , '7' , '9' )
put skip list ( 'character is an odd digit' );
OTHERWISE
put skip list ( 'character is no decimal digit' );
END;
方案型別 2 的 select 組具有以下形式
SELECT;
WHEN ( expr_1 ) unit_1;
....
WHEN ( expr_n ) unit_n;
OTHERWISE unit_other;
END;
expr_I(I = 1..n)可以是邏輯表示式或邏輯變數。
unit_I(I = 1..n)和 unit_other 可以是單個語句或 do 組。
select 組的評估方式為(虛擬碼)
For I = 1 to n: if expr_I is true: Execute unit_I and leave the select-group. Execute unit_other.
一個示例
SELECT;
WHEN ( a > b ) DO; call use_as_max ( a ); put skip list ( 'max = a' ); END;
WHEN ( b > a ) DO; call use_as_max ( b ); put skip list ( 'max = b' ); END;
OTHERWISE ; /* clause with empty statement to avoid run time error if a = b */
END;
迴圈語句的基本形式為
do k = 1 to 10;
>>body<<
end;
迴圈體執行十次,控制變數k取值從 1 到 10。迴圈退出時,k的值會增加 1,即 11。迴圈擴充套件到下一個END語句。迴圈語句的變體有
do k = 1 to n by 2;
do k = 20 to 1 by -1;
可以使用多個迭代規範
do k = 1 to 10, 20 to 100 by 5, 120 to 200 by 20;
其中迴圈將執行,控制變數k取值從 1 到 10,然後 20、25、30、35、... 100、120、140、160、180 和 200。
有條件終止的迴圈使用WHILE結構
do while (a > b);
此類迴圈執行零次或多次,測試在迴圈開始時執行。
do until (a > b);
上述語句指定的迴圈至少執行一次,測試在迴圈結束時執行。
在以下示例中,使用REPEAT指定控制變數值的異常變化。
do k = 1 repeat k*i until (k > 1000);
在這種情況下,控制變數k取值 1、2、4、8、16,依此類推,直到k大於 1000。
REPEAT形式
do next = head repeat next > link until (link = null);
可用於遍歷連結列表。
WHILE和UNTIL可以組合使用
do while ( W ) until ( U ); ... end;
do until ( U ) while ( W ); ... end;
(兩個示例是等效的,即語句中WHILE和UNTIL的順序無關緊要)
DO I = ...後面可以跟WHILE或REPEAT或兩者
do I = 1 to 5 while ( W ); ... end;
do I = 1 to 5 until ( U ); ... end;
do I = 1 to 5 while ( W ) until ( U ); ... end;
do I = 1 to 5 until ( U ) while ( W ); ... end;
(最後兩個示例是等效的)
評估順序
do I = 3 to 9 by 2 while ( W ) until ( U );
... some_statements ...
end;
put skip list ( 'I am ready' );
評估方式為
令 I 為 3如果 I > 9 則轉到 [7]如果 W 為假則轉到 [7]...一些語句...如果 U 為真則轉到 [7]令 I 為 I + 2 並轉到 [2]put skip list ( 'I am ready' );
另一個示例:以下語句指定 do 組必須執行八次,I 的值分別為 1、3、5、9、18、36、9、18。
do I = 1 to 5 by 2, /* 1 3 5 */
9 repeat 2 * I until ( I > 20 ), /* 9 18 36 */
9 repeat 2 * I while ( I < 21 ); /* 9 18 */
在 PL/I 中,bit (1) 字串表示單個布林值
'1'B解釋為True'0'B解釋為False
Bit (1) 字串可以在條件和迴圈語句中用作布林表示式。
declare my_bit bit (1);
my_bit = ( 1 < 2 ); /* now my_bit has the value '1'B */
my_bit = ( 2 < 1 ); /* now my_bit has the value '0'B */
.....
if my_bit then
put skip list ( 'value of my_bit is true' );
else
put skip list ( 'value of my_bit is false' );
do while ( my_bit );
.....
end;
布林運算子可用於計算新值
- 字首運算子
¬用作邏輯NOT。 - 中綴運算子
&用作邏輯AND。 - 中綴運算子
|用作邏輯OR。
declare bit_a bit (1);
declare bit_b bit (1);
declare result bit (1);
result = ¬ bit_a; /* result = '1'B if and only if bit_a is '0'B */
result = bit_a & bit_b; /* result = '1'B if and only if both bit_a and bit_b are '1'B */
result = bit_a | bit_b; /* result = '0'B if and only if both bit_a and bit_b are '0'B */
注意:使用編譯時選項,NOT運算子和OR運算子可能會被替換為其他符號,為了與現有的 PL/I 程式相容,通常^用作NOT,!用作OR。
PL/I 提供錯誤檢測和錯誤處理。檢測到的錯誤包括
- 浮點溢位和下溢;
- 定點溢位;
- 除以零;
- 下標越界錯誤;
- 子字串違規;
- 字串截斷。
- 一般錯誤。
使用者可以允許系統報告錯誤並執行預設操作,或者使用者可以指定在檢測到給定錯誤時要執行的計算操作。
事件(包括錯誤)稱為條件。
要檢測條件,必須首先啟用它。如果啟用了條件並且發生了相應的條件,則會執行相應的ON單元(如果使用者提供)。
考慮以下程式碼
on overflow snap go to restart;
a = b * c;
...
restart:
如果產品b*c超過允許的最大值,則會引發OVERFLOW條件,並執行OVERFLOW的ON單元。此處的ON單元是最簡單的(go to)之一,僅將控制權轉移到標記為RESTART的語句(在該語句中,程式可能處理下一組值)。
關鍵字SNAP導致正在執行的程式列印錯誤訊息,以及發生錯誤的語句編號。
以下是一個更一般的示例
(SUBSCRIPTRANGE):
a: procedure;
declare x(10) float;
declare (i, k) fixed binary;
ON SUBSCRIPTRANGE SNAP BEGIN; PUT DATA (K); STOP; END;
get list (k);
do i = 1 to k;
x(i) = 0;
end;
end a;
在本例中,錯誤檢測和處理部分以大寫顯示。下標檢查由條件字首SUBSCRIPTRANGE啟用(在PROCEDURE語句之前用括號括起來)。在執行任何語句之前,必須執行ON語句,該語句建立發生下標錯誤時要執行的操作。接下來執行的語句是輸入語句GET,它讀取一個值,例如12。進入迴圈,並初始化x的十個值。在嘗試初始化x(11)時,會引發SUBSCRIPTRANGE條件,並導致執行ON單元BEGIN; PUT DATA (K); STOP; END;。SNAP導致列印錯誤訊息以及語句號,然後執行BEGIN塊中的語句,即PUT DATA語句,它列印K = 11。然後程式STOP。一些特定的條件是
| OVERFLOW | 指數溢位 |
| UNDERFLOW | 指數下溢 |
| FIXEDOVERFLOW | 定點溢位 |
| SIZE | 定點溢位 |
| ZERODIVIDE | 被零除 |
| SUBSCRIPTRANGE | 下標越界錯誤 |
| STRINGRANGE | 子字串錯誤 |
| STRINGSIZE | 字串截斷 |
| ENDFILE | 檔案結束 |
| ENDPAGE | 頁面結束 |
| ATTENTION | 鍵盤請求 |
提供了一些條件來處理日常事件,例如檔案結束和頁面結束。例如,在每一頁的頂部列印頁碼
ON ENDPAGE PUT PAGE LIST('Page', Page_no);
檢測輸入檔案IN的結束,並轉移到命名語句
ON ENDFILE(IN) go to next;
程式設計師可以發明自己的條件,並可以發出訊號。
提供SIGNAL語句用於在程式測試期間測試給定的ON單元(錯誤處理程式)。例如,SIGNAL ZERODIVIDE;要引發名為RANGE的使用者定義條件,將使用語句SIGNAL CONDITION(RANGE);。
<列出此語言本機提供的容器或容器列表的引用。如果容器不是該語言的本機功能,請列出整合容器的方法。>
任何垃圾回收都是自動的。所有具有automatic屬性或使用automatic(auto)builtin分配的變數都會在宣告它們的塊結束時從儲存器中刪除。所有分配的儲存器(ALLOC語句或alloc builtin)都會在程式結束時從儲存器中刪除。
標準函式庫都是自動提供的;無需執行任何操作即可訪問它們,例如指定目錄或使用INCLUDE語句等。
- 源程式可以用大寫或小寫編寫。除了字串之外,大小寫之間沒有區別。
- 可變字串(用屬性
VARYING宣告)類似於C的char。
對於完全等效的版本,在Enterprise PL/I for z/OS中,可以使用屬性VARYINGZ,其中字串以零結尾。
如果沒有屬性VARYING,則字串為固定長度。此類字串始終儲存指定數量的字元。因此,
DECLARE S CHARACTER(8);
S = 'abc';
儲存八個字元,即abc後跟五個空格,而
declare V character(8) varying;
V = 'abc';
需要10位元組的儲存空間:2位元組用於V的實際長度,8位元組用於字元。後一種功能使得賦值成為可能
V = V || 'dog';
它將單詞dog追加到已儲存在V中的內容(在本例中最多8個字元,當然)。
Content of V after V = 'abc';:
+---+---+---+---+---+---+---+---+---+---+
| 3 | a | b | c | ? | ? | ? | ? | ? | /* "?" means: value is undefined */
+---+---+---+---+---+---+---+---+---+---+
1 2 3 4 5 6 7 8 /* index of character */
Content of V after V = V || 'dog';:
+---+---+---+---+---+---+---+---+---+---+
| 6 | a | b | c | d | o | g | ? | ? | /* "?" means: value is undefined */
+---+---+---+---+---+---+---+---+---+---+
1 2 3 4 5 6 7 8 /* index of character */
declare i fixed binary;
等效於C的int;
declare i fixed binary (31);
等效於C的long int。
declare i fixed binary (63);
等效於C的long long int。
- 十進位制算術,因為它固定點,需要小心。
提供內建函式ADD、SUBTRACT、MULTIPLY和DIVIDE,使程式設計師能夠指定每個結果的精度。因此,它們有助於避免溢位。對於十進位制定點除法,強烈建議使用DIVIDE函式。因此,要將A除以B,其中A的精度為(10,5),B的精度為(6),推薦的形式為
declare A fixed decimal (10,5), B fixed decimal(6), C fixed decimal (10,5);
A = 12345.67891; b = 98765;
C = divide(A, B, 10, 5);
在DIVIDE引用中,引數10和5分別指示結果A/B具有總共十位數字,小數點後有5位數字。然後將此值儲存在C中。IBM PL/I允許最多31位十進位制數字用於定點運算。
- 在執行PL/I程式時,在初始過程語句上使用條件字首
SIZE、STRINGRANGE、STRINGSIZE和SUBSCRIPTRANGE,如下所示
(SIZE, STRINGRANGE, STRINGSIZE, SUBSCRIPTRANGE):
trial: procedure options (main);
這使得可以檢查定點溢位,以及字串和下標的範圍檢查和截斷。
- 陣列的下界預設為1,而不是0。
- 整數(無論是十進位制、二進位制還是混合)的除法可能會產生具有比例因子的定點結果。因此,9/2產生4.5000000,這與產生整數4的Fortran不同。如果需要整數結果,可以使用
TRUNC或DIVIDE,例如:TRUNC(9/2)或更一般地 -DIVIDE(J, K, 31),後者為二進位制整數結果提供31位的精度。或者,將二進位制整數變數宣告為具有最大精度(通常為31位)將確保兩個此類整數變數相除的結果將產生整數結果。
- J. K. Hughes,PL/I結構化程式設計,第3版,Wiley,1986年。(初學者到專業人士;商業應用)
- R. Reddy和C. Ziegler,PL/I:結構化程式設計和問題解決,West,1986年,ISBN 0-314-93915-6。(初學者到高階)
- R. A. Barnes,程式設計師的PL/I,North-Holland,1979年。(專業)
- G. F. Groner,PL/I在技術應用中的程式設計,按需印刷書籍,密歇根州安阿伯,1971年。(專業)
- M. E. Anderson,程式設計師的PL/I,Prentice-Hall,1973年。(專業)
- D. R. Stoutemyer,工程與科學的PL/I程式設計,Prentice-Hall,1971年。(專業)
- E. Sturm,Das neue PL/I(für PC、Workstations和Mainframe),第5版,Vieweg-Verlag,2002年。