0

做一个摊销时间表。我在 300-REPORT 模块中设置了显示付款信息的行,然后在显示后更新该信息,就像进行了另一次付款一样。它应该在余额为 0 美元时停止,然后将其全部写入文本文件。

问题是,它显示了该行,而不是进行新的数学运算,它只是一遍又一遍地显示同一行,因此创建了一个无限循环。

300-REPORT.
      MOVE WS-BEGYEAR      TO WS-REP-YEAR
      MOVE WS-BEGMONTH     TO WS-REP-MO
      MOVE WS-PRINCIPAL    TO WS-REP-PRIN
      MOVE WS-INTEREST     TO WS-REP-INT                         
      MOVE WS-TERM         TO WS-REP-TERM

      COMPUTE WS-REP-BEG-BAL-M = WS-PRINCIPAL * (1+WS-INT-DEC)
      MOVE WS-REP-BEG-BAL-M   TO WS-REP-BEG-BAL
      MOVE WS-PRINCIPAL    TO WS-REP-BEG-BAL-M

      MOVE WS-PAYMENT-TOT  TO WS-REP-PAYMENT

      COMPUTE WS-INT-PAID-M = WS-PRINCIPAL * WS-INT-DEC
      MOVE WS-INT-PAID-M   TO WS-INT-PAID

      COMPUTE WS-CUR-PRIN-M                                                                                               
          = WS-PRINCIPAL - (WS-PAYMENT-TOT - WS-INT-PAID-M)                  
      MOVE WS-CUR-PRIN-M   TO WS-CUR-PRIN

      COMPUTE WS-END-BAL-M = WS-REP-BEG-BAL-M - WS-PAYMENT-TOT
      MOVE WS-END-BAL-M   TO WS-END-BAL

      WRITE OF-LINE       FROM WS-TITLE3
      WRITE OF-LINE       FROM WS-TITLE4
      WRITE OF-LINE       FROM WS-LINE
      WRITE OF-LINE       FROM WS-PRIN-LINE
      WRITE OF-LINE       FROM WS-INT-LINE
      WRITE OF-LINE       FROM WS-TERM-LINE
      WRITE OF-LINE       FROM WS-LINE
      WRITE OF-LINE       FROM WS-HEADERS
      WRITE OF-LINE       FROM WS-HEADER-SEP.

      PERFORM UNTIL WS-END-BAL-M IS <= 0

          WRITE OF-LINE        FROM WS-REP-DATA-LINE
          DISPLAY WS-REP-DATA-LINE
          ADD 1 TO WS-PMT-NUM
          ADD 1 TO WS-REP-MO
          IF WS-REP-MO = 13
             ADD 1   TO WS-REP-YEAR         
             MOVE 01 TO WS-REP-MO                          
          END-IF
          MOVE WS-END-BAL TO WS-REP-BEG-BAL

          COMPUTE WS-INT-PAID-M = WS-REP-BEG-BAL-M * WS-INT-DEC
          MOVE WS-INT-PAID-M  TO WS-INT-PAID

          COMPUTE WS-CUR-PRIN-M = 
          WS-REP-BEG-BAL-M - (WS-PAYMENT-TOT - WS-INT-PAID-M)
          MOVE WS-CUR-PRIN-M  TO WS-CUR-PRIN

          COMPUTE WS-END-BAL-M = WS-REP-BEG-BAL-M - WS-PAYMENT-TOT
          MOVE WS-END-BAL-M   TO WS-END-BAL

      END-PERFORM        
4

2 回答 2

1

你的问题(也许不是唯一的)在这里:

COMPUTE WS-END-BAL-M = WS-REP-BEG-BAL-M - WS-PAYMENT-TOT

WS-REP-BEG-BAL-M 和 WS-PAYMENT-TOT 在循环内都没有改变,所以答案总是一样的,循环永远不会终止。

您可以通过更好地命名事物、注意事物的编码方式以及使用更自然地代表您正在做的事情的事物来使事情变得更容易。例如:

  COMPUTE WS-REP-BEG-BAL-M = WS-PRINCIPAL * (1+WS-INT-DEC)
  MOVE WS-REP-BEG-BAL-M   TO WS-REP-BEG-BAL
  MOVE WS-PRINCIPAL    TO WS-REP-BEG-BAL-M

不如

  COMPUTE WS-REP-BEG-BAL = WS-PRINCIPAL * ( 1 + WS-INT-DEC)
  MOVE WS-PRINCIPAL    TO WS-REP-BEG-BAL-M

这是非常相似的名称,没有读者完全了解什么是什么:

WS-END-BAL-M 
WS-REP-BEG-BAL-M 
WS-REP-BEG-BAL
WS-END-BAL

特别是当你做这样的事情时:

MOVE WS-END-BAL TO WS-REP-BEG-BAL

还:

SUBTRACT this-monthly-amount FROM outstanding-amount

比它的目的更容易理解:

COMPUTE this-monthly-amount = this-monthly-amount - outstanding-amount

尤其是当分散在COMPUTE做其他事情的时候。

结合以上所有内容,您会得到一段很难一眼看懂的代码。

“重现的最短代码”有两个意图:首先,您可能会在执行过程中自己发现问题;其次,它有助于其他任何关注问题的人。

你有一个大循环,所以重要的是控制循环的条件。删除所有内容,除了影响起始值的内容以及修改值的方式。重要的是还包括数据定义。有时你会有一些需要签名的东西,但不是。

WS-PAYMENT-TOT 不是 300-REPORT 中的目标字段。它的价值在别处确定。正如@Julien Mousset 在评论中所指出的那样,如果它永远为零,并且这就是影响循环中减量的所有因素,那么您将拥有另一个 Big Fat Loop。所以我们需要查看定义,以及 WS-PAYMENT-TOT 的设置位置,以及 300-REPORT 的 PERFORM 是否以它为非零为条件。

与 WS-REP-BEG-BAL-M 的来源 WS-PRINCIPAL 类似。

现在取出与循环控制无关的所有内容。

300-REPORT.

      MOVE WS-PRINCIPAL    TO WS-REP-BEG-BAL-M

      COMPUTE WS-END-BAL-M = WS-REP-BEG-BAL-M - WS-PAYMENT-TOT

      PERFORM UNTIL WS-END-BAL-M IS <= 0

          DISPLAY "Here we are in BFL"

          COMPUTE WS-END-BAL-M = WS-REP-BEG-BAL-M - WS-PAYMENT-TOT

      END-PERFORM   

我们甚至可以将其标准化以使用 WS-PRINCIPAL 代替 WS-REP-BEG-BAL-M。

300-REPORT.

      COMPUTE WS-END-BAL-M = WS-PRINCIPAL - WS-PAYMENT-TOT

      PERFORM UNTIL WS-END-BAL-M IS <= 0

          DISPLAY "Here we are in BFL"

          COMPUTE WS-END-BAL-M = WS-PRINCIPAL - WS-PAYMENT-TOT

      END-PERFORM   

在生成“重现的最短代码”时,您可以看到循环内的计算是循环初始值的计算。如果 WS-PRINCIPAL 为零,则永远不会进入循环。如果 WS-PRINCIPAL = WS-PAYMENT-TOT,则永远不会进入循环。对于所有其他情况,循环将 BFL。

你也有你的结构倒退。

设置“下次”不是一个好主意。这意味着您正在做不必要的工作,使读者感到困惑,并且难以维护程序,因为不清楚何时可以安全地更改字段的处理。

先写出所有的行。

在您的循环中,为后续的详细信息行(如果有)完成所有工作,并将其写为循环中的最后一件事。

您没有“分页”逻辑。如果您的详细信息行数超过页面上的行数,即使每次执行程序只打印一个“whatever”,它也会看起来很难看。

像这样的东西:

      ADD 1 TO WS-REP-MO
      IF WS-REP-MO = 13
         ADD 1   TO WS-REP-YEAR         
         MOVE 01 TO WS-REP-MO                          
      END-IF

更好的是:

      IF WS-REP-MO = 12
         ADD 1   TO WS-REP-YEAR         
         MOVE 1  TO WS-REP-MO                          
      ELSE
         ADD 1   TO WS-REP-MO
      END-IF

现在 WS-REP-MO 永远不会在逻辑上变得无效。

更好的是,在 WS-REP-MO 上获得 88 分:

      IF WS-REP-PREV-MONTH-WAS-DECEMBER
          ADD 1   TO WS-REP-YEAR         
          MOVE 1  TO WS-REP-MO                          
      ELSE
          ADD 1   TO WS-REP-MO
      END-IF

现在在更明显的是你在做什么,为什么。

如果您有相同的代码,请将其放在一个段落中(如果您正在使用这些代码,则将其放在一个段落中)并执行它。当代码需要更改时,您只有一个地方可以更改它。给段落起个好名字,你就可以开始“阅读”程序了。

您可能是 COBOL 的初学者。如果你只是在进行过程中“修补”它,你最终会得到一个难以遵循、难以维护的可怕程序。

不要害怕重新开始。如果可能的话,从一个好的工作程序开始,它会生成一份报告并在其中包含分页。然后放入您的高级逻辑,一旦证明,继续向下。

我们过去常常用铅笔和纸设计程序(通常在旧程序列表的背面),我们用铅笔、纸和大脑贯穿整个设计。然后将设计转移到一个“骨架”程序,该程序执行我们想要的基本功能。添加细节,从高到低。在每个阶段,我们都会“进行桌面检查”,这意味着您再次查看代码、铅笔、纸和大脑。

然后,您使用编译器来发现拼写错误。修复那些。获得一个干净的编译,你已经做了很多工作来让你的程序工作。

这样做你会错过那些“哦老鼠!” 编写代码后发现必须进行重大更改的时刻。

凭借经验,您可以在脑海中完成所有这些过程。

这些天你坐在电脑前。我仍然推荐“纸和铅笔”路线,即使您使用 PC 来实现它。

如果您只是坐下来编写一个 COBOL 程序,然后在测试失败时对其进行修补,那么结果对任何人都没有好处。

我现在看到您最初发布了整个程序。

您在 100- 内执行 100-。这并不好,即使您避免获得另一个 BFL(取决于编译器)。

您正在从屏幕上获取数据,由人工输入。您必须验证这一点。

使用自由格式布局并不排除您通过使用缩进来帮助自己和其他任何人。

测试时,您必须非常努力地破坏程序。否则你的用户会第一次打破它。

于 2014-11-03T07:46:52.990 回答
0

您没有显示调用 300-REPORT 的控制逻辑,但根据您的操作方式,您的杂散期可能会导致一些意外行为。你可能想把它拿出来:

WRITE OF-LINE       FROM WS-HEADER-SEP.

不能保证它会引起问题,但它可以。通常,在 Cobol-85+ 代码中,您只需要标签后和段落末尾的句点。

于 2014-11-03T15:36:10.623 回答