看了一遍 perl 基础资料,很多 Perl 代码还是似懂非懂。无怪乎那些"乱七八糟 "的上下文形式,"莫名其妙"的符号,"神乎其神"的正则表达式。
其实这背后却是简单的,不过需要了解好基础。本文详解 Perl 中的变量,变量 是计算机程序语言的基础!
注意:这里不咬文嚼字了,变量是一个代词,可以是标量、数组、哈希(关联数 组)、引用、子程序、类型团。
常见变量类型
Perl 就下面这些变量(就我目前所知,:-)
| 类型 | 字符 | 例子 | 用于哪种名字 |
|---|---|---|---|
| 标量 | $ | $cents | 一个独立的数值(数字或字串) |
| 数组 | @ | @large | 一列数值,用编号做键字 |
| 散列 | % | %interest | 一组数值,用字串做键字 |
| 子过程 | & | &how | 一段可以调用的 Perl 代码 |
| 类型团 | * | *struck | 所有叫 struck 的东西 |
当你看到 "$I_am_not_a_scalar_variable" ,它一定是个标量变量!其他同理
标量
在 Perl 中标量可以赋予:整数,浮点数,字符串,甚至指向其他变量或对象的引用 这样深奥的东西。
由于 Perl 的设计者们认为要让 Perl 更像人类语言 ,所以 Perl 中出现了重要 的上下文含义。记住一点,引用数组,散列(哈希、字典)中的“值”时,因为它 们都是“标量”,所以要用 "$" 符号。
$number = 1024; # 一个数字变量(Perl 中所有数字都是以浮点类型存储的) $string = "Hello world!"; # 一个字符串变量 $string = \@a_array; # 一个引用 (后面讲解)
从现在开始,变量标识符前有 "$" 符号的都当作标量处理。("\$" 当作引用处理!)
数组
数组(array)是一个 list ,成员都是标量(引用也是标量)
@linuxs = ("debian", "redhat", "ylinux"); # 经典的数组赋值/创建,却是不常用的 :-)
@linuxs = qw# debian redhat ylinux#; # 这种常用,# 可以是 / { () 等等
注意,数组里面的标量可以是引用,后面讲解。
散列(哈希、字典)
哈希(也称散列、关联数组),Python 中对应概念是字典。提供一个无序的列 表,可以通过标量做索引。同数组是一个有序列表,使用自然数做索引有同有异。
%longday = ("Sun", "Sunday",
"Mon", "Monday",
"Tue", "Tuesday",
"Wed", "Wednesday",
"Thu", "Thursday",
"Fri", "Friday",
"Sat", "Saturday");
这样赋值更准确:
%longday = ("Sun" => "Sunday",
"Mon" => "Monday",
"Tue" => "Tuesday",
"Wed" => "Wednesday",
"Thu" => "Thursday",
"Fri" => "Friday",
"Sat" => "Saturday",);
因为散列是一种特殊的数组,你可以通过 {} 来获取单个的散列元素.比如,如果你 想找出与关键字 Wed 对应的值,你应该使用 $longday{"Wed"}.注意因为你在处理 标量,因此你在 longday 前面使用 $,而不是 %,% 代表整个散列.
引用 (reference)
参考
Perl 除了上面列举的直观数据结构,还可以组织非常复杂数据结构。这一切建立 在一个叫“引用”(reference)的概念上。(如果知道 C ,这和指针没有什么区 别)
引用是一个标量!
创建引用
使用 "\"
将 "\" 符号放在变量前可以创建指向这个变量的引用
$scalarref = \$foo; $arrayref = \@ARGV; $hashref = \%ENV; $coderef = \&handler; $globref = \*foo;
"[]" 和 "{}" 创建匿名引用
$arrayref = [1, 2, ['a', 'b', 'c']];
现在 $arrayref->[2][1] 可以得到 'b'
还可以这样创建:
@list = (\$a, \@b, \%c); @list = \($a, @b, %c); # 同上!
使用 "{}" 可以创建匿名哈希引用:
$hashref = {'Adam' => 'Eve',
'Clyde' => 'Bonnie',};
因为 "{}" 也有表示一个子过程含义,所以在有歧义的时候要变通一下。下面在 hashem 函数结尾想返回一个 hash 引用,需要加上 "+" 或 "return" 才正确!
sub hashem { { @_ } } # silently wrong
sub hashem { +{ @_ } } # ok
sub hashem { return { @_ } } # ok
如果你真的要表示另外的意思
sub showem { { @_ } } # 有歧义! (currently ok, but may change)
sub showem { {; @_ } } # ok
sub showem { { return @_ } } # ok
"+{" 和 "{" 可以消除歧义,表示一个引用,BLOCK(子过程)
子过程的引用
$coderef = sub { print "Boink!\n" };
下面一个示例,&$h("world") 等于 {my $y = shift; print "Howdy, $y!\n";}("world")
sub newprint {
my $x = shift;
return sub { my $y = shift; print "$x, $y!\n"; };
}
$h = newprint("Howdy");
$g = newprint("Greetings");
# Time passes...
&$h("world");
&$g("earthlings");
输出:
Howdy, world! Greetings, earthlings!
类型团中
$scalarref = *foo{SCALAR};
$arrayref = *ARGV{ARRAY};
$hashref = *ENV{HASH};
$coderef = *handler{CODE};
$ioref = *STDIN{IO};
$globref = *foo{GLOB};
$formatref = *foo{FORMAT};
使用引用 (Using References)
一般使用
任何时候将引用标识符(包括$符号)放到语句中 ,都是使用这个引用:
$bar = $$scalarref;
push(@$arrayref, $filename);
$$arrayref[0] = "January";
$$hashref{"KEY"} = "VALUE";
&$coderef(1,2,3);
print $globref "output\n";
较好的写法是用 "{}" 包围引用变量,上面的写法还可以这样:
$bar = ${$scalarref};
push(@{$arrayref}, $filename);
${$arrayref}[0] = "January";
${$hashref}{"KEY"} = "VALUE";
&{$coderef}(1,2,3);
$globref->print("output\n"); # iff IO::Handle is loaded
再一个多层引用:
$refrefref = \\\"howdy"; # 3层 print $$$$refrefref; # 3个$,并1个$指名3层引用对象实体为标量
几种 HASH 引用写法示例:
$$hashref{"KEY"} = "VALUE"; # CASE 0
${$hashref}{"KEY"} = "VALUE"; # CASE 1
${$hashref{"KEY"}} = "VALUE"; # CASE 2
${$hashref->{"KEY"}} = "VALUE"; # CASE 3
$arrayref->[0] = "January"; # Array element
$hashref->{"KEY"} = "VALUE"; # Hash element
$coderef->(1,2,3); # Subroutine call
更复杂点,array 是一个数组,第 x 个元素是个 hash 引用,hash 引用的 "foo" 值是一个数组引用,该数组引用的第 0 个元素是 "January" 字符串标量:
$array[$x]->{"foo"}->[0] = "January";
可以用简单脚本验证:
$ perl -e '$x=2;$array[$x]->{"foo"}->[0] = "January"; print $array[$x],"\n";'
HASH(0x9f849e0)
$ perl -e '$x=2;$array[$x]->{"foo"}->[0] = "January"; print $array[$x]->{"foo"},"\n";'
ARRAY(0x8a624b0)
$ perl -e '$x=2;$array[$x]->{"foo"}->[0] = "January"; print $array[$x]->{"foo"}->[0],"\n";'
January
更方便的写法:
$array[$x]{"foo"}[0] = "January";
和 C 里的多位数组有点像了:
$ perl -e '$score[3][4][5]=6;print $score[3],"\n";' ARRAY(0x933a9e0) $ perl -e '$score[3][4][5]=6;print $score[3][4],"\n";' ARRAY(0x84d7490) $ perl -e '$score[3][4][5]=6;print $score[3][4][5],"\n";' 6
