问题
I'm having some trouble figuring out the logic behind this.
I need to display a report calculating balance, interest and principal per month until the balance is zero.
As an example, if input is months=12, balance=25000, rate=4.5%, output should look like this:
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
I'm not sure what to write after DISPLAY col-hdr
and before STOP RUN
. Any ideas?
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.
回答1:
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
Firstly, get that sorted out.
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
That looks much more professional, and easy to produce. I don't like the "months" heading, because it is not clear what it means. Some capitalisation would be good as well, but those are up to you. Actual spacing you can sort out as well. In my experience, Principal would always be before Interest, and a figure of the Payment before that. The user will want to see the Payment, not have to work it out, and want to confirm the split of the payment, and visually verify the interest amount.
Maybe it's regional, however.
As Brian noted in a comment, you've had you elbow on the 9
key whilst defining the month in the detail line. Make it PIC 99
or PIC Z9
.
You are writing your program as a "fall through" structure. Perhaps that is what you are used to with other languages. Mainly COBOL programs that you would see would have a different structure.
Here is your code re-arranged, also with attention paid to indentation, which is important for the human reader. The spacing I find useful, but is not as mandatory as the indentation:
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
.
You have assignments. COBOL does not. COBOL has COMPUTE
, so you'll need to use that, although MOVE
, ADD
, SUBTRACT
, DIVIDE
and MULTIPLY
can clarify as well:
FORMAT-INITIAL-LINE.
MOVE LOANAMT TO DL-BALANCE
COMPUTE DL-INTEREST = LOAN
* ( INTRATE
/ NUMMONTHS )
SUBTRACT DL-INTEREST FROM LOANAMT
GIVING DL-PRINCIPAL
.
Note that GIVING
. SUBTRACT A FROM B will change the value of B. If you put GIVING C on the end, B will no longer be changed, instead the result will be placed in C. ADD A TO B changes B. ADD A B GIVING C does not (note this time no need for TO
, although syntactically it can be there). Ensure you understand what ADD, SUBTRACT, MULTIPLY and DIVIDE can do.
It is possible to only use COMPUTE. Unlike myth, there is no performance penalty in this, but extra human-reader information is lost.
With modern COBOL compilers it is not necessary to start a program with an arbitrary procedure name (either SECTION
or paragraph). It has no meaning, at all. So ditch this (unless dictated by tutor/site-standards):
000-MAIN SECTION.
You have things like this:
IF 0 > LOANAMT
And:
PERFORM UNTIL LOANAMT > 0
I understand the point made by cshneid made in a comment, but there is consistency, and there is the fact that COBOL has no assignment statement. An expression in a conditional construct can never cause a change to any field involved in the expression.
IF LOANAMT > 0
Or:
IF LOANAMT GREATER THAN 0
Can be read, by the mythical average COBOL programmer, without pause.
IF 0 < LOANAMT
Is more of a discontinuity. The reader has to stop and think what that means. There is no benefit in doing it that way, and there are disbenefits.
DISPLAY
and ACCEPT
are the COBOL verbs which vary the most from the COBOL standard, from compiler to compiler. To the COBOL 85 Standard, ACCEPT and DISPLAY are very plain. You are using a compiler with "Extended" ACCEPT and DISPLAY. This may (probably does) allow the entry of negative amounts, and may prevent the entry of non-numeric data, but you need to check the documentation for your compiler. It will be important that the data entered is numeric. It is easier to get a character in the number than to enter a negative value by accident.
From your original code:
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.
Here, since 100-init is not PERFORM
ed, the program control will drop through into 200-ADDMONTH. Labels (paragraphs or SECTIONs) are just labels. They can be the target of a PERFORM, a GO TO, or they can be "fallen through" or "dropped into". They are unlike "subroutine" or "function" definitions in other languages you probably know.
So, 100-init will PERFORM 200-ADDMONTH until it is finished with, then it will fall into 200-ADDMONTH again. Never code that deliberately. Each paragraph/SECTION should be self-contained and not rely on the physical location of its content.
If 100-init were PERFORMed, you'd be OK. Sort of. Because you have a STOP RUN
in 200-ADDMONTH. When 200-ADDMONTH is executed the first time, the program will stop executing. Not what you want.
I've not considered the logic of your actual calculation, just the methods of it. You have duplicated code, so that could go in another PERFORMed paragraph/SECTION.
Be aware of the difference between a paragraph and a SECTION when they are PERFORMed. A SECTION can (these days does not have to) contain paragraphs. When a SECTION is PERFORMed, control returns to the completed PERFORM before the next SECTION. When a paragraph is PERFORMed, control returns before the next paragraph. Paragraphs cannot contain other paragraphs. To PERFORM a range of paragraphs, THRU will be required on the PERFORM. Unless dictated by tutor/site-standards, avoid coding that. Again, it relies on the physical location of code. Which is bad.
These days, there should be no intrinsic need for SECTIONs, and no need (except diktat) for PERFORM ... THRU ....
来源:https://stackoverflow.com/questions/28622490/income-tax-logic-questions