f(sixleaves) = sixleaves

重剑无锋 大巧不工

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  95 随笔 :: 0 文章 :: 7 评论 :: 0 Trackbacks
回顾第1章,第一章我们认识到了变量的定义,定义ing时赋值,操作符重载(Overloaded),和没有深入探讨的构造函数,成员函数的概念、符号直接量(与字符直接量的区别),还有输入输出缓冲模型之其好处(三个事件才会刷新缓冲区,输出到设备上,分别是,缓冲区已经满,遇到cin,显示要求刷新(如std::endl,控制符(manipulator)))。
这章我写得有点急切,应为之前C++学过,有些概念一跳而过,看不懂的,可以往下找红色字体处开始(从循环不变式分析处开始的分析,再回头来看这个)。
 1 #include <iostream>
 2 #include <string>
 3 
 4 int main() {
 5     //ask for the person's name
 6     std::cout << "Please enter your first name: ";
 7 
 8     //read the name
 9     std::string name;
10     std::cin >> name;
11 
12     //build the message that we intend to write
13     const std:;string greeting = "Hello, " + name + "!";
14 
15     //we have to rewrite this part
16 }
#
#分析:我们现在需要重写(重构//we have...后面的代码),应该这样思考,以前的那个程序不具备好的可扩展性,为什么呢?首先如果要求输入的框架编程10行(空白#行变成10行),后面的代码久要多加很多行,一行行的进行输出。这时我们可以用循环对代码进行重构。我们先分析,在greeting上下空白行只有一行,所以我们用pad
#表示空白行,而总的行数为2 * pad + 3(头尾加greeting那行)。这样我们就可以让程序输出任意多行。于是有如下代码
const int pad = 1;

const int rows = pad * 2 + 3;
#另外我们这个输出的框架是要让左右两边的空白数和上下两端的空白数相同,所以也只需要定义一个变量就够了。每一行输出的字符数就是greeting的长度加上pad * 2加上两个#两个星号。即如下代码const std::string::size_type cols = greeting.size() + pad * 2 + 2;

 1 
 2 #include <iostream>
 3 #include <string>
 4 using std::cin;        using std::endl;
 5 using std::cout;       using std::string;
 6 int main() {
 7     cout << "Please enter your first name: ";
 8 
 9     string name;
10     cin >> name;
11 
12     const string greeting = "Hello, " + name + "!";
13 
14     const int pad = 1;
15 
16     const int rows = pad * 2 + 3;
17     const string::size_type cols = greeting.size() + pad * 2 + 2;
18 
19     cout << endl;
20 
21     // invariant:we have written r rows so far
22     for(int r = 0; r != rows; ++r) {
23 
24         string::size_type c = 0;
25 
26         // invariant:we have written c characters so far in the current row
27         while(c != cols) {
28 
29             if(r == pad + 1 && c == pad + 1) {
30                 cout << greeting;
31                 c += greeting.size();
32             } else {
33 
34                 if(r == 0 || r == rows -1 || c == 0 || c == cols - 1)
35                     cout << "*";
36                 else
37                     cout << " ";
38                 ++c;
39             }
40         }
41 
42         cout << endl;
43 
44     }
45     return 0;
46 }
#第一个::说明string名字定义在名字空间std中,而第二个::则表示size_type来自string类。std::string定义了size_type,用来表示一个string中含有的字#符数目。如果需要一个局部变量来表示一个string长度,可以使用std::string::size_type类型定义一个变量。
#size_type是一个无符号的类型
#输出边界字符,如果r = 0,由循环不变式可以知道,现在一行也没有输出。所以当r = row - 1,已经输出了row - 1行,接下来输出的是最后一个部分,类似的,如果c = 0,输出的将是第一列的部分。
#输出边界符号:
#那么我们如何判断输出greeting这行呢,由循环不变式,我们可以 r = pad + 1 时,c = pad + 1时,开始输出greeting。

#第二章写得有点乱,上面代码看不懂的,请看下面分析
#首先我们要介绍一个概念,叫做循环不变式,循环不变式就是我们设置一个断言,让该断言在该循环中始终都成立,结束后也成立,这样这个断言其实就是这段程序的意思。看如
#下代码:
//invariant:we have written r rows so far

int r = 0;
//setting r to 0 makes the invariant true

while(r != rows) {
    //we can assume that the invariant is true here
    
//waiting a row of output makes the invariant false
    std::cout << std::endl;
    //incrementing r makes the invariant true again
    r++;
}
//we can conclude that the invariant is true here
#首先你应该想一想要确保不变式始终为true,只要确保在循环进入点为true,一次循环结束点为true,那么这个不变式久永远为true,understand?如果还不理解,先吧我说
 #的这句话理解了,在继续往下看,不然你不知道我在讲什么东西!
 #我们的不变式就是上述断言invariant:we have written r rows so far 
 #我们分析过,不变式的两个断点,一个设在开头,一个在结尾,所以开头时r = 0。此时程序一行也没输出,不变式为true,在结尾处r++后,仍为true,为什么呢?举个例子,r = 0,进来之后,将输出一行,所以此时r不应该在为0,而应该为1.
 #这是每一行输出的框架,转换成for循环就是上面相应的代码,而至于另外一个循环一样个道理。
#下面再介绍一个重要的概念,这个概念我之前还真没学好,看完后,恍然大悟,大测大悟阿!那就是循环时的计数问题。
 #在C中C++中我们写循环经常是重int i = 0,从0开始是不?就算是,你是不是经常这样写for(int i = 0; i <= number; i++);但是更好的写法应该是for(int i = 0; i  #!= number; i++);为什么呢?请听我慢慢道来.
 #首先我们知道在不对称区间[0, rows)计数的话,很明显就是rows个数,但是如果你使用的是对称区间,[num,rows]则有rows - num + 1个数,是不是很不明显,再则从0开  #始一目了然,别说你看不出来,我在举个例子(0,66],和[21,86]哪一个你能快速判断出有几个数。
 #有的人又说,这算什么阿,我从1开始贝[1,66],不就多算一个数么,习惯就好。我想说,你说的没粗,但我懒,用不对称区间跟块算出,更不会出错。在则,用不对称区间的好  #处是容易和invariant(循环不变式)相结合,例如,如果你从1开始计数,有的人想我们把不变式改成现在输出第r行,但是这样是不能作为一个不变式的,所谓不变式,就是
 #这个断言永远正确,但是当你结束循环时r = rows + 1,就变成了输出第rows + 1行,但这个不变式就变成错的鸟,understand。
 #再则我们选者!=而不是<=来作为比较操作符。这个差别很小,但是很不一样,前者,循环结束时(只要没有在循环里break),就能判断此时r = rows,但是如果是后者,我  #们这能证明至少输出了rows行,为啥?回忆下学过的math,<=,是什么意思?
 #还有一条好处,我就不罗嗦了,综上所属,你可以发现从0开始计数的好处!,想当一时,在写链表时,就是因为这个计数问题,自己也整了个证明方法,哈哈,每想到早就有更  #简单的方式了。
#本人才疏学浅,看不懂的,可以留言讨论之。
posted on 2014-02-21 16:21 swp 阅读(237) 评论(0)  编辑 收藏 引用 所属分类: program language

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理