不同编译器使用 OCCURS 的不同结果

Different results using OCCURS with different compilers

我正在尝试使用 DISPLAY 输出以下行,并且在 Visual Studio 和 Tutorialspoint COBOL 编译器中的 Micro Focus COBOL 中得到了正确的结果,但是当 运行 它在z/OS 使用 IBM Enterprise COBOL 的大型机:

01 W05-OUTPUT-ROW.
   05 W05-OFFICE-NAME PIC X(13).
   05 W05-BENEFIT-ROW OCCURS 5 TIMES.
       10 PIC X(2) VALUE SPACES.
       10 W05-B-TOTAL PIC ZZ,ZZ9.99 VALUE ZEROS.
   05 PIC X(2) VALUE SPACES.
   05 W05-OFFICE-TOTAL PIC ZZ,ZZ9.99 VALUE ZEROS.

在 Enterprise COBOL 中似乎忽略了空格,并且正在添加一个额外的零填充列,即使两个版本中的 PERFORM VARYING 和 DISPLAY 代码完全相同:

PERFORM VARYING W02-O-IDX FROM 1 BY 1
   UNTIL W02-O-IDX > W12-OFFICE-COUNT

   MOVE W02-OFFICE-NAME(W02-O-IDX) TO W05-OFFICE-NAME

   PERFORM 310-CALC-TOTALS VARYING W02-B-IDX FROM 1 BY 1
       UNTIL W02-B-IDX > W13-BENEFIT-COUNT

   MOVE W02-O-TOTAL(W02-O-IDX) TO W05-OFFICE-TOTAL
   DISPLAY W05-OUTPUT-ROW
END-PERFORM

W13-BENEFIT-COUNT 是 5 并且在程序中永远不会改变,所以第 6 列对我来说是个谜。

正确输出:

奇怪的输出:

编辑:根据要求,这里是 W02-OFFICE-TABLE:

01 W02-OFFICE-TABLE.
    05 W02-OFFICE-ROW OCCURS 11 TIMES
    ASCENDING KEY IS W02-OFFICE-NAME
    INDEXED BY W02-O-IDX.
        10 W02-OFFICE-CODE PIC X(6).
        10 W02-OFFICE-NAME PIC X(13).
        10 W02-BENEFIT-ROW OCCURS 5 TIMES
        INDEXED BY W02-B-IDX.
            15 W02-B-CODE PIC 9(1).
            15 W02-B-TOTAL PIC 9(5)V99 VALUE ZERO.
        10 W02-O-TOTAL PIC 9(5)V99 VALUE ZERO.

W12-OFFICE-COUNT 始终为 11,永远不会改变:

01 W12-OFFICE-COUNT PIC 99 VALUE 11.

我会非常犹豫将 VALUEOCCURS 混合并将 WS 重新编码为

01 W05-OUTPUT-ROW.
   05 W05-OFFICE-NAME  PIC X(13).
   05 W05-BENEFITS     PIC X(55) VALUE SPACES.
   05 FILLER REDEFINES W05-BENEFITS.
     07 W05-BENEFIT-ROW OCCURS 5 TIMES.
       10 FILLER       PIC X(02).
       10 W05-B-TOTAL  PIC ZZ,ZZ9.99.
   05 FILLER           PIC X(02) VALUE SPACES.
   05 W05-OFFICE-TOTAL PIC ZZ,ZZ9.99 VALUE ZEROS.

可能与缺少的字段名有关?

啊!邪恶INDEXED。我会将两个 ***-IDX 变量都设为简单的 99s。

问题与其说是 "why does Enterprise COBOL do that?",不如说是因为它已被记录,而 "why do those other two compilers generate programs that do what I want?" 可能也被记录了。

这里引用了 2014 年 COBOL 标准的草案(实际标准需要花钱):

C.3.4.1 Subscripting using index-names

In order to facilitate such operations as table searching and manipulating specific items, a technique called indexing is available. To use this technique, the programmer assigns one or more index-names to an item whose data description entry contains an OCCURS clause. An index associated with an index-name acts as a subscript, and its value corresponds to an occurrence number for the item to which the index-name is associated.

The INDEXED BY phrase, by which the index-name is identified and associated with its table, is an optional part of the OCCURS clause. There is no separate entry to describe the index associated with index-name since its definition is completely hardware oriented. At runtime the contents of the index correspond to an occurrence number for that specific dimension of the table with which the index is associated; however, the manner of correspondence is determined by the implementor. The initial value of an index at runtime is undefined, and the index shall be initialized before use. The initial value of an index is assigned with the PERFORM statement with the VARYING phrase, the SEARCH statement with the ALL phrase, or the SET statement.

[...]

An index-name may be used to reference only the table to which it is associated via the INDEXED BY phrase.

从第二段可以清楚地看出,索引的实现方式取决于编译器的实现者。这意味着索引实际包含的内容以及它在内部的操作方式可能因编译器而异,只要结果相同即可。

引用的最后一段表明,根据标准,特定索引只能用于定义该特定索引的 table。

您在 310-CALC-TOTALS 中有一些与此等效的代码:使用其 table 中的索引获取源数据项,并使用 "wrong" [=78] 中的索引=] 来存储从不同的 table.

派生的值

这打破了 "An index-name may be used to reference only the table to which it is associated via the INDEXED BY phrase."

因此您将 310-CALC-TOTALS 中的代码更改为:使用其 table 中的索引获取源数据项,并使用在目标 [=78] 上定义的数据名称或索引=] 来存储从不同的 table.

派生的值

所以你的代码现在可以工作了,并且每个编译器都会给你相同的结果。

如果标准(这与以前的标准相同)禁止使用,为什么要编译企业 COBOL 代码?

IBM 有语言扩展。实际上有两个扩展适用于您的情况(引自 Enterprise COBOL Language Reference in Appendix A):

Indexing and subscripting ... Referencing a table with an index-name defined for a different table

OCCURS ... Reference to a table through indexing when no INDEXED BY phrase is specified

因此你不会得到编译错误,因为使用来自不同 table 的索引和在 table 上没有定义索引时使用索引都是可以的。

那么,当您使用另一个索引时,它有什么作用?再次来自语言参考,这次是 使用索引名称订阅(索引)

An index-name can be used to reference any table. However, the element length of the table being referenced and of the table that the index-name is associated with should match. Otherwise, the reference will not be to the same table element in each table, and you might get runtime errors.

这正是发生在你身上的事情。 OCCURS 中项目的长度差异归因于您的 PICture 中的 "insertion editing" 符号,用于您显示的 table。如果两个 table 中的项目长度相同,您就不会注意到问题。

您为 table 项目提供了一个 VALUE 子句(不必要,因为您总是在输出之前将一些东西放入其中)并且这留下了您的 "sixth" 列,前五列是写成较短的项目。请注意,当对一个长度进行编辑并使用不同的隐式长度进行存储时造成的混淆,您甚至会覆盖小数点后第二位。

IBM 的 INDEXED BY 实现意味着被索引的项目的长度是固有的。因此,当引用的字段实际长度不同时会出现意想不到的结果。

另外两个编译器呢?您需要查看他们的文档以确定发生了什么(就像由条目号表示的索引一样简单(如此简单的 1、2、3 等),以及允许索引引用另一个table 就足够了)。应该有两个扩展:允许在未定义该索引的 table 上使用索引;允许在未定义索引的 table 上使用索引。这两者在逻辑上是成对出现的,并且都只需要是特定的(第一个否则会这样做),因为它们特别违反标准。

Micro Focus 确实有一个语言扩展,其中一个 table 的索引可用于引用另一个 table 的数据。没有明确指出这包括引用未定义索引的 table,但这显然是这样。

Tutorialspoint 使用 OpenCOBOL 1.1。 OpenCOBOL 现在是 GnuCOBOL。 GnuCOBOL 1.1 是当前版本,它与 OpenCOBOL 1.1 不同且更新。 GnuCOBOL 2.0 即将推出。我在 SourceForge.Net 为 GnuCOBOL 的讨论区做出了贡献,并在那里提出了问题。 GnuCOBOL 项目的 Simon Sobisch 之前曾就过时的 OpenCOBOL 1.1 的使用与 Ideaone 和 Tuturialspoint 进行过接触。 Ideaone 提供了积极的反馈,Tutorialspoint,Simon 今天再次联系了他,但还没有。

作为附带问题,您似乎正在使用 SEARCH ALL 对 table 进行二进制搜索。对于 "small" tables,SEARCH ALL 提供的广义二进制搜索机制的开销可能超过机器资源的任何预期节省。如果您要处理大量数据,则普通 SEARCH 可能比 SEARCH ALL 更有效。

"small"有多小取决于您的数据。接近 100% 的时间,5 可能很小。

可以通过编码实现比 SEARCH 和 SEARCH ALL 功能更好的性能,但请记住 SEARCH 和 SEARCH ALL 不会出错。

但是,尤其是使用 SEARCH ALL 时,程序员很容易出错。如果数据乱序,则 SEARCH ALL 将无法正常运行。定义的数据多于填充的数据也会很快导致 table 乱序。如果对可变数量的项目使用 SEARCH ALL,请考虑对 table 或 "padding" 值超出可存在的最大键值的未使用尾随条目使用 OCCURS DEPENDING ON。