Perl语言非常灵活,语法上相对宽容(有的语言则非常严苛)。程序员可以使用Perl干任何事–从单行程序、一次性的自动化测试到为期数年、多人协作的大工程,Perl均能胜任。
只要你确定了解决问题的方法,Perl就会无条件按照你的意思去做。Perl是实用的,但效果取决于你自己。
在接下来的时间里,你将学习Perl相关的知识,你会明白Perl语言是如何工作的以及为什么会这样工作,这些知识将足以支撑你编写出实用的Perl程序。Modern Perl不仅包含了这些知识点,并且还集合了全球Perl社区的经验来帮助你写出实用、可维护的Perl代码。
首先,你需要知道的就是如何学习。
Perldoc
Perl历来有着良好的文档传统,自带了数千页的文档。perldoc是Perl自带的一个专门用来查看Perl文档的命令行工具,可以用它来查看已安装模块的文档–无论是核心模块还是来自于CPAN的模块。
- http://perldoc.perl.org/ 是Perl的在线文档系统,里面包含了不同版本的Perl文档
- http://search.cpan.org/ 和 http://metacpan.org/ 提供了所有CPAN模块的文档
- 一些发行版,如ActivePerl 和 Strawberry Perl 还提供有HTML格式的本地文档
使用perldoc查看文档的例子:
$ perldoc List::Util
$ perldoc perltoc
$ perldoc Moose::Manual
第一个例子会显示 List::Util 模块的文档;
第二个例子会显示核心模块perldoc的文档;
第三个例子将显示Moose模块(来自CPAN的模块)的文档。
perldoc并不区分模块是自带的(核心模块)还是来自CPAN,据说这种一致性将会促进外部模块的文档质量向核心(系统)文档靠拢。
标准的模块文档将包含:模块介绍,使用示例,模块/接口的细节说明。虽然文档的篇幅和数量会因模块而不同,但文档的组成结构都是一致的。
如何阅读文档
Perl文档众多,从哪开始呢?
perldoc perlfaq 查看关于Perl的经常被问到的问题及答案
perldoc perlop 查看Perl的操作符及优先级
perldoc perlsyn 查看Perl的的语法结构
perldoc perldiag 查看警告和错误相关的内容
perldoc perlvar 查看与变量相关的内容
浏览这些文档将帮助你对Perl语言有个大致的了解,在遇到Perl问题时也更容易找到原因及解决方法。
perldoc还有些功能选项(详见 perldoc perldoc)。
比如使用 -q 选项可以进行关键字搜索,如 perldoc -q sort 将可能搜到:
How do I sort an array by (anything)?
How do I sort ahash (optionally by value instead of key)?
How can I always keep my hash sorted?
一些选项说明:
-f 选项将用于内置函数,如 perldoc -f sort 将显示sort操作符的说明
-v 选项用于内置变量, 如 perldoc -v $PID 将显示变量$PID的说明
-l 选项用于查看文档的位置(而不是内容),如 perldoc -l perldoc 将显示perldoc文件的位置
-m 选项显示模块的所有内容,包括代码(无特别格式),如,perldoc -m strict 显示strict模块的所有内容
表现力
Larry Wall 是研究语言学和人类语言的。他所发明的Perl跟其他编程语言有很大的不同:其他编程语言都是围绕数学概念设计的,而Perl的设计模拟了人与人之间的交流。这样设计带来的优势是:你可以更自由的表达你的想法。
你可以使用多种方法来解决问题,所以你可以选择写出最有效率的,或者是最具可读性的,或者是最省事的,或者是最有趣的程序。Its up to you!
比如需要将一组数字变成原来的3倍。有可能是这么写的:
my @tripled;
for my $num (@numbers)
{
push @tripled, $num * 3;
}
还有可能:
my @tripled;
for (my $i = 0; $i < scalar @numbers; $i++)
{
$tripled[$i] = $numbers[$i] * 3;
}
也能这么写:
my @tripled = map { $_ * 3 } @numbers;
做同样的事情,不同的方式。
学习Perl有点类似学习一门口语。你只需要学习少量的词汇,就能享受交流(编程)的乐趣,勤加练习(读、写代码)就能彻底掌握。你可以不必要理解Perl的每一个细节就能变得高效而多产。本章会介绍一些原则性的内容,这些内容对你成长为一个Perl程序员是至关重要的。
Perl里的某些句法结构对于新手来说可能有点难度,容易感到迷糊。但是Perl的原则是:做事的方法不只一种,所以只要我们愿意就能避开这些,待熟悉Perl之后再来看看,或许这时那些些“怪异的结构”在你手里能够发挥出神奇的作用。
Perl强悍的表现力可以为大师所用创造出令人惊叹的程序,但也容易让新手犯错。经验和品位将引导你如何设计代码,你可以尽情地表现你自己。
作为另一个设计目标,Perl能理解你的想法。比如,2个变量相加($first_num + $second_num),因为你使用了数值操作符”+“号,表达出来的意思就是将它们看成数值来相加,所以不管$first_num 和 $second_num的内容是什么,Perl都会将它们看成数值,再相加。(这通常就是你想要的)
随着经验的丰富,你会发现更多专注的是做什么而不是怎么去做。
语境
口头语言中,一个词语或者句子表达的真实意思需要通过语境来确定。Perl也有语境的概念,比如有些Perl的操作符在不同的语境下将展现截然不同的行为。所以当你读、写Perl代码时,得结合上下文语境才能正确的理解和表达。
空语境,标量语境,列表语境
当你调用一个函数,并且不使用它的返回值时,你就使用了空语境。例如调用一个叫“find_chores()” 的函数:
find_chores();
如果将返回值赋值给一个标量,这就指定了标量语境,函数将表现为标量语境下的行为:
my $single_result = find_chores();
如果将函数的返回值赋值给一个数组、列表,或者在列表中使用,那么函数就处在列表语境中:
my @all_results = find_chores();
my ($single_element, @rest) = find_chores();
单一元素的列表,还是列表,所以也是列表语境:
my ($single_element) = find_chores();
函数在不同的语境下可能产生不同的行为,如果语境不对,结果可能与预期的不一致:
my %results =(
cheap_operation => $cheap_results,
expensive_operation => find_chores(), # 列表语境下的行为,可能不是你所期望的
);
这时可以使用关键词明确指定为标量语境:
my %results =
(
cheap_operation => $cheap_results,
expensive_operation => scalar find_chores(),
);
数值语境,字符串语境,布尔语境
操作符也会提供相应的语境,在前面的例子中我们看到,使用“+”号时,Perl会变量解析为数值,其实就是加号操作符提供了数值语境。
使用字符串操作符时,就会提供字符串语境。如操作符“eq”:
say "Catastrophic crypto fail!" if $alice eq $bob;
注意和下面这个的区别,数值操作“==”提供的是数值语境:
say "Catastrophic crypto fail!" if $alice == 'Bob';
布尔语境,出现在条件语句里面。比如上面的 if语句后面的表达式就是布尔语境。
在某些罕见的情况下,可能需要更为明确的指明语境:
my $numeric_x = 0 + $x; # forces numeric context
my $stringy_x = '' . $x; # forces string context
my $boolean_x = !!$x; # forces boolean context
如果知道了什么操作符提供什么语境,就能少犯错误。
默认行为
Perl代码起初看起来似乎有点罗嗦,但这里面是有捷径的。有经验的程序员可以充分使用这些捷径来精简代码。 捷径之一是语境,另一个就是默认变量。
默认标量变量
默认标量标量: $_ , 是最著名的默认变量。很多Perl的内置操作符在不提供操作变量时,默认操作对象就是$_。
当然你可以显式指定它,但更常见的情况是被省略了。
下面2行意思是一样的:
chomp $_;
chomp;
像这样默认操作$_的操作符有:
打印类 print,say;
标量操作类 chr,ord,lc,length,reverse, 正则等等。
print; #打印 $_到当前句柄,也就是输出$_的内容
say; # 打印 $_ \n到当前句柄,也就是输出$_的内容,再加一个换行符
$_ = 'My name is Paquito';
say if /My name is/;#默认使用$_匹配,正则类
s/Paquito/Paquita/; #默认使用$_替换,正则类
foreach (1 .. 10) #默认使用$_变量
{
say "#$_";
}
while (<STDIN>)#默认使用$_变量
{
chomp;#默认使用$_变量
say scalar reverse;#默认使用$_变量
}
my @squares = map { $_ * $_ } 1 .. 10;#默认使用$_变量
say 'Brunch time!' if grep { /pancake mix/ } @pantry;#默认使用$_变量
当然你也可以使用其他变量:
while (my $line = <STDIN>)
{
...
}
默认数组变量
Perl还提供了2个默认的数组变量。
在调用函数时,传递的参数就存在默认数组变量 @_ 里面。在函数内部,数组操作符默认就是操作这个数组变量。下面2段代码是等价的:
sub foo
{
my $arg = shift;
...
}
sub foo_explicit_args
{
my $arg = shift @_;
...
}
如果$_对应的意思是“那个”,那么@_对应的意思就是“那些”。
数组变量@_是每个函数内部私有的,不同函数之间互不影响。
如果在所有函数的外面,默认的数组变量就是@ARGV,里面包含了该程序的命令行参数。
@ARGV有自己的个性。如果你是使用钻石操作符(<>)读取空的文件句柄,Perl就会自动以@ARGV里面的元素为名字,读取对应的文件,如果@ARGV为空,Perl就从标准输入STDIN里读取。我们可以利用这个特性写个短小精悍的程序,如将输入的字符串逆序输出:
while (<>)
{
chomp;
say scalar reverse;
}
为什么要加上scalar呢?这个问题容易把人带偏,还多亏了perlchina群里同学的讨论。根本原因就是:如果在列表语境下,reverse并不默认操作$_ 。