7.1 为什么要取名
程序设计中,名字的发明至少在 50 年前。给变量和函数取了合适的名字后,程序的可读性显著提高。由于大部分语言都在使用名字,现在看来,取名似乎是理所当然的事了。
那么名字是缘何发明的呢?为解答这个问题,我们反过来想一下。在名字发明以前,程序员是如何指示现在那些用名字指示的内容的呢?
答案是使用编号 1。计算机记录数据的存储位置可以用编号来替代,于是有了“3456 号的数值加 1”这样的计算机指令。
1也叫做存储地址,即指针变量中的内容。
然而,人们都会觉得给内容或位置取个易于理解的名字,并用该名字来指示它们会更加方便。比如,从书架上取书时,相比使用“取出 3456 号书”或者“取出 ISBN 编号为 978-4-8399-2282-5 的书”来指定,“取出《Python 程序设计》这本书”这样直接使用名字的指定方法要方便得多。相比“把 12345 号放置的内容移到 98765 号位置”,“把桌子上的信封投入家门口的邮筒中”这种说法简洁明了得多 2。因此,程序设计语言也逐渐演变为使用名字来指定对象了。
2同样地,相比“74.125.235.164 的服务器”来说,“google.com”更易于理解,故使用 DNS。相比“纬度 35.693449”来说“东京都新宿区某街”更容易理解,故使用居住地址。
怎样取名
怎么把“取出书架左边第 36 本书”的说法转换成“取出《广辞苑》”呢?如果计算机有名字和内容的对照表就可以了。
{广辞苑 => 第36本,大辞林 => 第37本,新明解国语辞典 => 第38本}
有了上面这样的对照表,计算机在接到“取出《广辞苑》”的指令时,就可以理解成“原来说的是第 36 本书”。
用 Ruby 语言代码来试着创建 x、y、z 三个变量,如下所示:
Ruby
x = "Ruby"
p x.object_id #=> 2152227000
y = "Hello"
p y.object_id #=> 2152220380
z = x
p z.object_id #=> 2152227000
p 相当于其他语言中的 print,其输出结果写在注释符 #=> 的右边。如图 7.1 所示,x 和 z 这两个名字是与 2152227000 号的内容相对应的,而 y 这个名字是与 2152220380 号的内容相对应的。
图 7.1 名字与内容(对象)的对照表

因为 x 和 z 指示同样的内容,将 x 的开头改写为 P 的话,z 也会变成 Puby3。
3这样讲可能有些跑题,将这个例子理解为“变量就是盒子”的话,有些费解吧。
Ruby
x[0] = "P"
p x #=> "Puby"
p z #=> "Puby"
名字冲突
早期的程序设计语言中,对照表是整个程序所共有的,就好比一个公司里有一块大的白板,大家都在上面做记录一样。这里就会有一个很大的问题,我们举例来说明。
下面的代码在变量 i 的值从 0 逐增到 10 的过程中调用函数 shori4。
4从这里开始,我们会时不时使用 Perl 语言的代码来举例说明,这是因为 Perl 语言是可以使用很多种作用域的语言。
Perl
for($i = 0; $i < 10; $i++){
&shori();
print "处理", $i, "结止\n";
}
初看这段代码的人,可能会觉得这里的 for 语句在做完 10 次处理后会终止。其实并非一定如此。
如果在函数 shori 中不小心使用了 i 这个名字会怎样呢?这时 i 的值就被改写了。下面的例子中,每次函数 shori 被调用时变量 i 的值都被重置为 0,for 语句不管循环多少次,变量 i 都不会变为 10。于是就变成了无限循环。
Perl
sub shori{
$i = 0;
}
如何避免冲突
前面提到的“整个程序共用一个对照表”,也就是说,“变量名字的有效范围是整个程序”。这种情况也可以称为“该变量具有全局作用域”,讲得更简洁一些,“这是一个全局变量”。全局变量每次被改写它的影响会波及整个程序。因此,在函数实现时不得不考虑其调用处哪个名字的变量会被改写。上面的例子中,因为函数 shori 中使用了调用处同样使用了的变量 i,程序就陷入了无限循环之中。
要防止变量名不小心重复使用,即要防止名字冲突,该怎么做呢?
取更长的变量名
一种方法就是取更长的变量名。shori 函数中不要使用 i 这样短的名字,而是都换成 i_in_shori 这样的名字,之后名字冲突就不可能发生了吧。也有一些其他方法,比如,在多人共同开发的项目中,采用变量名使用申请制度,或者在变量名中加入开发者的名字,或者在变量名中加入连续的编号使之不重复。
使用作用域
随着程序规模的扩大,防止名字冲突的考量也变得艰难起来。没有人会觉得管理变量的名字是件开心的事情。没有其他更好的方法了吗?于是另一种使用作用域的概念的方法产生了。下一节我们来学习作用域。
