编程序其实就是将人类的思考过程,使用计算机语言描述出来,从而让计算机也能像人类一样去思考(计算)。人类大脑最基本的思考模式就是:将问题分解为若干个步骤,并依次计算→每计算完一个步骤,就记住这一步得到的结果→每个步骤都可能用到前面某一步的计算结果,直至完成最后一步并得到最终答案。在这个过程中,记住中间结果非常重要。
比如,当我们计算“2 2 +3 2 ”这个算式时,会将它分解为三个主要步骤:
① 计算2 2 。得到4这个中间结果并记在大脑内部的某处;
② 计算3 2 。得到9这个中间结果,也将它记在大脑某处;
③ 把在①和②中记住的两个中间结果都取出来并相加,从而得到最终结果13。
程序设计也一样。任何一个复杂的程序,都需要被分解为很多步骤,因此会产生和保存大量的中间结果。如果程序中没有保存某个步骤的结果,当需要使用它时,就不得不将该步骤重新计算一遍,这样写出来的程序代码就会变得很长,也影响效率。
案例2-1: 在图2.1所示的工作表中,存有员工张三的时薪和当月工时,二者相乘就是他的当月工资。现在,公司需要按照他的本月总工资为其发放岗位津贴、当月奖金和劳保补助,所以需要编写一个VBA程序,只要单击“计算本月福利”按钮就能把这三个金额计算并显示在单元格中。计算规则为:岗位津贴=当月工资×20%,当月奖金=当月工资×10 %,劳保补助=当月工资×5%。
图2.1 案例2-1工作表示意
按照前面讲过的Cells属性和算术运算等知识,读者可以很轻松地写出下面的程序,并将其关联到图中的按钮上,正确计算出各项应发福利。
不过在这个程序中,“Cells(4,3)* Cells(4,4)”这段代码多次出现,写起来十分烦琐耗时,也让代码显得非常冗长。这段的含义是根据C4单元格的时薪与D4单元格的本月工时计算出当月工资,以便计算各项福利金额,但是当月工资的计算其实并不需要重复出现在所有语句中。如果让大家计算,一定会先把当月工资(50×100)作为第一个步骤计算出来,得到5000这个数字,将其作为中间结果记在心里或写在纸上。然后每计算一项福利时,就找出这个数字直接使用,而不是重新计算50×100。
那么怎样修改这个程序,使它也能先计算出50×100的结果并记住,从而不必重写呢?使用“变量”就是解决这一问题的最好方法。
随便找一本计算机程序设计专业教材,都可以看到“变量”的学术定义,比如:
变量是指一个包含部分已知或未知的数值或信息(一个“值”)的内存单元,以及一个与之对应的符号名称(标识符) 。
如果读者从未学过程序设计,想必看到这段文字时会觉得不知所云。没关系,下面我们用通俗的语言解释一下变量的含义,虽然不完全精确,但足以帮助大家理解。
其实在执行案例2-1中的VBA程序时,计算机也是分解步骤并记住中间结果的。比如在执行“Cells(4,5)=Cells(4,3)* Cells(4,4)* 0.2”这一语句时,会先计算出 Cells(4,3)* Cells(4,4)的结果,再将其与0.2相乘。
那么计算机是用哪个“部件”记住这个结果的呢?就是大家经常听到的“内存(Memory,直译为‘记忆体’)”这个部件。我们可以把内存想象成由很多个“小房子”排在一起组成的“城市”,每个“小房子”里面都可以存放一个数值。在执行代码时,中间结果就存放在某个“小房子”中,如图2.2所示。
图2.2 内存空间分配示意。图中上方是一个常见的内存
不过问题在于,当计算机执行完“Cells(4,5)=Cells(4,3)* Cells(4,4)* 0.2”这一语句,得到最终结果1000(5000×0.2)并写入E5单元格之后,就自然而然地认为“5000”这个数字已经无用。所以马上就把它从内存中清除掉,腾出空间以便执行后面的计算。
因此,如果想“记住”5000这个会反复用到的重要数字,必须在内存中指定一个“房子”作为“长期居所”。这种房子的特点是:不允许计算机执行完一行代码就自作主张地把它清空,而是要保持记忆。
申请到“长期居所”后,就把程序计算出的“5000”保存到其中,并且在需要的时候直接把它调取出来使用。此外,鉴于一个程序中可能需要指定很多“长期居所”,为了便于区分,我们还要给每个“长期居所”起一个独一无二的名字。
内存中这种由我们亲自命名的“长期居所”就是“变量”。对应到前面那个专业的术语定义中就是:有标识符(起了名字)的内存单元(房子)。
理解了变量的概念,就可以修改案例2-1的代码。假如我们把保存数字5000的这个房子命名为“x”,就可以把上述代码改成下面这样:
这段程序中的“x”就是一个变量。计算机执行第一行代码时发现这是一条赋值语句,并要求用到一个名为“x”的变量,于是就先在内存中指定某个“房子”为“长期居所”,并且将其命名为“x”;然后计算等号右边的部分,得到结果“5000”,并将其保存到这个名为“x”的“房子”中。
在执行接下来的三行代码时,每当在代码中看到“x”变量,计算机就会到内存中找到名为“x”的“房子”,并将其内容(5000)取出用于计算。如此逐条执行代码,直到遇见“End Sub”,结束整个程序。
需要注意的是:“长期居所”不等于“永久居所”,当这个程序全部执行结束时,计算机还是会自动清空在它里面指定的所有变量(如x)。因为没有了程序保留这些变量也无用,所谓“皮之不存,毛将焉附”。
前面从内存单元的角度阐释了变量的概念,旨在让大家理解变量在计算机内部的物理实现方式,为后面更加深入的学习奠定基础。不过如果从程序逻辑的角度看,其实我们早在学习程序设计之前,就已经接触过变量的概念,因为代数这门课程用到的就是“变量”,比如在函数 y=3x+2中,x被称为“自变量”,而y则被称为“因变量”。
x和y的共同特征是可以代表任意一个数值。换言之,x和y只是一个名字,而它们的值可以随需而变,任意赋值。这就是把它们称为“变量”的原因所在。
更进一步来说,对于y=3x+2这种代数式,只要我们为x指定一个数值,就能够按照规定算法计算出对应的y。换句话说,给这个式子输入一个x,就能输出一个y。看到这里大家是否觉得有点眼熟了呢?没错,一个程序甚至一台计算机本身,归根到底与一个代数式没有什么区别:无非就是给它输入一些数据,它就会按照指定规则输出对应的结果。比如在案例2-1中,用户在单元格C4和D4中输入的数字(时薪和本月工时)就是输入,而运行后显示在E4到G4中的数字就是这个程序的输出。而在案例2-1的程序代码中,x 等符号 就相当于代数式中用到的变量。从这个角度讲,一个计算机程序的执行过程本质上就是:
① 接收若干个输入数值,存入变量(如x);
② 对这些变量中的数值按规则进行计算,根据需要将变化后的数值存入不同变量;
③ 所有计算完成后,某个变量中存放的数值就是最终结果,将其输出并结束整个程序。
所以我们在本章开始讲到:从某种意义上说,计算机程序的本质就是“变量及其变化过程”。如果大家对此有兴趣,可以自行科普一下“图灵机”的知识,相信会对计算机体系的本质有更加深刻的感悟。
前面讲了很多变量的本质这种形而上的概念,接下来我们回到实用主义的视角,以案例2-1为例,对比一下使用变量之后程序的代码质量到底有哪些提升,如图2.3所示。
图2.3 案例2-1及其两种解决方案
(1)代码简洁,省时、省力。 这一点可以非常直观地感受到:由于更改后的代码中使用变量x替代了 Cells(4,3)* Cells(4,4)这一串字符,所以可以明显减少敲击键盘的次数,加快开发速度。而且新的代码阅读起来更加方便,不必每读一行都要去思考Cells(4,3)* Cells(4,4)代表什么。
(2)降低了拼写错误的概率。 在更改前的代码中,需要多次重写Cells(4,3)* Cells(4,4)这个复杂的表达式。我们必须保证这些重复书写的表达式完全相同,否则就会造成“数据不一致”这种错误,即计算某个项目时使用的数字,与计算其他项目时所使用的数字不同。
例如,如果将左边代码的第二行误写成Cells(4,6)=Cells(4,3)* Cells(4,3)* 0.1,就等于在计算“当月奖金”时使用的“本月工时”数字是C4单元格的内容50,然而在计算其他两项福利时,使用的“本月工时”仍然是D4单元格中的100。
但在使用变量 x 以后,唯一能够发生这种错误的地方就是 x=Cells(4,3)*Cells(4,4)。而且即使在这里写错,比如误写为 x=Cells(4,3)* Cells(4,3),后面所有用到x的算式也会同步地受到影响,相互之间至少还是一致的。而保持这种“一致性”,在很多场合下十分重要。
(3)便于修改,机动灵活。 讲到“一致性”,就会引出使用变量的另一个优点——便于修改。假如我们对案例2-1中的表格进行了格式调整,把“时薪”的数据从C4单元格改写到A4单元格,把“本月工时”的数据从D4单元格改写到B4单元格,那么就需要对VBA程序也进行修改。
如果没有使用变量,我们需要把每行代码里面的 Cells(4,3)改成 Cells(4,1),把 Cells(4,4)改成Cells(4,2),一共需要修改6处。但是在使用变量x的程序中,我们只需让x=Cells(4,1)* Cells(4,2)即可,一共只要修改两处,后面的语句完全保持不变。
我们学习程序设计的初衷就是希望能够把日常操作写成通用代码,从而一劳永逸,所以这种“稍加修改”甚至“不必修改”就能够适应不同表格结构的编程方法,实在是非常重要。
(4)减少重复计算,提高工作效率。 在没有使用变量的代码中,每行代码都要执行两次乘法运算,整个程序执行了6次乘法运算。而在更改后的代码中,一共执行了4次乘法运算,速度自然有所提高。当然,对于计算机来说,多做两次乘法运算对性能的影响完全可以忽略不计。不过在实际工作中,我们往往需要处理数万条甚至数十万条数据,在这种情况下,如果每次处理可以节省一两次计算操作,整个程序的运行效率将会有非常明显的提升。除了减少乘法操作,使用变量 x后的代码中还大量减少了对Cells属性的使用。而VBA查询Cells(4,3)的数值要比查询变量x多耗费几倍的时间。因此,使用变量对程序性能的提升效果就更加显著了。
所以,请各位初学程序设计的读者牢记第一条重要的VBA编程原则:
把每个将会重复出现的数据定义为变量!