变量
Perl的变量有三种类型:标量(scalar)、数组(array)和哈希(hashes)(译者注:下文会继续使用英文原文scalar、array和hash),每种类型都有属于自己的符号:分别是$
、@
和%
。变量定义使用my
关键字,生命期直到其所在的代码块结束或者文件的末尾。
Scalar变量
一个scalar变量能包含:
undef
(对应Python中的None
、PHP中的null
)- 数值(Perl不区分整形和浮点类型)
- 字符串
- 其他变量的引用。
my $undef = undef;
print $undef; # 打印空字符串"",并且抛出一个警告
# 隐式的undef(译者注:未初始化的变量初值默认为undef):
my $undef2;
print $undef2; # 打印"",并且抛出完全一样的警告
my $num = 4040.5;
print $num; # "4040.5"
my $string = "world";
print $string; # "world"
(稍后会详细说明“引用”。)
用.
运算符进行字符串连接(与PHP一样):
print "Hello ".$string; # "Hello world"
“布尔类型”(“Boolean”)
Perl没有内置的布尔类型。if
语句中的scalar变量仅在以下情况下被认为是“false”:
undef
- 数值
0
- 字符串
""
- 字符串
"0"
。
Perl的文档中反复强调函数在某些情况下返回“true”或者“false”。实际上,当一个函数声称它返回“true”,返回值往往是1
,而当一个函数声称它返回“false”,返回值往往是一个空字符串""
。
弱类型
无法判定一个scalar包含的是一个数值还是字符串。更准确的来说,我们没有必要知道这个信息。一个scalar按照数值还是字符串的方式参与运算,是完全取决于运算符的。因此,像字符串一样使用的时候,scalar就按字符串的方式参与运算,而像数值一样使用的时候,scalar就按照数值的方式参与运算(如果无法转换成数值则会抛出一个警告):
my $str1 = "4G";
my $str2 = "4H";
print $str1 . $str2; # "4G4H"
print $str1 + $str2; # "8" 并且抛出两个警告
print $str1 eq $str2; # "" (空字符串,也就是false)
print $str1 == $str2; # "1" 并且抛出两个警告
# 经典错误
print "yes" == "no"; # "1" 并且抛出两个警告,按数值方式参与运算,两边求值结果都是0
教训是应该总是在恰当的情况下使用正确的运算符,对于比较数值和字符串有两套不同的运算符:
# 数值运算符: <, >, <=, >=, ==, !=, <=>, +, *
# 字符串运算符: lt, gt, le, ge, eq, ne, cmp, ., x
Array变量
Array变量是包含一个scalar列表的、由从0开始的整形数为下标存取的变量。在Python里被称为list,而在PHP里被称为array。数组可以用一个圆括号包围的scalar列表来声明(译者注:原文declaration,而这里实际表达的含义应为“初始化”,而不是对于变量标识符的声明,下同):
my @array = (
"print",
"these",
"strings",
"out",
"for",
"me", # 末尾多余的逗号语法上是允许的
);
你必须要使用美元符号来存取array中的值,因为取到的值是一个scalar而非array:
print $array[0]; # "print"
print $array[1]; # "these"
print $array[2]; # "strings"
print $array[3]; # "out"
print $array[4]; # "for"
print $array[5]; # "me"
print $array[6]; # 返回undef,打印""并且抛出一个警告
你也可以使用负数作为下标,这样就可以从末尾开始往前取某个元素:
print $array[-1]; # "me"
print $array[-2]; # "for"
print $array[-3]; # "out"
print $array[-4]; # "strings"
print $array[-5]; # "these"
print $array[-6]; # "print"
print $array[-7]; # 返回undef,打印""并且抛出一个警告
同时存在scalar变量$var
和包含scalar元素$var[0]
的array变量@var
是没有冲突的,不过会对代码的读者造成一些误导,所以请避免这种情况。
取得array的长度
print "This array has ".(scalar @array)."elements"; # "This array has 6 elements"
print "The last populated index is ".$#array; # "The last populated index is 5"
调用Perl脚本时使用的参数列表被保存在内置的array变量@ARGV
中。
变量可以被插入到字符串中被求值:
print "Hello $string"; # "Hello world"
print "@array"; # "print these strings out for me"
小心。也许有一点你会把某个人的email地址放在一个字符串里,比如"jeff@gmail.com"
。Perl会去尝试找一个名叫@gmail
的array变量,求值并插入到字符串中,如果没有找到这个变量,将会导致一个运行时错误。有两种方法可以避免对字符串中的变量名求值:用反斜杠对@
进行转义,或者将双引号改为单引号。
print "Hello \$string"; # "Hello $string"
print 'Hello $string'; # "Hello $string"
print "\@array"; # "@array"
print '@array'; # "@array"
Hash变量
Hash变量是包含一个scalar列表的、由字符串为下标存取的变量。在Python中被称为dictionary,而在PHP中被称为array。
my %scientists = (
"Newton" => "Isaac",
"Einstein" => "Albert",
"Darwin" => "Charles",
);
请注意这个声明与array何其相似。事实上,这个双箭头符号=>
被称为“fat comma”(胖逗号),因为它与逗号完全等价。Hash变量由偶数个元素组成的列表来声明,其中偶数下标(0、2、……)的元素都被当做字符串使用。
与array一样,你也需要用美元符号来存取hash中的值,因为取到的值是scalar而非hash:
print $scientists{"Newton"}; # "Isaac"
print $scientists{"Einstein"}; # "Albert"
print $scientists{"Darwin"}; # "Charles"
print $scientists{"Dyson"}; # 返回undef,打印""并且抛出一个警告
注意在这里使用的花括号。同样的,同时存在scalar变量$var
和包含scalar元素$var{"foo"}
的hash变量%var
是没有冲突的。
你可以将一个hash转换为两倍数量元素的array,原先hash中的键和值在转换后的array中交替出现(反向的转换也同样简单):
my @scientists = %scientists;
然而有一点与array不同,hash中的键没有特定的保存顺序,而是以一种比较高效的方式进行存储。因此,需要注意转换后的array会将hash中的键值对重新排列次序:
print "@scientists"; # 输出可能是"Einstein Albert Darwin Charles Newton Isaac"
回顾一下,我们使用方括号来取array中的值,而使用花括号来取hash中的值。方括号是一个有效的数值运算符,而花括号是一个有效的字符串运算符,因此事实上,作为下标的值是数值还是字符串类型其实并不重要(译者注:正如前文所提到的,scalar以什么方式参与运算取决于运算符):
my $data = "orange";
my @data = ("purple");
my %data = ( "0" => "blue");
print $data; # "orange"
print $data[0]; # "purple"
print $data["0"]; # "purple"
print $data{0}; # "blue"
print $data{"0"}; # "blue"
列表(Lists)
Perl中的列表与array和hash都不一样。你已经见过一些列表了:
(
"print",
"these",
"strings",
"out",
"for",
"me",
)
(
"Newton" => "Isaac",
"Einstein" => "Albert",
"Darwin" => "Charles",
)
列表不是一个变量列表是一个暂存的值,可以被赋值到一个array或者hash变量,这就是为什么声明array和hash的语法竟完全一样。在许多情况下“列表”和“array”这两个词可以混用,而在同样多的情况下,列表和array表现出微妙的区别,并且具有极其容易混淆的行为。
好的,回想一下=>
只是,
的一种伪装,然后看一下下面的例子:
("one", 1, "three", 3, "five", 5)
("one" => 1, "three" => 3, "five" => 5)
使用=>
暗示了其中一个是hash的声明(译者注:第二个),而另一个是array的声明,但就这两个列表自身并没有声明任何东西,它们只是列表,而且是完全相同的列表。同样的:
()
这里甚至没有任何变量类型的提示,这个列表可以用来声明一个空array或者空hash,而作为perl
解释器则完全无法知道将会是哪一种。一旦你理解了这一点,你也就能理解Perl的这个事实:列表不能嵌套。我们可以试一下:
my @array = (
"apples",
"bananas",
(
"inner",
"list",
"several",
"entries",
),
"cherries",
);
Perl无法知道("inner", "list", "several", "entries")
应该是array还是hash,因此Perl假设两者都不是,而将其扁平化为一个一维长列表:
print $array[0]; # "apples"
print $array[1]; # "bananas"
print $array[2]; # "inner"
print $array[3]; # "list"
print $array[4]; # "several"
print $array[5]; # "entries"
print $array[6]; # "cherries"
即使使用fat comma也会是同样的情况:
my %hash = (
"beer" => "good",
"bananas" => (
"green" => "wait",
"yellow" => "eat",
),
);
# 上面的代码会抛出一个警告,因为我们尝试用7个元素的列表来初始化这个hash
print $hash{"beer"}; # "good"
print $hash{"bananas"}; # "green"
print $hash{"wait"}; # "yellow";
print $hash{"eat"}; # undef,因此打印""并且抛出一个警告
当然,这倒让连接数组变得简单了:
my @bones = ("humerus", ("jaw", "skull"), "tibia");
my @fingers = ("thumb", "index", "middle", "ring", "little");
my @parts = (@bones, @fingers, ("foot", "toes"), "eyeball", "knuckle");
print @parts;
稍后会有更多关于这个问题的说明。