变量

Perl的变量有三种类型:标量(scalar)数组(array)哈希(hashes)(译者注:下文会继续使用英文原文scalar、array和hash),每种类型都有属于自己的符号:分别是$@%。变量定义使用my关键字,生命期直到其所在的代码块结束或者文件的末尾。

Scalar变量

一个scalar变量能包含:

  • undef(对应Python中的None、PHP中的null
  • 数值(Perl不区分整形和浮点类型)
  • 字符串
  • 其他变量的引用。
  1. my $undef = undef;
  2. print $undef; # 打印空字符串"",并且抛出一个警告
  3. # 隐式的undef(译者注:未初始化的变量初值默认为undef):
  4. my $undef2;
  5. print $undef2; # 打印"",并且抛出完全一样的警告
  1. my $num = 4040.5;
  2. print $num; # "4040.5"
  1. my $string = "world";
  2. print $string; # "world"

(稍后会详细说明“引用”。)

.运算符进行字符串连接(与PHP一样):

  1. print "Hello ".$string; # "Hello world"

“布尔类型”(“Boolean”)

Perl没有内置的布尔类型。if语句中的scalar变量仅在以下情况下被认为是“false”:

  • undef
  • 数值0
  • 字符串""
  • 字符串"0"

Perl的文档中反复强调函数在某些情况下返回“true”或者“false”。实际上,当一个函数声称它返回“true”,返回值往往是1,而当一个函数声称它返回“false”,返回值往往是一个空字符串""

弱类型

无法判定一个scalar包含的是一个数值还是字符串。更准确的来说,我们没有必要知道这个信息。一个scalar按照数值还是字符串的方式参与运算,是完全取决于运算符的。因此,像字符串一样使用的时候,scalar就按字符串的方式参与运算,而像数值一样使用的时候,scalar就按照数值的方式参与运算(如果无法转换成数值则会抛出一个警告):

  1. my $str1 = "4G";
  2. my $str2 = "4H";
  3. print $str1 . $str2; # "4G4H"
  4. print $str1 + $str2; # "8" 并且抛出两个警告
  5. print $str1 eq $str2; # "" (空字符串,也就是false)
  6. print $str1 == $str2; # "1" 并且抛出两个警告
  7. # 经典错误
  8. print "yes" == "no"; # "1" 并且抛出两个警告,按数值方式参与运算,两边求值结果都是0

教训是应该总是在恰当的情况下使用正确的运算符,对于比较数值和字符串有两套不同的运算符:

  1. # 数值运算符: <, >, <=, >=, ==, !=, <=>, +, *
  2. # 字符串运算符: lt, gt, le, ge, eq, ne, cmp, ., x

Array变量

Array变量是包含一个scalar列表的、由从0开始的整形数为下标存取的变量。在Python里被称为list,而在PHP里被称为array。数组可以用一个圆括号包围的scalar列表来声明(译者注:原文declaration,而这里实际表达的含义应为“初始化”,而不是对于变量标识符的声明,下同):

  1. my @array = (
  2. "print",
  3. "these",
  4. "strings",
  5. "out",
  6. "for",
  7. "me", # 末尾多余的逗号语法上是允许的
  8. );

你必须要使用美元符号来存取array中的值,因为取到的值是一个scalar而非array:

  1. print $array[0]; # "print"
  2. print $array[1]; # "these"
  3. print $array[2]; # "strings"
  4. print $array[3]; # "out"
  5. print $array[4]; # "for"
  6. print $array[5]; # "me"
  7. print $array[6]; # 返回undef,打印""并且抛出一个警告

你也可以使用负数作为下标,这样就可以从末尾开始往前取某个元素:

  1. print $array[-1]; # "me"
  2. print $array[-2]; # "for"
  3. print $array[-3]; # "out"
  4. print $array[-4]; # "strings"
  5. print $array[-5]; # "these"
  6. print $array[-6]; # "print"
  7. print $array[-7]; # 返回undef,打印""并且抛出一个警告

同时存在scalar变量$var和包含scalar元素$var[0]的array变量@var是没有冲突的,不过会对代码的读者造成一些误导,所以请避免这种情况。

取得array的长度

  1. print "This array has ".(scalar @array)."elements"; # "This array has 6 elements"
  2. print "The last populated index is ".$#array; # "The last populated index is 5"

调用Perl脚本时使用的参数列表被保存在内置的array变量@ARGV中。

变量可以被插入到字符串中被求值:

  1. print "Hello $string"; # "Hello world"
  2. print "@array"; # "print these strings out for me"

小心。也许有一点你会把某个人的email地址放在一个字符串里,比如"jeff@gmail.com"。Perl会去尝试找一个名叫@gmail的array变量,求值并插入到字符串中,如果没有找到这个变量,将会导致一个运行时错误。有两种方法可以避免对字符串中的变量名求值:用反斜杠对@进行转义,或者将双引号改为单引号。

  1. print "Hello \$string"; # "Hello $string"
  2. print 'Hello $string'; # "Hello $string"
  3. print "\@array"; # "@array"
  4. print '@array'; # "@array"

Hash变量

Hash变量是包含一个scalar列表的、由字符串为下标存取的变量。在Python中被称为dictionary,而在PHP中被称为array

  1. my %scientists = (
  2. "Newton" => "Isaac",
  3. "Einstein" => "Albert",
  4. "Darwin" => "Charles",
  5. );

请注意这个声明与array何其相似。事实上,这个双箭头符号=>被称为“fat comma”(胖逗号),因为它与逗号完全等价。Hash变量由偶数个元素组成的列表来声明,其中偶数下标(0、2、……)的元素都被当做字符串使用。

与array一样,你也需要用美元符号来存取hash中的值,因为取到的值是scalar而非hash:

  1. print $scientists{"Newton"}; # "Isaac"
  2. print $scientists{"Einstein"}; # "Albert"
  3. print $scientists{"Darwin"}; # "Charles"
  4. print $scientists{"Dyson"}; # 返回undef,打印""并且抛出一个警告

注意在这里使用的花括号。同样的,同时存在scalar变量$var和包含scalar元素$var{"foo"}的hash变量%var是没有冲突的。

你可以将一个hash转换为两倍数量元素的array,原先hash中的键和值在转换后的array中交替出现(反向的转换也同样简单):

  1. my @scientists = %scientists;

然而有一点与array不同,hash中的键没有特定的保存顺序,而是以一种比较高效的方式进行存储。因此,需要注意转换后的array会将hash中的键值对重新排列次序

  1. print "@scientists"; # 输出可能是"Einstein Albert Darwin Charles Newton Isaac"

回顾一下,我们使用方括号来取array中的值,而使用花括号来取hash中的值。方括号是一个有效的数值运算符,而花括号是一个有效的字符串运算符,因此事实上,作为下标的值是数值还是字符串类型其实并不重要(译者注:正如前文所提到的,scalar以什么方式参与运算取决于运算符):

  1. my $data = "orange";
  2. my @data = ("purple");
  3. my %data = ( "0" => "blue");
  4. print $data; # "orange"
  5. print $data[0]; # "purple"
  6. print $data["0"]; # "purple"
  7. print $data{0}; # "blue"
  8. print $data{"0"}; # "blue"

列表(Lists)

Perl中的列表与array和hash都不一样。你已经见过一些列表了:

  1. (
  2. "print",
  3. "these",
  4. "strings",
  5. "out",
  6. "for",
  7. "me",
  8. )
  9. (
  10. "Newton" => "Isaac",
  11. "Einstein" => "Albert",
  12. "Darwin" => "Charles",
  13. )

列表不是一个变量列表是一个暂存的,可以被赋值到一个array或者hash变量,这就是为什么声明array和hash的语法竟完全一样。在许多情况下“列表”和“array”这两个词可以混用,而在同样多的情况下,列表和array表现出微妙的区别,并且具有极其容易混淆的行为。

好的,回想一下=>只是,的一种伪装,然后看一下下面的例子:

  1. ("one", 1, "three", 3, "five", 5)
  2. ("one" => 1, "three" => 3, "five" => 5)

使用=>暗示了其中一个是hash的声明(译者注:第二个),而另一个是array的声明,但就这两个列表自身并没有声明任何东西,它们只是列表,而且是完全相同的列表。同样的:

  1. ()

这里甚至没有任何变量类型的提示,这个列表可以用来声明一个空array或者空hash,而作为perl解释器则完全无法知道将会是哪一种。一旦你理解了这一点,你也就能理解Perl的这个事实:列表不能嵌套。我们可以试一下:

  1. my @array = (
  2. "apples",
  3. "bananas",
  4. (
  5. "inner",
  6. "list",
  7. "several",
  8. "entries",
  9. ),
  10. "cherries",
  11. );

Perl无法知道("inner", "list", "several", "entries")应该是array还是hash,因此Perl假设两者都不是,而将其扁平化为一个一维长列表

  1. print $array[0]; # "apples"
  2. print $array[1]; # "bananas"
  3. print $array[2]; # "inner"
  4. print $array[3]; # "list"
  5. print $array[4]; # "several"
  6. print $array[5]; # "entries"
  7. print $array[6]; # "cherries"

即使使用fat comma也会是同样的情况:

  1. my %hash = (
  2. "beer" => "good",
  3. "bananas" => (
  4. "green" => "wait",
  5. "yellow" => "eat",
  6. ),
  7. );
  8. # 上面的代码会抛出一个警告,因为我们尝试用7个元素的列表来初始化这个hash
  9. print $hash{"beer"}; # "good"
  10. print $hash{"bananas"}; # "green"
  11. print $hash{"wait"}; # "yellow";
  12. print $hash{"eat"}; # undef,因此打印""并且抛出一个警告

当然,这倒让连接数组变得简单了:

  1. my @bones = ("humerus", ("jaw", "skull"), "tibia");
  2. my @fingers = ("thumb", "index", "middle", "ring", "little");
  3. my @parts = (@bones, @fingers, ("foot", "toes"), "eyeball", "knuckle");
  4. print @parts;

稍后会有更多关于这个问题的说明。