首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

3.4   关于魔数

程序员常常将数值字面量称作“魔数(magic numbers)”。有时从使用数值字面量(literal number)的上下文来看,它的意义是显而易见的,例如以下代码片断中用于将小时转换为分钟的系数:

我看不出在此上下文中使用字面量60有何不妥。但在有些情况下,即便上下文使得用途清晰,我可能依然倾向于使用具名值。例如:

你可能认出这个字面量是数学常量p的一个近似值,但你能确定我输入了正确的数字吗(其实我没有)?另外,如果p出现在程序中,它很可能会不止出现一次。

可读性和正确性只是把字面量换成名字的两个原因。在C++中,我们通过为字面量定义一个以const限定的名字来实现这一点。以上代码现在变为:

瞬间,任何人,即使具备的领域常识再少,也可以明白最后一行代码。使用具名常量意味着我们只需要仔细地输入一次数值,在那之后,我们就可以编写出易读的代码。万一我输错了值,或希望提供更多的有效数字,只有一处需要改动的地方。

数值本质上反复无常之处,就是魔数造成更多麻烦的地方。在number-sorting程序中,选择-999标记输入的结束,是完全随心所欲的。它之所以意味着输入的终止,是因为我决定让它具有这种含义。一个观看代码的陌生人有理由询问-999有什么特殊的含义。答案无它,只是因为它是一个魔数,其性质恰好是我决定赋予的。我们应当永远用具名值替代这种数值字面量。在C++中,我们通过为一个适当类型的const对象提供一个合适的名字而做到这一点。在number-sorting程序中,我可能会提供:

现在我将第11行替换为:

将第14行替换为:

我希望你觉得结果更清晰且易于维护。你可以通过修改end_input的定义简单地将终止值修改为别的什么。另外,源代码将需要更少的注释,因为具名值传达了你的意图。

从今以后,带着一定程度的怀疑看待字面量,问问你自己在上下文中使用它们是否明智,它们有“魔”的味道吗?你将从减少了许多倍的维护时间中,找回为一点点额外的录入而付出的代价。

在fgw::playpen对象所使用的颜色的默认调色板中,为颜色编码的位(bits)使用的名字fgw::red1等,就是消除魔数的一个例子。在本例中,当我们离开默认调色板,名字便失去作用(事实上,它们肯定变得有误导性)。具名值并不能解决所有的问题。我们必须谨慎地选择名字。

练习

1. 编写一个程序,询问用户将要输入多少个单词,然后将这些单词收集到一个std::vector<std::string>里面。输入完毕后,按字母顺序排列单词并将它们输出为一列。

2. 编写一个程序,从键盘收集单词直到用户输入“END”为止。然后它应该对单词进行排序并输出为一列。通过使用一个适当命名的常量对象,来设法避免使用魔值“END”。

3. 编写一个程序,此程序在Playpen窗口中显示一列20个黑色像素。仔细检查以确保它的一列有20个像素,不是19也不是21。我建议你不要使用默认的Playpen比例,而是把比例设置得大一些。你也许可以想出一种办法能让你计算出所绘制的像素。

4. 编写一个程序,提示用户输入两个整数,然后在Playpen窗口中显示一个十字架,它的高度是第一个数字,宽度是第二个数字。修改这个程序,以便用户可以在除了黑色的其他颜色中选择一个颜色。比方说,你可以问询用户红色的量(0到7),绿色的量(0到7)以及蓝色的量(0到7)。你知道的C++以及默认Playpen调色板的相关知识足以完成这个任务,但如果你还不是一个能熟练地以某种其他语言编程的程序员,这对你来说这可能有些难度。

5. 编写一个程序,用256种颜色的“瓷砖”覆盖Playpen,256种颜色中每一种颜色都在默认调色板中。如果你使用的比例是32,16行每行16个逻辑像素将精确地覆盖Playpen。我给你的唯一的提示是考虑使用嵌套的for循环。你也许不得不多实验几遍。(别忘了你可以改变原点,如果这对你有帮助的话)

6. 编写一个程序,画出Playpen的对角线。

7. 值集合的中位数是指当它们按照数字顺序排列好之后处于中间的那个数。如果因为在集合中有偶数个值而导致中间有两个值的话,中位数就是中间那一对值的算术平均数。编写一个程序收集输入的值,然后输出它们的中位数。

8. 考试成绩以这样的规律进行分级:前20%的考生得“A”,接下来的20%得“B”等,最后20%的人得“E”。编写一个程序,收集20位考生的分数,然后输出等级分界。注意,你无须知道可能的最高分。为得到完美的解决方案,你需要解决多位考生处于一个等级分界上的问题。

延伸练习

这些练习为有经验的程序员提供了更大的挑战。如果你感到以上练习很费力,那么暂时丢下这些练习。稍后你可以随时再回过头来做它们。像往常一样,答案被限制于仅仅使用迄今为止我介绍过的C++知识。

9. 三角形数(triangular numbers)是那些由前n个整数的和产生的数。前四个是:1,

3(= 1 + 2),6(= 1 + 2 + 3)和10(= 1 + 2 + 3 + 4)。编写一个程序,在Playpen中将三角形数的像素显示为三角形。它应该以你所选定的颜色显示一个单独的像素开始,一直等到用户按下Enter键。在那时,它应该添加一行两个像素,使现有的像素在新行的正上方。这个过程重复进行,直到屏幕显示了第十个三角形数为止。

10. 编写一个程序,将从1到100的整数分成两组,使两组数字的平方根之和相等,保留六位有效数字。小心:你正在处理浮点数,因此需要谨慎处理相等的概念。你可以使用库函数std::sqrt。你需要包含<cmath>头。

11. 编写一个程序,接受若干单词作为输入,并输出它们的平均(算术平均)长度以及元音字母对辅音字母的总体比率。你可以利用这一事实:std::string类型的对象与std::vector类型的对象一样,也可以通过使用size()来报告它们的大小(字符的个数)。

12. 编写一个程序,此程序接受成对的单词,然后依据它们是否由相同的字母组成,选择输出“anagram”或“not anagram”。可以使用和存储std::vector对象完全相同的方式来存储std::string对象。你还可以使用==比较两个字符串是否相等。那应该会有很大的帮助。

13. 编写一个程序,它接受值位于-100到100范围内的整数,并以文本输出它们的名字。因此,如果输入27,输出应该是“twenty-seven”。如果输入-39,输出应该是“minus thirty-nine”或“negative thirty-nine”。如果你学有余力,可尝试将程序转换为处理某种其他自然语言,例如法语。甚至尝试更困难的:编写一个程序,让用户输入文本,而输出是数字。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论