4.2 循环结构
在上一节中,我们曾经对D2单元格的成绩进行判断,并用“及格”或者“不及格”在E2单元格做出了评价。不过代码的缺陷仅仅是对一个单元格进行判断,如果在工作表中有若干行的记录,显然不可能一次一次地修改代码,来完成任务,必须给程序一定的条件,然后让程序自动的重复判断过程。VBA为我们提供了这样的功能,这就是循环结构。
4.2.1 For……Next循环
For……Next循环语句是最常用的循环语句,它的语法结构如下所示。
For变量=初值To结束值 [Step步长]
循环体内操作
Next [变量]
在For……Next结构中,变量起到了计数器的作用,能够按照指定的步长自动进行计数。上述结构中方括号中的内容在特定条件下可以省略。
1. 循环结构
For……Next循环语句结构中,步长如果是1可以省略,Next关键字后也可以省略计数器的名称。
例4-07:在如图4-1所示的表中显示了各门成绩,要求编写代码,根据D列的平均成绩给出及格与否的评价。
图4-1 成绩表
#001: Sub成绩评价() #002: Dim i As Integer #003: For i = 2 To 6 #004: If Range("d" & i) < 60 Then #005: Range("e" & i) = "不及格" #006: Else #007: Range("e" & i) = "及格" #008: End If #009: Next i #010: End Sub
第2行代码声明了一个变量i,该变量就是计数器。第3行代码表示进入循环的条件,当变量i的值位于2到6之间的时候,也就是i大于等于2并且小于等于6的时候,都会执行循环体内的操作。
第4行到第8行代码就是循环体内的操作过程。第4行代码中Range("d" & i)表示D列中某一个单元格,当i等于2的时候,表示D2单元格,将该单元格的值和60进行比较。如果指定的单元格的值小于60,那么就会执行第5行代码,将E2单元格的值更改为“不及格”,否则就将执行第7行,将其值更改为“及格”。
第9行代码表示一次循环结束,计数器i的值自动增加一个步长1,i的值变为了3。然后返回第3行代码,判断i的值是否仍然处于2到6之间,如果符合For所指定的条件,那么还将执行一次循环体中的操作过程,此时判断的单元格Range("d" & i)表示D3单元格。
循环过程要进行到i的值不符合循环条件为止。上述代码的运行结果如图4-2所示。
图4-2 成绩判断结果
说明
如果步长为1,那么第3行代码“Step 1”可以省略。第9行代码中“Next i”可以表示为“Next”,其中变量i可以省略。
2. 步长
步长是计数器每次自动增加值的大小,如果省略步长,则表示步长的值为1。步长可以是正数,可以是负数,但不能为0。巧妙地使用步长有时候可以起到简化程序的效果。
例4-08:从A1单元格开始从输入数据1到100,删除该数据列中行号为偶数的行。
#001: Sub删除行号为偶数的行() #002: Dim i As Integer #003: Dim s As Integer #004: For i = 100 To 1 Step -1 #005: s = Range("a" & i).Row #006: If Int(s / 2) = s / 2 Then #007: Rows(i).Select #008: Selection.Delete Shift:=xlUp #009: End If #010: Next #011: End Sub
第2行代码声明了变量i用来表示计数器,第3行代码声明了变量s用来行号。第4行到第10行代码构成了一个循环,其中该循环是从100开始,终值是1,步长为-1,也就是说每执行一次循环,变量的值就要减1。
第5行代码表示对将A列指定的单元格的行号赋值给变量s,Row是单元格的一种属性,表示单元格的行号。第6行代码将判断该行号是否为一个偶数,如果为一个偶数,那么将执行第7行代码。“Rows(i).Select”表示选中指定行。第8行代码表示删除指定行,这些都是对工作表的基本操作。有关这方面的内容将在后续章节中详细介绍,此处用户做一个了解。
说明
如果循环过程从第1行开始到第100行结束,步长为1,那么执行判断并删除指定的行就会变得相当的困难。
4.2.2 For Each……Next循环
在上节介绍的For……Next循环中,循环的次数在循环开始的时候就已经指定了。例如“For i = 100 To 1 Step -1”就知道循环要进行100次。但是在有的时候,进入循环的时候并不确切地知道将要执行循环的次数,解决的方法之一就是可以用For Each……Next循环结构。
使用For Each……Next的语法结构为:
For Each成员变量名In集合
循环体内操作步骤
Next
例4-09:在图4-1中给出了一张成绩表,在该成绩表中,凡是语文、数学或者平均成绩小于60的都用红色字体表示。
#001: Sub不及格成绩用红色表示() #002: Dim rng As Range #003: For Each rng In Range("b2:d6") #004: If rng < 60 Then #005: rng.Font.Color = vbRed #006: End If #007: Next rng #008: End Sub
第1行代码声明了一个单元格对象变量rng,该变量表示的是一个单元格。第2行代码中Range("b2:d6")表示B2到D6单元格区域,也就是单元格的集合,rng是一个单元格,表示的是该集合中的一个单元格,可以用“For Each……Next”循环来遍历整个单元格区域。用这种方式进行循环操作时,不必知道循环进行的次数。
第4行代码将集合中某一个单元格和60进行比较,如果该值小于60,那么就执行第5行代码,将单元格的字体颜色更改为红色,红色可以用vbRed这样的形式来表示。“rng.Font.Color”表示某个单元格的字体颜色。
说明
和“For……Next”结构类似,Next后跟的变量名可以省略,因此第7行代码也可以写成“Next”。
4.2.3 Do……Loop循环
Do……Loop的循环结构和For……Next的循环结构其最大的区别在于:For……Next循环结构中要指明计数器的起点和终点,是判断计数器是否达到指定的数值为条件的,但是Do……Loop循环中则是以满足或者不满足某个指定条件为基础进行循环的条件,这个条件有时候和数值无关。Do……Loop循环通常有两种判断条件的方式,分别是使用While和Until来判断条件。
1. 使用While判断条件
使用While进行条件判断也有如下的两种类型分别是前测型循环结构和后测型循环结构,它们的区别在于判断条件是放在Do后还是放置在Loop后,如果放置在Do后就属于前测型循环结构,如果放置在Loop后就属于后测型循环结构。
方式一前测型循环结构:
Do [While条件表达式]
循环体内操作步骤
Loop
方式二后测型循环结构:
Do
循环体内操作步骤
Loop [While条件表达式]
从字面上来看,Do的含义表示要执行某种操作,在第一种方式前测型循环结构中,Do首先要做的就是进行条件判断,如果条件表达式的结果为True,那么就会执行循环体中的过程。在第二种方式下Do关键字后没有内容,因此它首先要做的就是执行循环体内的过程,然后到了Loop一句的时候再执行条件判断,如果Loop后的条件表达式结果为True,那么仍然要回到循环体完成执行循环提内的过程。
由此也可以前测型和后测型两种循环结构是有区别的。在Do……Loop While的结构中肯定会经过一次循环,但是Do While……Loop的结构中有可能一次循环也不进行。
例4-10:根据图4-1所示的表格,要求找出D列中平均成绩的最高值。循环结构要求使用Do While……Loop。
#001: Sub查找最高分() #002: Dim i As Integer #003: Dim MaxValue As Single #004: i = 2 #005: Do While i <= 6 #006: If Range("d" & i) > MaxValue Then #007: MaxValue = Range("d" & i) #008: End If #009: i = i + 1 #010: Loop #011: MsgBox "平均成绩最高分是:" & MaxValue #012: End Sub
第2行代码声明了变量i,该变量表示行号。第3行代码声明了变量MaxValue,该变量用来放置最高成绩。第4行代为变量i赋初值,由于是从第2行开始判断,因此行号的初值就是2。第5行到第10行是循环结构。第5行代码首先会对i行号进行判断,如果该行号小于等于6,那么将允许进入循环体。提6到第8行代码是计算最大值的过程。第6行代码中“Range("d" & i)”表示D列中的单元格,该行代码的含义是如果D列中的指定单元格的值大于最大值,那么就执行第7行代码,将单元格中的值赋给变量MaxValue。第9行代码用将变量i加1,然后通过Loop继续回到第5行参与对变量i的判断。如果i仍旧是小于等于6,那么还可以执行循环体内的操作。第11行代码将最高成绩用消息框的方式显示出来。
说明
上述代码所使用的Do While……Loop结构代码也可以换成Do……Loop While的结构,则其代码如下所示。
Sub查找最高分 () Dim i As Integer Dim MaxValue As Single i = 2 Do If Range("d" & i) > MaxValue Then MaxValue = Range("d" & i) End If i = i + 1 Loop While i <= 6 MsgBox "平均成绩最高分是:" & MaxValue End Sub
要注意的是上述两种代码中由于判断条件的先后问题,最终的运算结果有可能是不一样的。
2. 使用Until判断条件
除了可以使用While进行条件判断的关键字外,还可以使用Until进行条件判断。使用Until判断时也会存在前测型和后测型两种循环结构。用如下的两种格式来表示:
方式一前测型判断结构:
Do [Until判断条件]
循环体内操作步骤
Loop
方式二后测型判断结构:
Do
循环体内操作步骤
Loop [Until判断条件]
和While进行条件判断的方法不同。当使用While进行判断的时,判断的条件满足,那么即可进入循环。与之相反的是,使用Until进行条件判断的时候,当条件满足时,就不执行循环体中的操作,含义是“直到满足指定条件时就不执行循环操作”。
例4-11:根据图4-1所示的表格,要求找出D列中平均成绩的最高值。循环结构要求使用Do Until……Loop。
#001: Sub查找最高分() #002: Dim i As Integer #003: Dim MaxValue As Single #004: i = 2 #005: Do Until i > 6 #006: If Range("d" & i) > MaxValue Then #007: MaxValue = Range("d" & i) #008: End If #009: i = i + 1 #010: Loop #011: MsgBox "平均成绩最高分是:" & MaxValue #012: End Sub
上述代码和例4-10中所使用的代码大致相同,此处不再做详细解释,两段代码不同之处发生在第5行。本例判断的条件刚好和使用While相反,使用Until实际上描述的是不进入循环的条件。当行号比6大的时候,循环就结束了,因此第5行代码中使用了“Do Until i > 6”。
说明
上述代码所使用的Do Until……Loop结构代码也可以换成Do……Loop Until的结构,则其代码如下所示。
Sub查找最高分() Dim i As Integer Dim MaxValue As Single i = 2 Do If Range("d" & i) > MaxValue Then MaxValue = Range("d" & i) End If i = i + 1 Loop Until i > 6 MsgBox "平均成绩最高分是:" & MaxValue End Sub
4.2.4 While……Wend循环
While……Wend循环的用法和前测型的Do While……Loop循环相同。其语法结构为:
While条件表达式
循环体内操作步骤
Wend
当条件表达式的结果为True的时候,就进入循环体。
例4-12:根据图4-1所示的表格,要求找出D列中平均成绩的最高值。循环结构要求使用While……Wend。
#001: Sub查找最高分() #002: Dim i As Integer #003: Dim MaxValue As Single #004: i = 2 #005: While i <= 6 #006: If Range("d" & i) > MaxValue Then #007: MaxValue = Range("d" & i) #008: End If #009: i = i + 1 #010: Wend #011: MsgBox "平均成绩最高分是:" & MaxValue #012: End Sub
和例4-10的代码相对照,可以发现除了第5行和第10行代码不同外,其余都是相同的。在本例中第5行代码给出了进入循环的条件,也就是变量i的值要小于等于6。
4.2.5 循环出口
在执行循环体的过程中,如果满足一定的条件,那么可以提前跳出循环,接着执行循环体后的语句,这就是循环出口语句。循环出口语句有两种。
● Exit For:用于以For作为循环入口的循环结构中。
● Exit Do:用于以Do作为循环入口的循环结构中。
要提前跳出循环通常还会用到条件判断语句,这些条件判断包括而来If结构和Select Case结构的判断语句。
例4-13:假设有一项初始投资为5万元,投资最高期限为20年,预计投资将会在每年产生10%的收益,这些收益将会再次用于投资额中,当投资总额超过10万元的时候即完成投资目标。根据上述条件判断到第几年的时候就可以完成投资目标。
#001: Sub投资年数确认() #002: Dim n As Integer #003: For n = 1 To 20 #004: If 5 * (1 + 0.1) ^ n => 10 Then #005: MsgBox n #006: Exit For #007: End If #008: Next #009: End Sub
第2行代码声明了变量n表示年份数,第3行代码就是投资的最长期限,第4行代码判断第n年的投资总额是否已经超过了10万元,如果已经超过了10万元,那么第5行代码就将n的值通过消息框显示出来,并通过第6行代码提前退出循环,不再参与运行其他循环过程。上述代码的最终结果是8。
4.2.6 循环嵌套
在VBA中有时候仅仅靠简单的循环无法完成操作,此时会用到更加复杂的循环形式,那就是循环嵌套,所谓循环嵌套就是在一个循环中还包含有另一个循环。
在Excel VBA中用户会经常访问到工作表中的单元格区域,这些单元格区域是有若干行和列构成的,此时如果要遍历单元格区域中每个单元格,除了可以用For Each……Next这种结构外,也可以用循环嵌套的方式来访问。
例4-14:在图4-1中给出了一张成绩表,在该成绩表中,凡是语文、数学或者平均成绩小于60的都用红色字体表示。要求使用循环嵌套来完成代码。
在图4-1中,成绩部分是从第2行开始到第6行结束,列是从第2列开始到第4列结束。表示某个单元格除了可以使用Range(”D2”)这种方式外,还可以使用Cells(行号,列号)来表示,例如D2单元格就是第2行第4列,表示为Cells(2,4)。
用循环嵌套的方法将不及格成绩用红色标示出来的代码如下所示。
#001: Sub循环嵌套演示() #002: Dim i As Integer #003: Dim j As Integer #004: For i = 2 To 6 #005: For j = 2 To 4 #006: If Cells(i, j) < 60 Then #007: Cells(i, j).Font.Color = vbRed #008: End If #009: Next #010: Next #011: End Sub
第2行代码和第3行代码声明的变量i和j分别表示行号和列号。第4行代码和第10行代码构成了一个For……Next循环,表示从指定的第2行开始进行循环。第5行代码和第9行代码也构成了一个循环,表示在行号一定的时候,列号从第2列变动到第4列。第6行代码就是对指定的行号和列号的单元格的值和60进行比较,如果该值小于60成立,那么就会运行第7行代码,将该单元格的字体颜色更改为红色。上述代码的运行最终结果能够将图4-1中所有不及格的成绩都以红色来表示。
观察上述代码就使用了循环嵌套结构,它是由两个循环构成的,处于外部的是以行号i为标志的是外循环,处于内部的是以列号j为标志的是内循环。循环嵌套结构中,从外循环进入,然后执行完整个内循环过程,然后再次进行一次外循环,然后再次执行整个内循环过程,直到外循环不符合条件,不再进行循环为止。
就本例的代码而言,当开始外循环的时候,行号的值i为2,然后进入到第5行代码内循环过程,此时列号的值j为2,第6行代码中Cells(2,2)表示第2行第2列的一个单元格,将该单元格和60相比较;然后再次执行内循环过程。此时行号仍旧为2,列号变为了3,代表的单元格为Cells(2,3)。当内循环列号的值由2变到4都完成了以后,接着执行外循环,行号变为3,列号又从2开始到4为止,这样反复直到外循环也结束才结束了整个循环过程。
说明
将单元格表示成Cells(行号,列号)的方式更加有利于循环的控制,因此在今后的代码中会大量地采用这种表示方式,而不是使用类似Range(”D2”)的方式。