编程除了要处理条件分支语句涉及的逻辑问题外,也经常要反复去做一系列内容相近的工作。
例如处理一堆报表数据,其流程就是依次读取数据的每一行,计算后,得到新的数据再写入到新的数据表里,然后将新数据表保存起来。
我们描述这样的工作总是很简单,因为人类的大脑会将真正重要的东西转变为潜意识,这样你在理解一件事情的时候,就会只专注于不理解的那一部分了,但这样会带来一个新的问题,就是在理解类似编程这种原理性的问题是,会变得很复杂。
正如爱因斯坦所说,如果一件事情你不能用简单的话把它描述出来,那说明你没有很好的理解它。
循环就是这样一件事,在我带过的很多新人里,绝大多数都患有循环懵圈症,一看到复杂的循环代码,当场放弃治疗。
那么要怎么理解循环呢?首先我们要挖掘出潜意识里包含的东西,例如我么刚才轻描淡写的那一句:依次读取数据的每一行。
当然,多数工作人员在和你描述这个动作的时候,可能更糟糕,例如:把数据全都读出来。
为什么这种描述更糟糕呢?因为它会让你的潜意识觉得,数据是一瞬间一次性全都读出来的,一旦陷入了这种思维陷阱,之后的流程就很难理解了。
试问,让你的脑袋去一次性把数据全都读出来,能做到吗?显然真正的工作流程并不是这样的,对吧?
真正的流程是这样的:
1. 首先我们必须要打开一个 Excel 工作簿,然后才能读取表格数据吧?
2. 接下来我们得确定要读区哪一个工作表才行吧?不然一个文件三个工作表,我们读错了怎么办呢?
3. 然后,你在读 Excel 表格的时候,是怎么定位数据的?列头?行号?至少得有个行和列吧?
4. 如果需求是把工作表的数据全都读出来,你是怎么做的?从第一行开始,一行一行的看,然后每一行你还得一列一列的看对吧?
你看,这才是真正的流程,而流程处理能力差的人,可能轻描淡写的用:把数据全都读出来,就给概括了,一旦陷入这种思维,你就很难学会编程了。
电脑可以理解为一种仿生的大脑,你必须更好的理解你的大脑是如何工作的,把每一个步骤都干了什么清晰准确的分析出来,然后再开始。
因为了解这种差异,所以这一篇教程我明明可以简单的告诉你什么是循环,语句怎么写,达成了什么样的效果,但还是要洋洋洒洒的写一大堆。
只有充分理解了这些东西,我才认为,你具备了学习循环的基础:循环并不是一次性把活全都干完的。
上面的内容,是需要思考乃至融会贯通的,接下来我们要了解一个循环包含了哪些元素,这些内容很简单,死记硬背即可。
一系列类似的事情反复做,就是循环,因此循环必须有要做的事。
这些事是类似的或者完全一致的,如果是类似的事,你必须找出它们的不同点。
比如我们上面举的例子,读 Excel 工作表,每次只能读一个单元格,所以我们做的并不是完全一样的事,而是存在变数的,这个变数就是单元格的行和列,第一次读第一行,第二次读第二行,以此类推……
然后是我循环往复的做一件事情,有没有尽头?比如读 Excel 工作表,一共就3000行,读完了我就不读了,或者我要监控一个东西,永远无休止的监控下去,前者我们一般认为是一个正常的循环,后者我们认为是死训话,不是说死循环不正常啊,而是很多程序员在写程序的时候,因为个人原因,把一个正常的循环给写成死循环了,导致程序卡死永远都出不来,这也是很多新手经常犯的错误,所以这块逻辑一定要盘明白。
现在我们总结归纳一下,循环有哪些要点:
1. 循环的工作内容是不是完全相同的,如果不是,找出所有不同的部分。
2. 循环的结束条件是怎样的,确定好结束条件,确实没有结束条件的写死循环。
就这么简单,循环的内核其实就这两条,剩下的东西,都是基于这两条扩展出来的,比如多数编程语言都有两种循环语句 do 和 for,他们的内核其实都是完全相同的,只不过一个是用次数作为结束条件了,一个还是用判断做结束条件。
For 循环一般被称作【计次循环】,这种循环的目的性很强,在学习初期用的比较多,它的写法如下:
// 代入的循环变量在外面定义 Dim i As Long For i = 1 To 10 Step 1 TracePrint i Next // 代入的循环变量直接在循环内定义 For i As Long = 1 To 10 Step 1 TracePrint i Next
这两段代码的含义是一样的,区别只在于循环内定义变量 i,还是循环前面定义变量 i,循环内直接使用。
这段代码的含义是,循环从 1 到 10,一共执行10次,分别为:1、2、3、4、5、6、7、8、9、10。
后面的 Step 1 就是每次执行数值累加多少,如果写为下面的代码:
// 代入的循环变量直接在循环内定义 For i As Long = 1 To 10 Step 2 TracePrint i Next
虽然还是 1 To 10,但只会循环内5次,分别为:1、3、5、7、9。
这个 Step 2 和上面写的 Step 1 都是可选的,如果不写,默认为 Step 1。
为社么我要专门说出这些数字序列呢?因为这些数字会在【每次循环】执行的时候代入给循环变量 i,当然,这个 i 只是个变量名,你可以改成任何名字,就像其他变量一样。
所以 For 语句的逻辑到这里就很简答了吧?
定义一个循环变量【 i 】(你可以随便改成任何名字),然后将它的值代入为 1 To 10 范围内的数值(每次累加 Step 语句指定的数值)
每代入一个数值,就会执行一遍 For 语句第二行到 Next 语句之间的代码,当代码执行到 Next 的时候,会跳回到 For 语句,重新代入一个新的数值,如果新的数值超出了 1 To 10 这个范围,则循环结束,代码跳转到 Next 后面继续执行。
For 语句的语法规则:
For 循环变量 = 起始值 To 结束值 Step 步进值 循环代码块 Next
【Step 步进值】是可选的,其他部分都是必须的。
接下来让我们做一个测验题:
循环弹出 5 个窗口,每次弹出的窗口,内容都是【消息内容 x】,把 x 替换为 10、5、0、-5、-10。
分析这个需求我们总结的循环内相同点为:执行 TracePrint 命令,参数的消息模板为【消息内容 x】。
循环内的不同点为:每次弹出的数字不一样,分析规律为每次减少5。
于是我们可以使用这样的循环代码解决问题:
For i As Long = 10 To -10 Step -5 MsgBox("消息内容 " & i) Next
这段代码在执行时,第一次将 i 代入初始值 10,然后依次减少 5,最终在结束值 -10 之后的下一次循环(-15)的时候判断失败跳出循环。
所以中间的 MsgBox 命令一共分别执行了5次,每一次执行的时候,变量 i 的值都会产生上一句说的变化。
所以实际上代码的执行顺序是这样的:
1. Dim i As Long (先定义循环变量)
2. For i = 10 To -10 Step -5 (第一次循环头判断,循环变量 i 代入初始值 10)
3. MsgBox("消息内容 " & i) (执行 MsgBox 函数,现在 i 的值是 10)
4. Next (Next的意思就是让循环变量加上Step值,所以是 10 + -5,等于5)
5. For i = 10 To -10 Step -5 (这一次循环,循环变量等于 5 了,还没到最终值,所以循环继续)
6. MsgBox("消息内容 " & i) (执行 MsgBox 函数,现在 i 的值是 5)
7. Next (循环变量继续 -5,现在是 0 了)
8. For i = 10 To -10 Step -5 (循环变量为 0,还没超过最终值,循环继续)
9. MsgBox("消息内容 " & i) (执行 MsgBox 函数,现在 i 的值是 0)
10. Next (循环变量继续 -5,现在是 -5 了)
11. For i = 10 To -10 Step -5 (循环变量为 -5,还没超过最终值,循环继续)
12. MsgBox("消息内容 " & i) (执行 MsgBox 函数,现在 i 的值是 -5)
13. Next (循环变量继续 -5,现在是 -10 了)
14. For i = 10 To -10 Step -5 (循环变量为 -10,还没超过最终值,循环继续)
15. MsgBox("消息内容 " & i) (执行 MsgBox 函数,现在 i 的值是 -10)
16. Next (循环变量继续 -5,现在是 -15 了)
17. For i = 10 To -10 Step -5 (循环变量为 -15,已超过最终值,循环结束,会继续执行 Next 之后的代码)
必须理解循环的本质也是顺序执行这一点,这非常重要,所以,循环内也可以再嵌套一个循环,循环可以有好几层,但那也是一行一行执行的。
另外,对循环变量的值预测也非常重要,例如我们稍微改变一下这段代码:
For i As Long = 10 To -10 Step -5 MsgBox("消息内容 " & i) Next MsgBox i
当代码执行到第四行的时候,会弹出什么内容呢?看我们上面的数据步骤分析,第 16 步,循环变量 i 的数值为 15 对吧?运行一下这段代码,相互印证一下自己的猜测是否正确。
相比 For 语句,Do 语句更忠于循环的本质(所以一开始学习的时候也更难用),没有那么多东西,直接一个条件决定是否循环。
越简单的东西,本质上越复杂,Do 语句虽然只通过一个数据就能实现循环,但实际上在运行时数据的变化导致的变数更多。
所以实际使用起来,是比 For 语句更复杂一些的,在刚学编程初期,可以只记 For 语句,For 语句无法满足需求之后再回来拾遗。
Do 语句一共有 5 种写法:
// 死循环 Do 循环代码 Loop // 前置判断为真循环 Do While 判断条件 循环代码 Loop // 前置判断为假循环 Do Until 判断条件 循环代码 Loop // 后置判断为真循环 Do 循环代码 Loop While 判断条件 // 后置判断为假循环 Do 循环代码 Loop Until 判断条件
第一种写法是死循环,循环代码会不断执行,直到在循环内运行跳出循环或跳出命令等函数退出循环。
第二种和第三种写法是前置循环,前置循环的特点为在执行循环代码之前进行逻辑判断,如果逻辑不成立,则可能一次循环代码都不会执行。
第四种和第五种写法是后置循环,后置循环的特点为循环代码至少会执行一次,执行后再判断逻辑,确定接下来要不要继续循环执行。
循环难理解,主要在于正确理解了【循环不是一瞬间把所有事情干完的,而是一次又一次的,逐步把事情做完的】,理解了这一点之后,不管是计次循环还是条件循环,都可以应用自如了。
所以我没有在 Do 语句上浪费太多笔墨,当你能够正确理解 For 的时候,Do 语句不过是看一眼就能明白怎么回事的东西。
在循环代码内,如果执行到一半不想继续循环了,或者执行的是一个死循环,现在不想继续执行下去了,可以运行代码直接跳出循环。
跳出 For 循环的写法为:Exit For
跳出 Do 循环的写法为:Exit Do
在循环代码内,如果你希望跳过这次循环未执行到的代码,直接进入下一轮循环,可以使用代码立刻开始下一轮循环。
进行下一轮 For 循环的代码为:Continue For
进行下一路 Do 循环的代码为:Continue Do