我在弄清楚这背后的逻辑时遇到了一些麻烦。
我需要显示一份报告,计算每月的余额,利息和本金,直到余额为零。
例如,如果输入为months = 12,balance = 25000,rate = 4.5%,则输出应如下所示:
months balance interest principal
1 $25000.00 $93.75 $2,040.71
2 $22,959.29 $86.10 $2,048.36
.......
12 $2,126.53 $7.97 $2,126.49
我不确定在DISPLAY col-hdr
之后和STOP RUN
之前写什么。有任何想法吗?
IDENTIFICATION DIVISION.
PROGRAM-ID. practice.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 LOANFMT PIC $$$$,$$$,$$$.$$.
01 LOANAMT PIC S9(9)V9(2) VALUE 0.
01 INTRATE PIC S9V9(2) VALUE 0.
01 INTFMT PIC 9.999.
01 NUMMONTHS PIC S9(3) VALUE 0.
01 MONFMT PIC ZZ9.
01 MONCNT PIC S999 VALUE 1.
01 PMT PIC S9(9)V9(2) VALUE 0.
01 PMTFMT PIC $$$$,$$$,$$$.$9.
01 TOTPMT PIC S9(9)V9(2) VALUE 0.
01 TOTFMT PIC $$$$,$$$,$$$.$9.
01 col-hdr.
05 pic x(15) value "Month".
05 pic x(15) value "Balance".
05 pic x(15) value "Interest".
05 pic x(15) value "Principal".
01 Detail-Line.
05 Pic X(2) Value Spaces.
05 DL-MONTH Pic X(999) VALUE 1.
05 Pic X(5) Value Spaces.
05 DL-BALANCE Pic $$$$,$$$,$$$.$9.
05 Pic X(4) Value Spaces.
05 DL-INTEREST Pic $$$$,$$$,$$$.$9.
05 Pic X(4) Value Spaces.
05 DL-PRINCIPAL Pic $$$$,$$$,$$$.$9.
PROCEDURE DIVISION.
000-MAIN SECTION.
DISPLAY "Enter Loan Amount: " WITH NO ADVANCING
ACCEPT LOANAMT
IF 0 > LOANAMT
PERFORM UNTIL LOANAMT > 0
DISPLAY "Loan Amount must be positive"
DISPLAY "Enter Loan Amount: " WITH NO ADVANCING
ACCEPT LOANAMT
end-PERFORM
END-IF
DISPLAY "Enter Annual Interest Rate: " WITH NO ADVANCING
ACCEPT INTRATE
IF 0 > INTRATE
PERFORM UNTIL INTRATE > 0
DISPLAY "Annual Interest Rate must be positive"
DISPLAY "Enter Annual Interest Rate: " WITH
NO ADVANCING
ACCEPT INTRATE
end-PERFORM
END-IF
DISPLAY "Enter Number of Months: " WITH NO ADVANCING
ACCEPT NUMMONTHS
IF 0 > NUMMONTHS
PERFORM UNTIL NUMMONTHS > 0
DISPLAY "Number of Months must be positive"
DISPLAY "Enter Number of Months: " WITH NO
ADVANCING
ACCEPT NUMMONTHS
end-PERFORM
END-IF
DISPLAY SPACE
move LOANAMT TO LOANFMT
move INTRATE TO INTFMT
MOVE NUMMONTHS TO MONFMT
MOVE PMT TO PMTFMT
MOVE TOTPMT TO TOTFMT
DISPLAY col-hdr
100-init.
DL-BALANCE = LOANAMT
DL-INTEREST = LOAN * (INTRATE/NUMMONTHS)
DL-PRINCIPAL = LOANAMT - DL-INTEREST
DISPLAY DETAIL-LINE
PERFORM 200-ADDMONTH UNTIL NUMMONTHS = DL-MONTH
200-ADDMONTH.
ADD 1 TO DL-MONTH
DL-BALANCE = DL-BALANCE - DL-PRINCIPAL
DL-INTEREST = LOAN * (INTRATE/NUMMONTHS)
DL-PRINCIPAL = LOANAMT - DL-INTEREST
DISPLAY DETAIL-LINE.
STOP RUN.
答案 0 :(得分:6)
months balance interest principal
1 $25000.00 $93.75 $2,040.71
2 $22,959.29 $86.10 $2,048.36
.......
12 $2,126.53 $7.97 $2,126.49
首先,整理出来。
months balance interest principal
01 $25,000.00 $93.75 $2,040.71
02 $22,959.29 $86.10 $2,048.36
.......
12 $2,126.53 $7.97 $2,126.49
看起来更专业,更容易制作。我不喜欢“月”标题,因为它不清楚它是什么意思。一些资本化也会很好,但这取决于你。您也可以整理实际间距。根据我的经验,校长将始终在利息之前,并且在此之前是付款的数字。用户将希望看到付款,而不是必须解决,并且想要确认付款的分割,并在视觉上验证利息金额。
然而,也许它是区域性的。
正如Brian在评论中指出的那样,你已经让你肘击9
键,同时在细节线中定义月份。将其设为PIC 99
或PIC Z9
。
您正在将程序编写为“堕落”结构。也许这就是你习惯使用其他语言的东西。你会看到的主要是COBOL程序会有不同的结构。
这是你的代码重新安排,同时注意缩进,这对人类读者很重要。我认为间距很有用,但不像缩进那样强制:
PROCEDURE DIVISION.
PERFORM GET-AND-VALIDATE-USER-INPUT
PERFORM PROCESS-USER-INPUT
PERFORM PRODUCE-REPORT
GOBACK
.
GET-AND-VALIDATE-USER-INPUT.
PERFORM GET-AND-VALIDATE-LOAN-AMT
PERFORM GET-AND-VALIDATE-INT-RATE
PERFORM GET-AND-VALIDATE-MONTHS
.
GET-AND-VALIDATE-LOAN-AMT.
DISPLAY "Enter Loan Amount: " WITH NO ADVANCING
ACCEPT LOANAMT
IF 0 > LOANAMT
PERFORM UNTIL LOANAMT > 0
DISPLAY "Loan Amount must be positive"
DISPLAY "Enter Loan Amount: "
WITH NO ADVANCING
ACCEPT LOANAMT
end-PERFORM
END-IF
.
GET-AND-VALIDATE-INT-RATE.
DISPLAY "Enter Annual Interest Rate: " WITH NO ADVANCING
ACCEPT INTRATE
IF 0 > INTRATE
PERFORM UNTIL INTRATE > 0
DISPLAY "Annual Interest Rate must be positive"
DISPLAY "Enter Annual Interest Rate: "
WITH NO ADVANCING
ACCEPT INTRATE
end-PERFORM
END-IF
.
GET-AND-VALIDATE-MONTHS.
DISPLAY "Enter Number of Months: " WITH NO ADVANCING
ACCEPT NUMMONTHS
IF 0 > NUMMONTHS
PERFORM UNTIL NUMMONTHS > 0
DISPLAY "Number of Months must be positive"
DISPLAY "Enter Number of Months: "
WITH NO ADVANCING
ACCEPT NUMMONTHS
end-PERFORM
END-IF
.
PROCESS-USER-INPUT.
PERFORM GET-AND-VALIDATE-MONTHS
move LOANAMT TO LOANFMT
move INTRATE TO INTFMT
MOVE NUMMONTHS TO MONFMT
MOVE PMT TO PMTFMT
MOVE TOTPMT TO TOTFMT
.
PRODUCE-REPORT.
DISPLAY SPACE [don't know what you want that for]
DISPLAY col-hdr
PERFORM FORMAT-INITIAL-LINE
PERFORM OUTPUT-DETAIL-LINE
PERFORM FORMAT-MONTHS-TO-END
.
FORMAT-INITIAL-LINE.
DL-BALANCE = LOANAMT
DL-INTEREST = LOAN
* ( INTRATE
/ NUMMONTHS )
DL-PRINCIPAL = LOANAMT
- DL-INTEREST
.
OUTPUT-DETAIL-LINE.
DISPLAY DETAIL-LINE
.
FORMAT-MONTHS-TO-END.
PERFORM NUMMONTHS = DL-MONTH
ADD 1 TO DL-MONTH
DL-BALANCE = DL-BALANCE
- DL-PRINCIPAL
DL-INTEREST = LOAN
* ( INTRATE
/ NUMMONTHS )
DL-PRINCIPAL = LOANAMT
- DL-INTEREST
PERFORM OUTPUT-DETAIL-LINE
END-PERFORM
.
你有作业。 COBOL没有。 COBOL有COMPUTE
,因此您需要使用它,尽管MOVE
,ADD
,SUBTRACT
,DIVIDE
和MULTIPLY
可以澄清为好:
FORMAT-INITIAL-LINE.
MOVE LOANAMT TO DL-BALANCE
COMPUTE DL-INTEREST = LOAN
* ( INTRATE
/ NUMMONTHS )
SUBTRACT DL-INTEREST FROM LOANAMT
GIVING DL-PRINCIPAL
.
请注意GIVING
。从B中减去B将改变B的值。如果你把GIVING C放在最后,B将不再被改变,而是将结果放在C. ADD A TO B改变B. ADD AB GIVING C没有(请注意,这次不需要TO
,虽然从语法上讲它可以在那里)。确保您了解ADD,SUBTRACT,MULTIPLY和DIVIDE可以做什么。
可能仅使用COMPUTE。与神话不同,在这方面没有性能损失,但是额外的人类读者信息会丢失。
使用现代COBOL编译器,没有必要启动具有任意过程名称(SECTION
或段落)的程序。根本没有任何意义。所以放弃这个(除非由导师/网站标准决定):
000-MAIN SECTION.
你有这样的事情:
IF 0 > LOANAMT
和
PERFORM UNTIL LOANAMT > 0
我理解cshneid在评论中提出的观点,但是存在一致性,并且事实上COBOL没有赋值语句。条件构造中的表达式永远不会导致对表达式中涉及的任何字段进行更改。
IF LOANAMT > 0
或者:
IF LOANAMT GREATER THAN 0
神秘的普通COBOL程序员可以毫不停顿地阅读。
IF 0 < LOANAMT
更多的是不连续性。读者必须停下来思考这意味着什么。这样做没有任何好处,并且有不利之处。
DISPLAY
和ACCEPT
是COBOL动词,它们与COBOL标准(从编译器到编译器)的变化最大。对于COBOL 85标准,ACCEPT和DISPLAY非常简单。您正在使用带有“Extended”ACCEPT和DISPLAY的编译器。这可能(可能确实)允许输入负数,并可能阻止输入非数字数据,但您需要检查编译器的文档。输入的数据必须是数字。获取数字中的字符比偶然输入负值更容易。
从原始代码:
100-init.
DL-BALANCE = LOANAMT
DL-INTEREST = LOAN * (INTRATE/NUMMONTHS)
DL-PRINCIPAL = LOANAMT - DL-INTEREST
DISPLAY DETAIL-LINE
PERFORM 200-ADDMONTH UNTIL NUMMONTHS = DL-MONTH
200-ADDMONTH.
ADD 1 TO DL-MONTH
DL-BALANCE = DL-BALANCE - DL-PRINCIPAL
DL-INTEREST = LOAN * (INTRATE/NUMMONTHS)
DL-PRINCIPAL = LOANAMT - DL-INTEREST
DISPLAY DETAIL-LINE.
STOP RUN.
这里,由于100-init不是PERFORM
,程序控制将进入200-ADDMONTH。标签(段落或SECTION)只是标签。他们可以成为PERFORM,GO TO的目标,或者他们可以“堕落”或“落入”。它们与您可能知道的其他语言中的“子程序”或“函数”定义不同。
因此,100-init将执行200-ADDMONTH直到完成,然后它将再次落入200-ADDMONTH。 从不故意编码。每个段落/部分应该是独立的,而不是依赖于其内容的物理位置。
如果执行了100-init,你就可以了。有点。因为您在200-ADDMONTH中有STOP RUN
。第一次执行200-ADDMONTH时,程序将停止执行。不是你想要的。
我没有考虑你实际计算的逻辑,只考虑它的方法。您有重复的代码,因此可以使用另一个PERFORMed段落/部分。
在执行时,请注意段落与SECTION之间的区别。 SECTION可以(这些天不必)包含段落。当执行SECTION时,控制将在下一个SECTION之前返回到完成的PERFORM。当一个段落被执行时,控制在下一个段落之前返回。段落不能包含其他段落。要执行一系列段落,PERFORM将需要THRU。除非由导师/网站标准规定,否则请避免编码。同样,它依赖于代码的物理位置。这很糟糕。
现在,对于SECTION而言,应该没有内在的需求,并且不需要(除了diktat)PERFORM ... THRU ....