语句(14-19)

一:顺序语句

组织直线型的代码的原则是按照依赖关系进行排列。

如果代码之间没有依赖的关系,那就使相关的语句可能靠近。

二:if语句的指导原则

把正常的情况放到if的后面而不是else的后面。

让if子句的后面跟随一个有意义的语句(if的语句块不要为空。)

注意不要把if与else的语句弄反。

三:循环的指导原则

只有一个位置进入循环。

把初始化的代码放到循环的前面。

用while(true)表示无限循环。

避免空循环。

一个循环只做一件事。

设法确认循环可以终止。

循环要尽可能的短,把嵌套限制在三层以内。

把长循环的内容移到子程序里面。

四:表驱动法:是一种编程模式,从表里面查找信息而不使用逻辑语句。

三步去介绍这个表驱动法。第一步是定义,前面已经抄了书上的一句话,已经说的很明白了,就是用查表来代替if语句或switch语句。第二步是原因,可以举个例子,如果有这样一个函数int getTotalDayInMonth(int month),它输入一个月份,然后返回这个月份的总天数(不考虑润年,二月以28天计),比如输入5,返回的是31,因为5月里共有31天。

一种写法是这样的:

 1 // 获得某一月中的总天数,monthIndex从 1 开始
 2 int getTotalDayInMonth(int month)
 3 {
 4     int totalDay = 0;
 5     if(month == 2)
 6     {
 7         totalDay = 28;
 8     }
 9     else if(month == 4 || month == 6 || month == 9 || month == 11)
10     {
11         totalDay = 30;
12     }
13     else
14     {
15         totalDay = 31;
16     }
17     return totalDay;
18 }

在这种写法里使用了逻辑if判断,里面有很多凭空出现的数字,比如2,4,11等,这些称为magic number的数字出现在程序里是很不好的,因为不好修改与扩充,比如说上面的代码适合与地球上的计算,现在要你去改一个火星上的情况(火星公转时长不同于地球,所以每个月划分的天数也会不同),你可能就要去改这些magic number了,更复杂的,你可能要因为更多的天数可能性(假定火星7月只有17天,而9月有21天),而添加更多的if分支。但如果像下面这样使用一个表,就使程序简单多了:

 1 const int totalDayTable[12] =
 2 {
 3     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
 4 };
 5
 6 // 获得某一月中的总天数,monthIndex从 1 开始
 7 int getTotalDayInMonthFromTable(int month)
 8 {
 9     return totalDayTable[month - 1];
10 }

怎么样,把用表与不用表的代码对比一下,是不是要简化许多呢?更有意义的是,如果某个天数变了,可以直接修改或扩充totalDayTable就行了,至于函数则分毫不用修改。

好吧,写到这里,应该已经解决了第二步了,就是使用表驱动可以提高源程序的可读性,使之更简洁而且更容易修改与扩充。

下面走到第三步,也是本章中花篇幅最大的一步了——如何去用表驱动来解决问题。

书上说查表方法可以细分为三种:

(1)     直接访问

(2)     索引访问

(3)     阶梯访问

直接访问是最简单的,查表本质其实就是去索引“键”来获得“值”,有点像获得数组值一样,给定下标index,然后matrix[index]就获得数组在相应下标处的数值,再如前面的return totalDayTable[month - 1]就是直接用month-1来作为键的,而值可以直接通过查表来获得。

现在有个问题,万一“键”是不能直接用的呢?比如我想设计一个幼儿学习动物的软件,若小孩想查找牛的信息,这时屏幕上会打印出牛的特性,而若小孩想查找狗的信息,则屏幕上会打印出狗的信息。显然,这里的键是动物名,而值是相应的描述。下标必须是整数,但动物名是string,怎么办呢?数据结构中的hash表当然可以了,它就是计算string的hash值,通过hash值来索引表格的,但在这里我们不打算用hash值,而是由程序员自行设计string到int的映射,怎么做呢?很简单啊,自己做个菜单呗,让用户只能选择相应的数字,这样“键”就成int了哈。

代码如下:

 1 class Animal
 2 {
 3 public:
 4     virtual void print() = 0;
 5 };
 6
 7 class Dog: public Animal
 8 {
 9 public:
10     void print()
11     {
12         cout << "This is Dog..." << endl;
13     }
14 };
15
16 class Cat: public Animal
17 {
18 public:
19     void print()
20     {
21         cout << "This is Cat..." << endl;
22     }
23 };
24
25 class Cow: public Animal
26 {
27 public:
28     void print()
29     {
30         cout << "This is Cow..." << endl;
31     }
32 };
33
34 Animal* animalTable[] = {
35     new Dog, new Cat, new Cow
36 };
37
38 int main()
39 {
40     cout << "想知道哪种动物的描述?" << endl;
41     cout << "1. 狗" << endl << "2. 猫" << endl << "3. 奶牛" << endl << endl;
42     int choiceIndex;
43     cout << "我选择:";
44     cin >> choiceIndex;
45     assert(choiceIndex >= 1 && choiceIndex <= 3);
46     animalTable[choiceIndex - 1]->print();
47 }

运行结果为:

这里用了C++的多态性,根据不同的实体对象能调用相应的print函数,但我更想表达的是表驱动法的应用,请把目光放在表animalTable上吧,这样的排序,就是将Dog映射成数字1,Cat映射成数字2了。这是人为的映射,但用途更广的是hash映射,不知道的同学去看看数据结构吧,hash表可以快查找的利器,面试中常常被问到。

第二种表驱动法是索引访问表,它适用于这样的情况,假设你经营一家商店,有100种商品,每种商品都有一个ID号,但很多商品的描述都差不多,所以只有30条不同的描述,现在的问题是建立商品与商品描述的表,如何建立?还是同上面做法来一一对应吗?那样描述会扩充到100的,会有70个描述是重复的!如何解决这个问题呢?方法是建立一个100长的索引,然后这些索引指向相应的描述,注意不同的索引可以指向相同的描述,这样就解决了表数据冗余的问题啦。

第三种表驱动法是阶梯访问表,它适用于数据不是一个固定的值,而是一个范围的问题,比如将百分制成绩转成五级分制(我们用的优、良、中、合格、不合格,西方用的A、B、C、D和F),假定转换关系是当成绩在90-100区间,判为A,成绩在80-90区间,判为B,成绩在70-80区间,判为C,成绩在60-70区间,判为D,成绩在60以下,判为F(failure)。现在的问题是,怎么用表格对付这个范围问题?一种笨笨的方法是申请一个100长的表,然后在这个表中填充相应的等级就行了,但这样太浪费空间了,有没有更好的方法?

在《代码大全》上是用表格记录范围上限的,但其实用下限也是可以的,我就尝试用下限做了下(A级的下限是90,B级的下限是80…):

 1 //阶梯访问表,顺序查找
 2 const char gradeTable[] = {
 3     ‘A‘, ‘B‘, ‘C‘, ‘D‘, ‘F‘
 4 };
 5
 6 const int downLimit[] = {
 7     90, 80, 70, 60
 8 };
 9
10 int main()
11 {
12     int score = 87;
13     int gradeLevel = 0;
14     while(gradeTable[gradeLevel] != ‘F‘)
15     {
16         if(score < downLimit[gradeLevel])
17         {
18             ++ gradeLevel;
19         }
20         else
21         {
22             break;
23         }
24     }
25     cout << "等级为 " << gradeTable[gradeLevel] << endl;
26     return 0;
27 }

运行结果如下:

gradeLevel相当于表指针,在程序中就是通过调整这个表指针来使之指向正确的位置的。

程序还有优化的地方,注意到这些下限是有顺序(降序),那可以用二分查找啊,程序如下:

 1 //阶梯访问表,二分查找
 2 const char gradeTable[] = {
 3     ‘A‘, ‘B‘, ‘C‘, ‘D‘, ‘F‘
 4 };
 5
 6 const int DONWLIMIT_LENGTH = 4;
 7
 8 const int downLimit[] = {
 9     90, 80, 70, 60
10 };
11
12
13 int BinarySearch(int score)
14 {
15     int low = 0;
16     int high = DONWLIMIT_LENGTH - 1; //downLimit的最大的Index
17     while(low <= high)
18     {
19         int mid = (low + high) / 2;
20         if(score < downLimit[mid])
21         {
22             low = mid + 1;
23         }
24         else if(score > downLimit[mid])
25         {
26             high = mid - 1;
27         }
28         else
29         {
30             return mid;
31         }
32     }
33     return low;
34 }
35
36 int main()
37 {
38     int score = 87;
39     int gradeLevel = BinarySearch(score);
40     cout << "等级为 " << gradeTable[gradeLevel] << endl;
41     return 0;
42 }
时间: 2024-11-03 16:41:01

语句(14-19)的相关文章

2016年6月22日 星期三 --出埃及记 Exodus 14:19

Then the angel of God, who had been traveling in front of Israel's army, withdrew and went behind them. The pillar of cloud also moved from in front and stood behind them, 在以色列营前行走 神的使者,转到他们后边去,云柱也从他们前边转到他们后边立住.

LINQ to SQL语句Concat/Union/Intersect/Except--2017年2月22日

Concat/Union/Intersect/Except操作 适用场景:对两个集合的处理,例如追加.合并.取相同项.相交项等等. Concat(连接) 说明:连接不同的集合,不会自动过滤相同项:延迟. 1.简单形式: var q = ( from c in db.Customers select c.Phone ).Concat( from c in db.Customers select c.Fax ).Concat( from e in db.Employees select e.Home

黑马程序员_JavaSE学习总结第19天_IO流1

------- android培训.java培训.期待与您交流! ----------  19.01  集合的特点和数据结构总结 HashSet.HashMap.Hashtable判断元素唯一性的方式: 通过对象的hashCode和equals方法来完成元素唯一性 如果对象的hashCode值不同,那么不用判断equals方法,就直接存储到哈希表中. 如果对象的hashCode值相同,那么要再次判断对象的equals方法是否为true. 如果为true,视为相同元素,不存.如果为false,那么

NO2:自动生成sql语句

SQL语句自动生成工具 大哉乾元 2016/2/26   作者原创转载请注明出处 前言 这个程序是几年前做成的,现在整理成文档和大家分享,当时参与的项目中大量使用的sql语句,所以SqL语句的代码输入占了较大的比例,为了提高sql语句的书写正确性和输入效率,做成了这个自动生成工具. 一:ADO.NET中执行sql文,对应的sql语句嵌入到vb.net的代码内,分析sql的语法格式个特点: 已检索语句为例分析: A:必须是已select开头 B:检索的字段名(一些系统函数:可选,例如max,sum

JavaScript基础4——关于语句流程控制(分支语句、循环语句等)

分支语句 (1)if...else...语句,基本格式分三种,如下 1 <script type="text/javascript"> 2 var i=50; 3 //if语句 4 if (条件) 5 { 6 条件成立时执行代码 7 } 8 9 //if...else语句 10 if (条件) 11 { 12 条件成立时执行此代码 13 } 14 else 15 { 16 条件不成立时执行此代码 17 } 18 19 //if...else if...else if...e

[MySQL Reference Manual]14 InnoDB存储引擎

14 InnoDB存储引擎 14 InnoDB存储引擎... 1 14.1 InnoDB说明... 5 14.1.1 InnoDB作为默认存储引擎... 5 14.1.1.1 存储引擎的趋势... 5 14.1.1.2 InnoDB变成默认存储引擎之后... 5 14.1.1.3 InnoDB表好处... 6 14.1.1.4 InnoDB表最佳实践... 6 14.1.1.5 InnoDB表提升... 6 14.1.1.6 InnoDB作为默认存储引擎测试... 6 14.1.1.7 验证In

页面加载优化的14条原则

1. 尽可能的减少 HTTP 的请求数 [content] 2. 使用 CDN(Content Delivery Network) [server] 3. 添加 Expires 头(或者 Cache-control ) [server] 4. Gzip 组件 [server] 5. 将 CSS 样式放在页面的上方 [css] 6. 将脚本移动到底部(包括内联的) [javascript] 7. 避免使用 CSS 中的 Expressions [css] 8. 将 JavaScript 和 CSS

14个优化网站性能提高网站访问速度技巧

相信互联网已经越来越成为人们生活中不可或缺的一部分.ajax,flex等等富客户端的应用使得人们越加“幸福”地体验着许多原先只能在C/S实 现的功能.比如Google机会已经把最基本的office应用都搬到了互联网上.当然便利的同时毫无疑问的也使页面的速度越来越慢.自己是做前端开发的,在性能方面,根据yahoo的调查,后台只占5%,而前端高达95%之多,其中有88%的东西是可以优化的. 以上是一张web2.0页面的生命周期图.工程师很形象地讲它分成了“怀孕,出生,毕业,结婚”四个阶段.如果在我们

JavaSE学习总结第19天_IO流1

19.01  集合的特点和数据结构总结 HashSet.HashMap.Hashtable判断元素唯一性的方式: 通过对象的hashCode和equals方法来完成元素唯一性 如果对象的hashCode值不同,那么不用判断equals方法,就直接存储到哈希表中. 如果对象的hashCode值相同,那么要再次判断对象的equals方法是否为true. 如果为true,视为相同元素,不存.如果为false,那么视为不同元素,就进行存储. 最终:自动生成hashCode()和equals()即可 Tr

【书评:Oracle查询优化改写】第14章 结尾章

[书评:Oracle查询优化改写]第14章 结尾章 一.1  相关参考文章链接 前13章的链接参考相关连接: [书评:Oracle查询优化改写]第一章 http://blog.itpub.net/26736162/viewspace-1652985/ [书评:Oracle查询优化改写]第二章 http://blog.itpub.net/26736162/viewspace-1654252/ [书评:Oracle查询优化改写]第三章 http://blog.itpub.net/26736162/v