the summary of loop structure in C
| 这个作业属于哪个班级 |
| ---- | ---- | ---- |
| 这个作业的地址 |
| 这个作业的目标 | 学习循环结构内容,包括for循环、while循环、循环嵌套 |
| 姓名 | 骆锟宏 |
0.PTA总分展示:
循环嵌套:
单循环:
1.本章学习总结:
1.1 for循环的语法
for (初值1,初值2;循环条件;循环条件控制语句1,循环条件控制语句2)
{
循环体语句;
}
-
for(;条件;)
等价于while(条件)
-
for循环的初值和循环条件控制语句(或者称为循环体控制的语句)是可以允许多句的,其中每句之间按
,
隔开这是逗号运算符的作用. -
for循环的执行顺序:(直接上截图)
-
先执行表达式1(表达式1也只执行者一遍),然后执行表达式2进行循环体条件的判断,如果判定为假的话,就离开循环,如果判定为真的话,就执行循环体语句,执行完循环体语句后再执行表达式3,
如此重复循环直到不满足循环条件的时刻,离开循环,for循环语句执行完毕。
1.2 while和do-while的语法
while(循环条件判断)
{
循环体语句;
}
do
{
循环体语句;
}while(循环条件);
- while循环可能存在一次也不执行的可能性,而do-while语句至少执行一次。
- 用while语句来写for语句
初值;
while (循环条件)
{
循环体语句;
与循环条件控制相关的语句;
}
- while语句的顺序:(直接上图)
先进行判断循环条件,如果判断条件为假,则不进入循环执行while后的下一条语句,如果判断结果为真则进入循环内部,执行循环体语句,执行完后再次进行循环体条件的判断,按照前面的情况不断循环知道某一次判断循环条件的时候,条件不满足,跳出循环,循环结束。 - do-while语句的顺序:(直接上图)
至少先进行循环体的执行一次,在这之后再进行循环条件的判断,如果判断为假,则不再执行循环体语句,将会执行do-while语句下的语句,而如果判断结果为真的话,将会再次进入循环去执行循环体语句,依次循环,直到条件不满足,判定为假,跳出循环。
1.3跳出循环相关语句break\continue
- break有两个应用场景,一个是在循环语句当中,用于跳出整轮循环,执行break语句后不管循环条件还满不满足都不重要,直接跳出整个循环;break语句也可以用于switch-case语句,在这个语句当中,用于调节该语句,case语句会连续执行下去的特型来作为一个结束点,分隔点来跳出语句。
- 而continue语句只能用于循环语句当中,用于跳过当前这个循环还没有执行的其他语句,直接进入下一个循环。、
1.4循环嵌套
- 对于内循环而言,外循环每执行一次,内循环都要执行一整个循环!
- 对于内循环因该特别注意,内循环执行完后,是否有变量需要被重置。
- 在内循环内如果需要调用库函数比如说数学函数,并且,如果用库函数计算的值不会改变的话,那么考虑在内循环外部用一个变量来储存这个计算出来的数值会让整个程序的效率更高一点!
1.5 for语句,while语句,do-while语句的区别和各自的特点:
- for语句和while语句都属于先判断条件再进入循环的类型,同时,for循环更适用于已知循环次数的情况,而while语句更适用于未知循环次数的情况;do-while语句则是适用于无论条件满不满足,循环体内的语句都至少要执行一遍的类型。
- 比如遇到计算数位的问题的时候,往往我们设置的循环结束的条件为
n != 0
,这时候如果使用while语句,对于 0 这个数的计数位就会变成 0位而出现错误,但这时候如果使用do-while语句的话,就巧妙解决了这个问题。
- 比如遇到计算数位的问题的时候,往往我们设置的循环结束的条件为
2.0学习体会
反馈一下对上次学习体会的总结,这几周来的调整来说,没有太大的进步,但不是没有完全的进步,从数学的学习当中初步发现了结构化的思维逻辑,并且打算应用在c语言程序设计的学习过程中。
或许可以刚好比较好的得契合当下c语言作业提出的思维,博客的作业就契合为让自己人为地在自己的脑子里面建立起合适的知识体系。并且对一些实际问题中用到的编程技巧和实际例题的实操进行总结记忆;
而课堂派上的测试题目刚好适用于对知识点进行巩固和加强,最后PTA刷题进行代码编写能力的训练。将形成先构建起知识体系,然后在构建过程中不断细化,直到形成一个完整的树状结构,然后再在具体实践
过程中根据具体需要逐步调用知识体系内的内容细化,进行应用。
2.编程技巧总结
2.1零散的小插曲
- 可以利用flag这样的状态变量来实现对不同结果或者说是不同分类情况的分类,然后最后再统一设置一个分支语句来同意输出,将程序由抽象到具体一层层结构抽离出来,逐步细化,追求结构化树状展开的逻辑思维。
2.2 各种数列求和问题:
- 首先就是求和一定要有一个用来放置和的量,一般是:
int sum = 0;
或
double sum = 0;
具体的数据类型具体分析。 - 对于加减号交错出现的情况,可以考虑设置一个符号变量sign,用来解决符号的问题,在效率上会比数学上使用-1的奇偶次方的性质来得高效。
- 对于分母或者分子的数值变化情况比较复杂的情况,可以考虑设置一个分母的变量或者分子的变量,在去对分子或者分母单独处理。
- 尤其是对于数目比较大的求数列的情况,一定一定一定一定要注意对于变量的数据类型的确认,因为求数列这种问题很容易出现大数的情况一不小心就会很容易出现数据溢出的情况!而数据溢出之后,再有值就会开始向反方向运算
- 另外补充一下,double是目前可用的数据类型当中,数据范围相对较大的数据!往后还有
long double
2.3 字符如何转数字、数字逆序等问题:
2.3.1 字符如何转数字:
* 字符转数字本质上是利用ASCII码表,数字字符想要转成数字的话,靠的方法就是,用存放字符的字符变量减去字符常量'0',则差值就是对应的数值,而如果是10以上的数值的话,那就得从高到低一位一位取数字,每次取完后,下一次要对上一次整体取到的数字的和乘10进位,在加上目前这次取到的这一位数字,构成这一轮得到的数字和,如此循环知道取到最后以为结束为止。
按代码来说就是这个样子:
//这是一个把按字符输入的数字转化成一个真正意义上的数字的代码。
int sum = 0;
char op;
do
{
ch = getchar();
if (op>='0' && op<='9')
{
sum = sum*10 + (op - '0');
}
}while (循环条件具体情况具体设置,可以自定义为某一种字符比如常用'=')
2.3.2 关于数字逆序的问题:
可以使用'numb = number % 10'来取数的最后一位,
使用然后使用`digit = digit*10 + numb;`来将得到的数位不断向前进位;
最后要记得对 已经取过的末尾进行取舍`number /= 10;`(这里建议另外建一个变量量来存
储这个输入的数字)
==但是这种做法,对于末尾有零的数字不管用!!!==
**所以提出了另外的针对末尾有零的数字的解决办法:**
* 那就是对末尾的零的数量进行计数,这里引入一个计数的变量;然后其他的情况照常进行,接着引入一个状态变量分别用分支语句区分这两种情况;最后输出的时候利用来两种情况的不同使用分支语句以不同的状态变量为条件来接入不同类型的输出,其中对于有零的情况,加入循环语句借用刚才的对结尾零数量的计数变量,先进行一个的对于零的输出,再去进行对其他数字的输出。
2.4 图形打印问题:
- 关键是要找好排数和具体需要做的动作的关系,比如在打印菱形图形这个题目当中,就要合理利用好排数和需要打印的空格数的关系和需要打印的 * 号的关系。
- 其次更重要的是,找出关系后,打印是一个可以用表达式控制的线性关系的重复过程,所以要学会用函数的方式来解决重复的动作,节省效率。
- 还有就是想解决类型菱形这种上下对称的打印结构的问题的时候,要学会使用好数学上绝对值的概念和定义,找到排数不同但是动作相同的操作的联系性,将二者联系到一起,
比如设置一个相对排数的概念,这样可以使代码逻辑性更强。(具体见下面的PTA实验题目) - 将重复的部分用函数写出来还有一个优势就是,在这种情况下的循环嵌套,可以省去对控制变量进行归零的操作,提高代码的效率。
- 要注意使用控制长度的输出默认为右对齐在左边补零,但是如果数字前面加个符号的话就会变成左对齐,而这在循环嵌套的图形打印问题当中很常见。
2.5 四则运算:
- 为了解决输入过程当中会同时有数字和表达式的问题,可以统一用字符变量来存储,然后在通过前文提到的字符转数字的操作将输入的数字先转化成真正的数字的形式,
然后等输入的是运算符的情况在利用switch-case语句转到对应的运算分支去,当然同样的这里也可以设置成一个函数,然后用运算符作为实参转入函数中进行运算。 - 需要设置另外一个数来慢慢储存已经运算过的数字,所以在这个机制下,要第二个运算符输入的时候,第一次运算才刚好完成。
2.6 关于如何设置输出一串数字(或字符),并让他们用空格间隔开的方法。
* 方法之一是让第一个数字(或字符)提前输出 后面的数字(或字符)在以 空格加数字(或字符) 的形式输出。
(这种方式一般会比较好,比如遇到单词长度这道题目中,有需要考虑一个单词的情况,使用方法二就会爆炸~~)
* 方法之二是反着来,让最后一个数字(或者字符最后输出),然后前面的都用 数字(或字符)加空格 的格式进行输出。
!!这两种离不开的都是一个问题,那就是分支逻辑的处理,那就是怎么分支,其中常用的办法是引入状态变量来作为条件来作为钥匙。
* 相对来说。(可以参考 单词长度这一道题目) 代码如下:
2.7 对循环思维的新理解:
在考虑是否使用循环或者循环嵌套的时候,要先估计到底那一部分的动作是可以靠循环来执行的,其次如果是循环嵌套问题还应该考虑有没有必要把内循环写成一个函数的形式。
3.PTA的实验作业
3.1 数列求和问题:求幂级数展开的部分和
题面如下:
3.1.1 思路介绍:
//数据表达
前一个括号表示数据类型,后一个表示初值;
引入变量 x(double)用来作为输入数据的存储;
引入变量 item (double)(1)用来存放数列的每一个项;
引入变量 sum (double) (0)用来存放数列的各项的和;
引入变量 count(int) (0)用来记数方便次方数的计算;
引入变量 deno (===double=) 用来存储分母的变量;
//流程思路:
start:
scanf 输入x;
for循环:
初值:无;
循环条件:只要item的绝对值比0.00001大那循环就继续进行;
循环控制语句:让计数变量count每一轮循环执行完成后都加一;
循环体的内容:
对分母进行计算:阶乘的计算:deno = deno * count;
设置额外的分支给0的阶乘赋有意义的值为1;
计算每一项的值,并赋值给item;
计算项与项的和,并赋值给sum;
end for;
printf 按照要求输出sum;
end.
3.1.2 代码截图:
3.1.3 PTA提交列表及说明:
- 基本就是数据溢出的问题,最后询问他人后才知道,对于大数的计算,尤其是在循环当中,一定要特别注意由于数据溢出而引起的死循环问题!
3.2 图形打印问题:(打印菱形)
题面如下:
3.2.1 数据处理:
- Doprint函数的具体思路:
- 分别定义两个计算函数count1,count2分别用于对打印空格和 * 号进行控制
- 在本题中规律是相对排数(也就是numb)和需要打的空格数的总和相同是6,
所以可以用这作为循环条件来控制空格的打印。 - 在本题中 * 号打印的规律是其打印个数为相对排数(也就是numb)的两倍减一
所以可以用这作为循环条件来控制 * 号的打印。 - 由于最后需要连续打印出整个菱形所以一次执行完后记得要换行一次。
- 解释一下什么是相对排数,由于菱形上下是对称展开的,所以到中心排数距离相同的排数其实执行的动作是一样的,所以只要引用相对排数把实际排数划归到菱形对应的上三角的动作就行。
具体代码实现如下:
void Doprint(int digit)
{
int count1 = 1;
int count2 = 1;
for (; count1 <= (6 - digit); count1++)
{
printf(" ");
}
for (; count2 <= (2 * digit - 1); count2++)
{
printf("*");
}
printf("\n");
3.2.2 代码截图:
3.2.3 PTA提交列表及说明:
uploading-image-27139.png
前两个是看到这个题目想到当初最早用printf做的一道题想试试这个方法的可行性;
后面的提交直接一次过。
3.3 对龟兔赛跑问题的长久思考引申出来的体会:
题面如下:
3.3.1 单步调试截图:
-
这是基于对过程完全讨论分析的版本
-
这是基于不同对象的不同运动状态进行分析的版本的龟兔赛跑
-
用printf进行调试能够让我们很清晰地去观测这个过程当中产生的数据方便直观地找出逻辑上的问题!
-
同时可以看出来,前一种方法和后一种方法的printf的数据是纯在各自的特殊性的,对过程分析会省略掉被逻辑忽略的,而对状态的过程模拟,则可以得到完整的数据,但是前者效率明显更高,而后者明显更需要更长的计算时间。
3.3.2 代码截图:
* 这是基于不同对象的不同运动状态进行分析的版本的龟兔赛跑
来自于对**智凯学长**的讲解思路的学习
然后评价一下对于这种方法的好处,它可以避开对整个运动过程的十分繁琐的讨论而只要把每个不同的对象在这个过程中的不同状态叙述出来,程序就会进行自动模拟!
* 这是基于对过程完全讨论分析的版本
ps:别问我为啥会有两个版本,这一切来源于我没有发现俺真实的错误是啥,所以这里提出一个检查基于PTA系统下的代码检查的新思路
- 当你对你的逻辑过程已经经过单步调试千万次,但是仍然没有结果的时候!请不妨重新回去读一读题目,想一想你是不是有什么格式上的问题出现失误了!!这里表示很有可能是格式上出问题了,特别的,尤其是当你的测试点报错集中在几个有共同指向性的点的时候,试着去按那个共同点的角度去思考。
- 开始哲学性总结:单步调试更多程度上其实只能帮助我们找出一些已经完成的代码上的逻辑错误,但是并不具备有全局上的一种思考能力,尤其是在格式或者非逻辑要求上出现小错误的时候,单步调试能带给我们的效果就更加微弱了!!
- (划重点!!!)所以作为一个程序员,更重要的是培养自己不仅仅局限与代码编写上的逻辑思维,更要从数学的角度上去,去提高自己宏观上的逻辑思维,能够在看待问题的时候,通过对现有问题的形象进行分析,然后理性地去推到出可能出现的情况!!做到人比机器更灵活是必须要具备的能力!!
3.3.3 PTA提交列表及说明:
大概的整体具体过程如下:
1.能按照整个比赛的过程进行分析写出大概的逻辑情况(在上机实验课上)
2.在上完课回去的路上想到了应该对兔子在睡觉过程中比赛就结束的情况进行讨论,于是有了这里的第一个版本。
3.听完智凯学长的讲解后发现新大陆尝试了对对象进行分析这一新型思维!
4.(后期根据数学学习慢慢延申出结构化思维:即我每次只考虑一层因一层果的平行层的逻辑,然后具体的对某一功能的细化我就不在这一层进行讨论,把概括性的功能定义为函数,这个功能的细化留给设置函数的时候去考虑,我现在不考虑,最后达到的效果是,在主函数内能够看到的是最上层的清晰的思路,使代码更可读,且最后逻辑性加强!)
5.最后是发现报错的测试点都和平局有关,所以去认真研读题目,归因后尝试加上对平局成绩的输出,最后验证猜想成立。问题解决。