3

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

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

代码:

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

谢谢!

4

10 回答 10

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+")。

于 2010-01-25T05:37:39.357 回答
4

按照你做事的方式,我想说它们不是神奇的数字。你会给他们改什么名字?我想不出任何有用的答案(static const int One = 1;没用。)

400, 381,起初,等线对我来说更令人困惑。我会在// GPA times 100上面放一些类似的东西来澄清。

事实上,虽然你的问题(数组索引)不是太神奇,但该400...行可能应该替换为static const int A = 400; static const int AMinus = 381;then...BARRIERS[] = {A, AMinus,}等等。这些绝对是有意义的常数

有一些替代(更干净)的方法需要绝对应该转换为命名常量的数字。(与上面建议的相同)

于 2010-01-25T05:36:17.130 回答
3

是的。您需要重新编译才能更改数字;这就是问题所在。

任何类似的配置都应该是可配置的,并且不需要重新编译。当然,您的配置中可能仍然有数字,但在您的情况下,这一切似乎都是配置表的合法数据。

于 2010-01-25T05:37:11.230 回答
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];
}
于 2010-01-25T08:07:57.983 回答
1

的定义与LETTER_GRADE_BARRIERS它们实际代表的内容脱节,所以是的。如果它是一个 int 和一个 char* (标记)的结构数组,那么没有。

于 2010-01-25T05:36:46.467 回答
1

是的,但是它们使用常量正确表示,所以没有问题。

但是,我会考虑将字母等级分配给另一个数组并将它们与障碍对齐。

而且我肯定会使用一个循环,而不是单独写出 12 个案例中的每一个。

于 2010-01-25T05:40:34.327 回答
1

它可以看起来更简单,例如使用std::lower_bound来查找属于哪个括号分数和字母数组,例如letter_grade[]= { "A", ... };将括号转换为字母等级

于 2010-01-25T05:41:28.303 回答
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' 所代表的含义。

将它们存储在数组中几乎没有什么好处,因为您实际上并没有遍历数组。

对幻数的一个很好的检查是向某人描述问题的解决方案。如果这些数字没有出现在你的口头描述中,那么它们几乎肯定是神奇的。

于 2010-01-25T06:02:28.537 回答
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)];
  }
}
于 2010-01-25T05:56:38.377 回答
0

如果您谈论的是构成LETTER_GRADE_BARRIERS数组内容的数字,我可能会将这些数字视为data,而不一定是值得唯一名称的数字。

我猜想理想情况下它们将来自数据文件而不是嵌入程序中,但您的分配/要求可能另有规定。

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

于 2010-01-25T07:13:21.307 回答