В исполнении код не будет выполнять математические операции после отображения строки. (КОБОЛ 85)

Делаем график амортизации. У меня настроено в модуле 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        

person user1828341    schedule 03.11.2014    source источник
comment
Я отказываюсь даже читать это, пока вы не сузите проблему до наименьшего фрагмента кода, необходимого для ее воспроизведения.   -  person John Saunders    schedule 03.11.2014
comment
Я предлагаю вам либо использовать отладчик, либо использовать какой-либо другой метод для отображения WS-END-BAL-M в цикле PERFORM UNTIL. Я подозреваю, что это никогда не будет <= 0. Кроме того, к вашему сведению, еще в древние времена (COBOL-74) нас учили делать отступы в коде для удобочитаемости. Наверняка вы могли бы сделать то же самое.   -  person John Saunders    schedule 03.11.2014
comment
Каково значение WS-PAYMENT-TOT???, если оно равно нулю, у вас будет бесконечный цикл. Включите несколько дисплеев, чтобы узнать, каковы различные значения.   -  person Bruce Martin    schedule 03.11.2014
comment
Является ли WS-END-BAL-M подписанным полем? Проще всего сделать то, что указал John Saunders, добавить DISPLAY для WS-END-BAL-M.   -  person cschneid    schedule 03.11.2014
comment
Я ценю всю помощь.   -  person user1828341    schedule 03.11.2014
comment
Я предполагаю, что WS-PAYMENT-TOT равен нулям.   -  person Julien Mousset    schedule 03.11.2014


Ответы (2)


Ваша проблема (возможно, не единственная) здесь:

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, если это когда-либо будет равно нулю, и это все, что когда-либо повлияет на ваше уменьшение в цикле, тогда у вас будет еще одна большая толстая петля. Итак, нам нужно увидеть определение, и где установлен WS-PAYMENT-TOT, и зависит ли ВЫПОЛНЕНИЕ 300-REPORT от того, что оно не равно нулю.

Аналогично WS-PRINCIPAL, который является источником WS-REP-BEG-BAL-M.

Теперь выньте все, что не связано с управлением петлей.

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.

У вас также есть структура назад.

Не стоит откладывать дела «на следующий раз». Это означает, что вы выполняете ненужную работу, сбиваете с толку читателя и затрудняете поддержку программы, поскольку неясно, когда можно безопасно изменить обработку полей.

Сначала выпишите все строки.

В своем цикле выполните всю работу для последующих строк деталей (если они есть) и запишите их как последнее в цикле.

У вас нет логики "разбиения на страницы". Если у вас больше, чем количество строк на странице строк с подробными сведениями, это будет выглядеть уродливо, даже если за одно выполнение программы будет напечатано только одно «что угодно».

Что-то вроде этого:

      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 никогда не становится логически недействительным.

Еще лучше, с 88 на WS-REP-MO:

      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. Если вы просто «залатаете» ее по мере продвижения, вы получите ужасную программу, которой трудно следовать и которую трудно поддерживать.

Не бойтесь начать заново. Если возможно, начните с хорошей рабочей программы, которая создает отчет и имеет в нем разбиение на страницы. Затем вставьте свою высокоуровневую логику и, как только это будет доказано, двигайтесь вниз.

Раньше мы разрабатывали программы с помощью карандаша и бумаги (обычно на обратной стороне листинга старой программы), мы прорабатываем дизайн с помощью карандаша, бумаги и мозга. Затем перенесите дизайн в «скелетную» программу, которая сделает то, что мы хотели. Добавьте детали, от высокого к низкому. На каждом этапе мы проводим «кабинетную проверку», что означает, что вы снова просматриваете код, карандаш, бумагу и мозги.

Затем вы используете компилятор, чтобы обнаружить опечатки. Исправьте те. Получите чистую компиляцию, и вы уже сделали много вещей, чтобы ваша программа заработала.

Делая это, вы пропустите эти «о, крысы!» моменты, когда вы обнаруживаете после написания кода, что вам нужно внести серьезные изменения.

С опытом вы сможете проделывать весь этот процесс в своей голове.

В эти дни вы сидите перед ПК. Я по-прежнему рекомендую путь «бумага и карандаш», даже если вы реализуете его с помощью своего ПК.

Если вы просто сядете и напишете программу на языке COBOL, а затем исправите ее по мере того, как ваше тестирование окажется неудачным, результаты ни для кого не будут хорошими.

Теперь я вижу, что вы выложили всю программу изначально.

Вы ВЫПОЛНЯЕТЕ 100- из 100-. Это нехорошо, даже если вы избежите получения другого BFL (зависит от компилятора).

Вы берете данные с экрана, набранные человеком. Вы должны убедиться в этом.

Использование макета произвольного формата не исключает возможности помочь себе и кому-либо еще с помощью отступов.

При тестировании надо очень постараться, чтобы сломать программу. В противном случае ваш пользователь сломает его в первый раз.

person Bill Woodger    schedule 03.11.2014

Вы не показываете свою управляющую логику, которая вызывает 300-REPORT, но в зависимости от того, как вы это сделали, период беспризорности может привести к неожиданному поведению. Возможно, вы захотите убрать это:

WRITE OF-LINE       FROM WS-HEADER-SEP.

Не факт, что это вызовет проблемы, но может. Как правило, точки нужны только после меток и в самом конце абзаца в коде Cobol-85+.

person Joe Zitzelberger    schedule 03.11.2014
comment
Это хорошие глаза, Джо, но на этот раз это никоим образом не вызовет проблем, несмотря на ввод 300-REPORT. Только в определенных условиях бездомные могут вызвать проблему. Никак иначе. - person Bill Woodger; 03.11.2014
comment
Не заставляйте меня начинать с двусмысленности выхода из абзаца, случайных точек и искаженных вещей, которые могут произойти, если вы используете стили выполнения через. Но да, вы правы, это не виновник, даже если 300-отчет был выходным абзацем выполнения, мне просто нравится применять теорию кода разбитых окон... - person Joe Zitzelberger; 07.11.2014
comment
Да, хорошая мысль. Невнимание к таким деталям указывает на вероятность того, что когда-нибудь они вызовут проблемы. Я не думаю, что @user1828341 в любом случае признается в чем-либо, просто тихо идет дальше. - person Bill Woodger; 07.11.2014