跳转到内容

软件工程师手册/语言词典/PLI

来自 Wikibooks,开放世界中的开放书籍

这是维基百科条目

PL/I 是一种完整的过程式语言。

执行入口点

[编辑 | 编辑源代码]

程序的入口点指定为OPTIONS(MAIN),在过程语句中,例如

 tree: procedure options (main);

列特性

[编辑 | 编辑源代码]

没有列特性。

通用语法

[编辑 | 编辑源代码]

赋值示例

a = b*(c-d);

在这个数值赋值语句中,dc 中减去,结果乘以 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]

关键字 DECLAREINITIAL 可以缩写为 DCLINIT,许多属性也可以缩写。

属性 缩写
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语句,该语句列出了从调用程序所需的参数,即bcangle。这些参数在下一条语句(DECLARE)中定义。PROCEDURE语句还说明了要返回给调用程序的结果类型,即浮点值。这由RETURNS (FLOAT)表示。RETURN语句包含用于计算三角形面积的公式。然后它将控制权返回给调用程序。RETURN语句通常包含变量的名称,但也可以包含表达式(如本例所示)。

作用域

[编辑 | 编辑源代码]

PL/I 变量的作用域限制在声明它们的程序中。作用域扩展到该程序中包含的任何程序(当然,除了任何程序中出现相同名称的新声明)。

如果未声明变量,则该变量的作用域为整个程序。同样,如果声明(对于同名变量)出现在从属(使用变量)的一个或多个块中,则作用域将从包含声明的块中排除。

PL/I 有两种类型的块。一种是用于子程序和函数的PROCEDURE块。另一种是BEGIN块(BEGIN; ... END;)。BEGIN块的行为类似于PROCEDURE块,但BEGIN块没有参数,也不必有标签。它以正常的顺序执行进入,而PROCEDURE块只能通过执行CALL语句或函数引用来执行。

条件语句:IF..THEN..ELSE

[编辑 | 编辑源代码]

考虑条件语句的两个示例

if a = b then c = d;
if a > b then c = d; else c = e;

第一个示例说明了一个可选执行的语句c = d;

第二个示例显示了两个可选执行的语句,第一个在a > b时执行,第二个在a <= b时执行;

当需要有条件地执行多个语句时,使用语句括号doend

if a > b then
   do; p = q; r = s; end;

多分支条件:SELECT

[编辑 | 编辑源代码]

select 组提供多分支评估,它可以用以下两种语法方案之一编写

  1. SELECT ( var_or_expr ); WHEN ( val_1 ) unit_1; .... OTHERWISE unit_other; END;
  2. 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_exprval_II = 1..n)可以是变量或表达式。

unit_II = 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_II = 1..n)可以是逻辑表达式或逻辑变量。
unit_II = 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);

可用于遍历链接列表。

WHILEUNTIL可以组合使用

do while ( W ) until ( U ); ... end;
do until ( U ) while ( W ); ... end;

(两个示例是等效的,即语句中WHILEUNTIL的顺序无关紧要)

DO I = ...后面可以跟WHILEREPEAT或两者

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' );

评估方式为

  1. 令 I 为 3
  2. 如果 I > 9 则转到 [7]
  3. 如果 W 为假则转到 [7]
  4. ...一些语句...
  5. 如果 U 为真则转到 [7]
  6. 令 I 为 I + 2 并转到 [2]
  7. 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条件,并执行OVERFLOWON单元。此处的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属性或使用automaticautobuiltin分配的变量都会在声明它们的块结束时从存储器中删除。所有分配的存储器(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

  • 十进制算术,因为它固定点,需要小心。

提供内置函数ADDSUBTRACTMULTIPLYDIVIDE,使程序员能够指定每个结果的精度。因此,它们有助于避免溢出。对于十进制定点除法,强烈建议使用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程序时,在初始过程语句上使用条件前缀SIZESTRINGRANGESTRINGSIZESUBSCRIPTRANGE,如下所示
(SIZE, STRINGRANGE, STRINGSIZE, SUBSCRIPTRANGE):
trial: procedure options (main);

这使得可以检查定点溢出,以及字符串和下标的范围检查和截断。

  • 数组的下界默认为1,而不是0。
  • 整数(无论是十进制、二进制还是混合)的除法可能会产生具有比例因子的定点结果。因此,9/2产生4.5000000,这与产生整数4的Fortran不同。如果需要整数结果,可以使用TRUNCDIVIDE,例如:TRUNC(9/2)或更一般地 - DIVIDE(J, K, 31),后者为二进制整数结果提供31位的精度。或者,将二进制整数变量声明为具有最大精度(通常为31位)将确保两个此类整数变量相除的结果将产生整数结果。

网络参考

[编辑 | 编辑源代码]
  • [1] IBM手册:用于MVS和VM的PL/I
  • [2] IBM手册:用于z/OS的Enterprise PL/I

书籍和文章

[编辑 | 编辑源代码]
  • 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年。

另请参阅

[编辑 | 编辑源代码]
华夏公益教科书