问题
My homework task is to create a program that accepts a numeric input between 0-100 and returns a letter and +/- if appropriate. We are to accomplish this using nested if statements. I attempted to create an outer if statement that would return a letter grade followed by nested if statements that would return the +/- part.
The output I receive varies from 6543 to 00. I have copied my code below. Could anyone point me in the right direction? I feel like this is a bit of a mess.
import java.util.Scanner;
import java.lang.Math;
public class Grade {
public static void main (String [] args) {
Scanner scan = new Scanner (System.in);
//Prompt user to enter grade
System.out.println("Please enter your grade ");
int grade = scan.nextInt();
byte grade1 = (0);
byte note = (0);
//Determine letter and +/-
if ( grade <= 100 && grade >= 90 ) {
grade1 = 'A';
if (grade <= 100 && grade >= 96) {
note = '+';
}
else if (grade <= 94 && grade >= 90) {
note = '-';
}
}
else if ( grade <= 80 && grade >= 89 ) {
grade1 = 'B';
if (grade <= 89 && grade >= 86) {
note = '+';
}
else if (grade <= 84 && grade >= 80) {
note = '-';
}
}
else if ( grade <= 70 && grade >= 79 ) {
grade1 = 'C';
if (grade <= 79 && grade >= 76) {
note = '+';
}
else if (grade <= 74 && grade >= 70) {
note = '-';
}
}
else if ( grade <= 60 && grade >= 69 ) {
grade1 = 'D';
if (grade <= 69 && grade >= 66) {
note = '+';
}
else if (grade <= 64 && grade >= 60) {
note = '-';
}
}
else if ( grade <= 59 ) {
grade1 = 'F';
}
//Print out grade
System.out.println("You have a " + grade1 + note + " in the class.");
// End program
scan.close();
System.exit(0);
}
}
回答1:
else if ( grade <= 80 && grade >= 89 ) {
Take a moment to think about that one. I think you intend for e.g. 85
to cause this if to trigger, right.
Is 85 lower or equal than 80? I don't think it is. It's not 89 or higher either. In fact, no number will ever satisfy this condition.
Flip your <
and >
signs :)
Second issue is that grade1 and note are bytes, which are numbers, so "You have a " + (some number) + (some other number) + " in the class" is always going to print "You have a 12345677 in the class", that is, those things are, well, numbers. I have no idea why you thought byte
was going to work out here. Give it another try with char
.
回答2:
I feel like this is a bit of a mess.
Yes, conditional statements tend to have that effect. The terms are "high cyclomatic complexity" for code with many branches and WET for code with much repetition (note how there are many branches with note = '-';
or note = '+';
in them--these could be mostly moved out into one branch and appended after determining the letter grade).
Although it's instructive (and in this case, required) to write code like this initially, it's good to see that there are better ways to achieve the result. The typical solution for nasty branching logic is to find a pattern and use a lookup table of some sort. In this case, the string "FFFFFFDCBAA"
lets us enumerate the 5 possible grade buckets by dividing the score by 10 and indexing into the string. We can determine +
/-
by taking the modulus of the score by the number of available grade buckets to see where it lands in the 0-10 range.
Here's a complete example with a few tests. It's not flawless and there are a few annoying edge cases to write explicit conditionals for, but we've managed to reduce over a dozen nested, error-prone branches down to 2 (a third branch was added to test the precondition).
Note also that code was moved out of main
into a reusable function we could call as many times as we wanted. User input/interaction is a totally separate module that's best to keep as decoupled from program logic as possible.
I also made an effort to keep magic numbers out of the code as much as possible, relegating them to constants at the top of the function. These could be parameters in some use cases to make our function more adaptable, but it seems a safe bet that the grading system is fixed for the lifetime of this app.
class Grader {
public static String getGrade(int score) {
final int MAX_SCORE = 100;
final int MIN_SCORE = 0;
final var GRADES = "FFFFFFDCBAA";
final int BUCKETS = GRADES.length() - 1;
if (score < MIN_SCORE || score > MAX_SCORE) {
var msg = "score must be between " + MIN_SCORE +
" and " + MAX_SCORE + " inclusive";
throw new IllegalArgumentException(msg);
}
final char letter = GRADES.charAt(score / BUCKETS);
final int adjustment = score % BUCKETS - BUCKETS / 2;
if (adjustment == 0 || letter == GRADES.charAt(0)) {
return "" + letter;
}
return letter + (adjustment < 0 && score != MAX_SCORE ? "-" : "+");
}
public static void main(String[] args) {
var tests = new int[] {
0, 16, 59, 60, 72, 75, 89, 90, 93, 95, 96, 100
};
for (int test : tests) {
System.out.println(test + " => " + getGrade(test));
}
}
}
Output:
0 => F
16 => F
59 => F
60 => D-
72 => C-
75 => C
89 => B+
90 => A-
93 => A-
95 => A
96 => A+
100 => A+
回答3:
Here's another suggestion that let's you reduce the conplexity of your code. As long as you evaluate the numeric grade in ascending order, you don't need both a low and high range.
if (grade >= 90) {
// you know it's an A so decide what type
grade1 = 'A';
if (grade >= 96) {
note = '+';
} else if (grade <= 94) {
// note that here it can'be less than 90 since you're
// already in the >= 90 conditional block
note = '-';
}
// it wasn't an A so continue on
} else if (grade >= 80) {
// must be a B so determine what type
grade1 = 'B';
// Can't be >= 90 since you already processed that case.
// Had it succeeded, you wouldn't be here.
if (grade >= 86) {
note = '+';
} else if (grade <= 84) {
// can't be less than 80 since you are in
// the >= 80 conditional block.
note = '-';
}
// so continue on in that manner.
} else if (grade >= 70) {
...
...
As you're new to programming, the above may be a little hard to follow. But eventually it will be clear why it works.
Note: In the first case above when checking for an A, it does not check an upper bound since it is presumed that anything greater than 90 is an A. If you need to check for erroneous input (e.g. 2973) then in that particular case it may be appropriate to check for <= 100.
Of course, there are other tools in Java that lets one simplify this. Here is one example using a TreeMap. A map simply associates one value with another, via a key
. TreeMaps have additional methods that lend themselves to accessing values relative to a given key or the key itself.
TreeMap<Integer,String> map = new TreeMap<>();
String grades = "DCBA";
Enter the grades using the key as the threshold and the value as the grade. The end grades are added explicitly. The others are added for the top of their ranges. The grade chosen is the character at index i/10-6
with the appropriate suffix appended for the given range.
map.put(100,"A+");
map.put(59, "F");
for (int i = 65; i <= 100; i+= 10) {
String grade = grades.charAt(i/10-6)+"";
map.put(i+4, grade+"+");
map.put(i, grade);
map.put(i-1,grade+"-");
}
The hard part is done. Now you can use the TreeMap#ceilingEntry method to obtain the appropriate grade for the key. It works by returning the key-value mapping associated with the least key greater than or equal to the given key, or null if there is no such key.
The following prints the grades for the numeric scores from 100 to 80 inclusive.
for (int i = 100; i >= 80; i--) {
System.out.println( i + " " + map.ceilingEntry(i).getValue());
}
Prints
100 A+
99 A+
98 A+
97 A+
96 A+
95 A
94 A-
93 A-
92 A-
91 A-
90 A-
89 B+
88 B+
87 B+
86 B+
85 B
84 B-
83 B-
82 B-
81 B-
80 B-
来源:https://stackoverflow.com/questions/63864819/using-nested-if-statements-in-java-to-calculate-letter-grade-from-numeric-input