我刚刚为编程课程编写了一个程序,我想避免使用魔术数字,所以这是我的问题:
在下面的函数中,我的数组索引器是否会被视为幻数?
代码:
string CalcGrade(int s1, int s2, int s3, double median)
{
const int SIZE = 23;
const int LETTER_GRADE_BARRIERS[SIZE] = { 400, 381, 380, 361, 360, 341, 340, 321, 320, 301, 300, 281, 280, 261, 260, 241, 240, 221, 220, 201, 200, 181, 180 };
double finalGrade;
string letterGrade;
finalGrade = s1 + s2 + s3 + median;
if (finalGrade >= LETTER_GRADE_BARRIERS[1] && finalGrade <= LETTER_GRADE_BARRIERS[0])
{
letterGrade = "A";
}
else if (finalGrade >= LETTER_GRADE_BARRIERS[3] && finalGrade <= LETTER_GRADE_BARRIERS[2])
{
letterGrade = "A-";
}
else if (finalGrade >= LETTER_GRADE_BARRIERS[5] && finalGrade <= LETTER_GRADE_BARRIERS[4])
{
letterGrade = "B+";
}
else if (finalGrade >= LETTER_GRADE_BARRIERS[7] && finalGrade <= LETTER_GRADE_BARRIERS[6])
{
letterGrade = "B";
}
else if (finalGrade >= LETTER_GRADE_BARRIERS[9] && finalGrade <= LETTER_GRADE_BARRIERS[8])
{
letterGrade = "B-";
}
else if (finalGrade >= LETTER_GRADE_BARRIERS[11] && finalGrade <= LETTER_GRADE_BARRIERS[10])
{
letterGrade = "C+";
}
else if (finalGrade >= LETTER_GRADE_BARRIERS[13] && finalGrade <= LETTER_GRADE_BARRIERS[12])
{
letterGrade = "C";
}
else if (finalGrade >= LETTER_GRADE_BARRIERS[15] && finalGrade <= LETTER_GRADE_BARRIERS[14])
{
letterGrade = "C-";
}
else if (finalGrade >= LETTER_GRADE_BARRIERS[17] && finalGrade <= LETTER_GRADE_BARRIERS[16])
{
letterGrade = "D+";
}
else if (finalGrade >= LETTER_GRADE_BARRIERS[19] && finalGrade <= LETTER_GRADE_BARRIERS[18])
{
letterGrade = "D";
}
else if (finalGrade >= LETTER_GRADE_BARRIERS[21] && finalGrade <= LETTER_GRADE_BARRIERS[20])
{
letterGrade = "D-";
}
else if (finalGrade <= LETTER_GRADE_BARRIERS[22])
{
letterGrade = "Fail";
}
return letterGrade;
}
谢谢!
答案 0 :(得分:14)
是的,除了-1,0或1之外的任何数字都可能是一个幻数。
除非你是一个真正的大师,否则你可能也可以自由使用两个权力: - )
顺便说一下,你可能会重构那些代码,使其更容易理解,例如:
string CalcGrade (int s1, int s2, int s3, double median) {
// Grade lookup arrays. If grade is >= limit[n], string is grades[n].
// Anything below D- is a fail.
static const int Limits[] = {400, 380, 360, 340,320, 300, 280,260, 240, 220,200,180 };
static const int Grades[] = {"A+","A","A-","B+","B","B-","C+","C","C-","D+","D","D-"};
double finalGrade = s1 + s2 + s3 + median;
// Check each element of the array and, if the final grade is greater
// than or equal to, return the grade string.
for (int i = 0; i < sizeof(Limits) / sizeof(*Limits); i++)
if (finalGrade >= Limits[i])
return Grades[i];
// Otherwise, failed.
return "Fail";
}
这会将遍布代码的幻数移到一个显而易见的区域(假设你很好地对齐它们)。
它还消除了原始解决方案的问题,即我们对得分为380.5的人所做的事情 - 它不是真的公平地失败那些人:-)或者指定等级到""
对400以上的人(因为似乎无法返回"A+"
)。
答案 1 :(得分:4)
在你正在做事的时尚中,我会说它们不是魔术数字。你会把它们重命名为什么?我想不出任何有用的答案(static const int One = 1;
没用。)
400, 381,
等行首先让我感到困惑。我会在它上面加上// GPA times 100
之类的内容来澄清。
事实上,虽然你的问题(数组索引)不是太神奇,但400...
行应该替换为static const int A = 400; static const int AMinus = 381;
然后...BARRIERS[] = {A, AMinus,}
等等。那些肯定是有意义的常数
有替代(更干净)的方法需要数字,绝对应该变成命名常量。 (上面提到的相同)
答案 2 :(得分:3)
是。您需要重新编译才能更改数字;这就是问题所在。
任何类似的配置都应该是可配置的,不需要重新编译。当然,您的配置中可能仍有数字,但在您的情况下,它似乎都是配置表的合法数据。
答案 3 :(得分:3)
不如何做一点幽默?
string CalcGrade (int s1, int s2, int s3, double median) {
int grade = median + s1 + s2 + s3;
grade = (grade>400)?400:((grade<180)?179:grade);
return
"Fail\0D-\0\0\0D\0\0\0\0D+\0\0\0C-\0\0\0C\0\0\0\0"C+\0\0\0"
"B-\0\0\0B\0\0\0\0B+\0\0\0A-\0\0\0A\0\0\0\0A+"[((grade-160)/20)*5];
}
答案 4 :(得分:1)
LETTER_GRADE_BARRIERS
的定义与它们实际代表的内容不相符,所以是的。如果它是int和char *(标记)的结构数组,则为no。
答案 5 :(得分:1)
是的,但它们使用常数正确表示,所以没有问题。
但是,我会考虑将字母等级分配给另一个数组并将它们与障碍对齐。我肯定会使用一个循环而不是分别写出12个案例中的每一个。
答案 6 :(得分:1)
它可以看起来更简单,例如使用std::lower_bound
来查找哪个括号分数和字母数组,例如letter_grade[]= { "A", ... };
将括号转换为字母等级
答案 7 :(得分:0)
是的,他们绝对是神奇的数字。你的方式也没有帮助。所有这些数字间隔20步(每个数字之前有额外的+1缓冲区),但这在代码中并不明显。一个更好的实现将是这样的:
string CalcGrade(int s1, int s2, int s3, double median) {
const int MAXIMUM_GRADE = 400;
const int MINIMUM_GRADE = 180;
const int GRADE_STEP = 20;
const char* GRADES[] = { "A", "A-", "B+", "B", "B-", "C+", "C", "C-", "D+", "D", "D-" };
double finalGrade = s1 + s2 + s3 + median;
if (finalGrade >= MAXIMUM_GRADE) {
return "A+";
} else if (finalGrade <= MINIMUM_GRADE) {
return "Fail";
} else {
return GRADES[(size_t)((MAXIMUM_GRADE - finalGrade) / GRADE_STEP)];
}
}
答案 8 :(得分:0)
是。数组中的索引没有任何语义含义。这使他们“神奇”。
paxdiablo的反应是一种非常好的方式,尽管我很想将限制和等级名称合并到一个类/结构中。
即使保留代码结构,也要考虑以下两个片段:
// original
else if (finalGrade >= LETTER_GRADE_BARRIERS[13] && finalGrade <= LETTER_GRADE_BARRIERS[12])
{
letterGrade = "C";
}
// compared to
else if (finalGrade >= MIN_C_GRADE && finalGrade < MIN_C_PLUS_GRADE)
{
letterGrade = "C";
}
第二个示例为代码添加了更多的语义含义,而不是依赖于'13'和'14'代表的内容。
将它们存储在一个数组中几乎没有什么收获,因为你实际上没有迭代数组。
对魔术数字的一个很好的检查是向某人描述问题的解决方案。如果这些数字没有出现在你的口头描述中,那么它们几乎肯定是神奇的。
答案 9 :(得分:0)
如果您正在讨论组成LETTER_GRADE_BARRIERS
数组内容的数字,我可能会将这些数字视为数据,而不一定是值得使用唯一名称的数字。
我认为理想情况下,它们会来自数据文件,而不是嵌入到程序中,但您的任务/要求可能会另有规定。
但是,用于索引数组的数字可能值得名字。