这些被认为是魔术数字吗?

时间:2010-01-25 05:34:15

标签: c++ arrays magic-numbers

我刚刚为编程课程编写了一个程序,我想避免使用魔术数字,所以这是我的问题:

在下面的函数中,我的数组索引器是否会被视为幻数?

代码:

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;
}

谢谢!

10 个答案:

答案 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数组内容的数字,我可能会将这些数字视为数据,而不一定是值得使用唯一名称的数字。

我认为理想情况下,它们会来自数据文件,而不是嵌入到程序中,但您的任务/要求可能会另有规定。

但是,用于索引数组的数字可能值得名字。