标量
Perl中的变量总是有一个前缀魔符。对于标量魔符是$,数组是@,哈希表是%。
一个标量含有单个值,例如一个数字或者一个字符串。它也可以包含指向另外一个数据结构的引用,这样我们可以寻址使用它们。
标量的名字总是以$(美元符号)开头,后面是字母、数字和下划线。例如,标量的名字可以是$name 或 $long_and_descriptive_name,也可以是$LongAndDescriptiveName(这种称为 “驼峰式”),但是在Perl社区中,人们更喜欢使用小写字母加下划线的形式命名。
因为我们一直使用strict,所以必须首先使用my来声明变量。(后面你也会学习到out以及其他声明方法,但是现在先把注意力放在my声明。) 我们可以在声明变量的时候立即赋值:
use strict; use warnings; use 5.010; my $name = "Foo"; say $name;
或者也可以先声明变量之后再赋值:
use strict; use warnings; use 5.010; my $name; $name = "Foo"; say $name;
如果代码的逻辑允许的话,我们更倾向于使用前者的方式。
如果我们声明了一个变量,但是没有赋值,那么它含有一种称为undef的值,它类似于数据库中的NULL,但是又稍微的不同。
我们可以通过defined函数来检查一个变量是否为undef:
use strict; use warnings; use 5.010; my $name; if (defined $name) { say 'defined'; } else { say 'NOT defined'; } $name = "Foo"; if (defined $name) { say 'defined'; } else { say 'NOT defined'; } say $name;
我们可以通过赋值undef把一个变量设置成undef:
$name = undef;
标量可以赋值成数字或字符串。所以可以写:
use strict; use warnings; use 5.010; my $x = "hi"; say $x; $x = 42; say $x;
它也有效。
Perl中的符号和符号重载是符合工作的呢?
大体上,Perl和其它编程语言的处理方式是不同的。它是通过操作符来告诉操作数扮演什么角色,而不是操作数告诉操作符如何去处理。
所以,假如我们有两个数字变量,操作符决定它们的表现行为是数字还是字符串:
use strict; use warnings; use 5.010; my $z = 2; say $z; # 2 my $y = 4; say $y; # 4 say $z + $y; # 6 say $z . $y; # 24 say $z x $y; # 2222
+(数字加号)会把两个数字相加,此时$y和$z会表现的像两个数字。
.会连接两个字符串,此时$y和$z会表现像两个字符串。(在其它语言中你可能称之为字符串加法。)
x(重复操作符),会根据右侧的数字将左侧的字符串重复相应的次数,所以此时$z会表现成一个字符串,而$y是一个数字。
如果我们赋值的时候使用的是字符串,结果还是一样的:
use strict; use warnings; use 5.010; my $z = "2"; say $z; # 2 my $y = "4"; say $y; # 4 say $z + $y; # 6 say $z . $y; # 24 say $z x $y; # 2222
即便其中一个是数字,而其它的是字符串,也是一样:
use strict; use warnings; use 5.010; my $z = 7; say $z; # 7 my $y = "4"; say $y; # 4 say $z + $y; # 11 say $z . $y; # 74 say $z x $y; # 7777
Perl会根据操作符自动地进行数字到字符串以及字符串到数字的转换。
我们称之为数字和字符串上下文。
上面的例子相对简单。我们把数字转换成字符串,似乎是仅仅用引号括起来。把字符串转换成数字的例子也很简单,因为这些字符串全是由数字组成的。如果在字符串中加入小数点也是一样的,好比"3.14"。问题是,如果字符串包含非构成数字的字符会怎么样?例如, "3.14 is pi",这会在数字操作中(数字上下文)如何处理呢?
即便是简单的例子也需要说明一下:
use strict; use warnings; use 5.010; my $z = 2; say $z; # 2 my $y = "3.14 is pi"; say $y; # 3.14 is pi say $z + $y; # 5.14 say $z . $y; # 23.14 is pi say $z x $y; # 222
当字符串处于数字上下文时,Perl会查看字符串的左侧,并尝试将它转换成数字。转换的时候会尽可能长的将有意义的部分转换成数字。在数字上下文(+)中,字符串"3.14 is pi"会被看成数字3.14。
从某种程度上来说,这样的转换相当任意,但是这就是它工作的方式。
如果你使用了use warnings(强烈建议这么做),上面的代码会在标准错误输出(STDERR)上产生一个警告:
Argument "3.14 is pi" isn't numeric in addition(+) at example.pl line 10.
这样会帮助你注意到与设想不是严格匹配的情况。希望现在$x + $y这样的结果对你已经很清晰了。
Background
需要确定的是,Perl并没有把$y转换成3.14,而仅仅是在加法操作时使用了它有数字意义的值。从$z . $y的结果上也可以得到佐证。后者Perl使用的是字符串的原始值。
你可能会好奇,为什么当右侧是3.14时$z x $y显示的是222,但很明显的是perl只会重复整数次字符串... 在这个操作中,perl悄悄地对右侧的数字取整。(如果仔细琢磨,你就会意识到之前提到的数字上下文实际上也有好几个子上下文,其中之一就是整数上下文。在大多数情况下,perl会为不是程序员的人们做“正确的事情”)。
不仅如此,我们也看不到在+例子中“部分字符串转换成数字”的警告。
这不是因为操作符的不同。如果注释掉加法操作,我们又会再此操作上看到警告。缺失第二次警告的原因时,当perl给字符串"3.14 is pi"创建数字值时,它也把这个值放在了$y变量一个隐藏的位置。所以,实际上$y现在有字符串和数字两个值,在之后新的操作上会从中选用正确的值来避免转换。
还有三件事情我需要说明一下。第一个是包含undef的变量的行为,第二个是fatal warnings,第三个是如何避免自动地“字符串到数字转换”。
undef
如果一个变量的值是undef,对于大部分人来说这表示"nothing",不过它仍然可以使用。在数字上下文中它表示0,在字符串上下文中它表示空串。
use strict; use warnings; use 5.010; my $z = 3; say $z; # 3 my $y; say $z + $y; # 3 say $z . $y; # 3 if (defined $y) { say "defined"; } else { say "NOT"; # NOT }
有两个警告:
Use of uninitialized value $y in addition (+) at example.pl line 9. Use of uninitialized value $y in concatenation (.) or string at example.pl line 10.
如你所见,到最后变量仍然是undef,因此条件判断时会打印"NOT"。
Fatal warnings
有些人可能更倾向于抛出一个硬异常而不是软警告。如果这是你想要的,你可以改变一下脚本的开头,这么写:
use warnings FATAL => "all";
把这些包含在代码里后,脚本会打印出数字3,然后抛出异常:
Use of uninitialized value $y in addition (+) at example.pl line 9.
这与第一个警告的信息一样,但是此时脚本会停止运行。(除非异常被捕获,这我们后再再说。)
避免字符串到数字的自动转换
如果想要在没有明确转换的时候避免字符串的自动转换,你可以当从外部获取数据的时候检查一下字符串是否看起来像数字。
为此,我们会加载 Scalar::Util模块,并调用它提供的looks_like_number函数。
use strict; use warnings FATAL => "all"; use 5.010; use Scalar::Util qw(looks_like_number); my $z = 3; say $z; my $y = "3.14"; if (looks_like_number($z) and looks_like_number($y)) { say $z + $y; } say $z . $y; if (defined $y) { say "defined"; } else { say "NOT"; }
操作符重载
在操作数告知操作符如何处理的时候,你已经使用了操作符重载。这部分留在高级内容时再讲解。
Published on 2013-05-14