ITPub博客

首页 > Linux操作系统 > Linux操作系统 > 学习perl(3)

学习perl(3)

原创 Linux操作系统 作者:湖湘文化 时间:2013-11-19 15:35:42 0 删除 编辑
 

学习Perl (3)
这几天学习Perl,我的感觉是越到后面的内容越难一些。

子例程
1)定义子例程
定义你自己的子例程时,将会用到关键字sub、子例程的名称(不含&符号)以及(花括号内)经缩排的构成子例程主体的程序代码块:(子例程的定义可以放在程序中的任何地方)
#!/usr/bin/perl -w

sub marine {

$n += 1;

print "Hello,sailor number $n!n";

}

2
)调用子例程
任何表达式中只要使用了子例程的名称(加上&符号),就会调用子例程:&marine;
3
)返回值
Perl中,任何子例程都有返回值,子例程并没有分成有返回值和没有返回值两种。
#!/usr/bin/perl -w

sub sum_of_fred_and_barney {

print "Hey,you called the sum_of_fred_and_barney subroutine!n";

$fred + $barney;

}

$fred = 3;

$barney = 4;

$c = &sum_of_fred_and_barney;

print "$c is $c.n";

$d = 3 * &sum_of_fred_and_barney;

print "$d is $d.n";

sub larger_of_fred_and_barney {

if ($fred > $barney){

$fred;

} else {

$barney;

}

}

$fred = 3;

$barney = 4;

$c = &larger_of_fred_and_barney;

print "The larger is $c.n";

3
)参数
要传递参数列表到子例程里,只要在子例程调用的后面加上被括住的列表表达式就行了:$n = &max(10,15); #本子例程的调用包含了两个参数
当然得有变量来存储这个列表,所以Perl会自动将参数列表存储到名为@_的特殊数组变量,在子例程执行期间内都有效。子例程可以访问这个数组,以判断参数的个数和这些参数的值。所以这表示子例程的第一个参数存储于$_[0],第二个参数存储于$_[1],依次类推。
但是请特别注意,这些变量和$_变量毫无关联,就像$dino[3]$dino能彼此共存一样。
sub max {

if ($_[0] > $_[1]) {

$_[0];

} else {

$_[1];

}

}

4)子例程里的私有变量
Perl里,所有变量都被默认为全局变量,也就是说,在程序里的任何地方都可以访问它们。但是你可以随时运行my操作符来创建称为词法变量的私有变量:
sub max {

my($m,$n) = @_;

if ($m > $n) { $m } else { $n }

}

5)长度可变的参数列表
更好的&max例程
#!/usr/bin/perl -w

$maximum = &max(3 , 5, 10, 4, 6);

sub max {

my($max_so_far) = shift @_;

foreach (@_) {

if ($_ > $max_so_far) {

$max_so_far = $_;

}

}

$max_so_far;

}

print "$maximum.n";
上面的代码使用了一般称为高水位标记的算法:大水过后,在最后一波浪消退时,高水位标记会表示所见过最高的水位。在本例中,$max_so_far记录了最高水位标记,亦最大数字。

6)关于词法(my)变量
事实上,词法变量可以使用在任何块内,而不仅限于子例程的块。
还需要注意的是,my操作符并不会更改变量赋值时的上下文:
my($num) = @_; #列表上下文,和($num) = @_;相同
my $num = @_; #标量上下文,和$num = @_;相同
注意,在my不加括号时,只会声明一个词法变量而已:
my $fred,$barney; #错,没声明$barney my ($fred,$barney); #两个都声明了

7use strict编译命令
所谓编译命令,就是对编译器的指示,告诉它关于程序代码的一些信息。
要告诉Perl你愿意接受某些限制,请use strict这个编译命令放在程序开头(或在任何想要强制使用这些规则的块或文件里): use strict; #强制使用一些良好的程序语言规则
这样,撇开其他种种限制不谈,Perl会要求你一定要用my来声明每个新变量。当然,此限制只适用于新创建的变量。根据大部分人的建议,比整个屏幕长的程序都应该加上use strict

8return操作符
在子例程里,return操作符会立即返回某个值:
#!/usr/bin/perl -w

use strict;

my @names = qw/ fred barney betty dino wilma pebbles bamm-bamm /;

my $result = &which_element_is("dino", @names);

sub which_element_is {

my($what,@list) = @_;

foreach (0..$#list) {

if ($what eq $list[$_]){

return $_;

}

}

-1;

}
Perl程序时return最常见的用法:马上返回某个值,而不要执行子例程的其余部分。

9)省略&符号
不要将子例程的声明放到子例程的调用之后,不然编译器就无法知道division的意义何在。
真正的陷阱是,假如这个子例程与Perl内置函数同名,你就必须适用&符号来调用:
#!/usr/bin/perl -w

use strict ;

sub chomp {

print " Munch, munch!n";

}

&chomp; #&符号不可省略!
所以,真正的省略规则如下:除非你知道所有Perl内置函数的名称,否则请务必在调用函数时使用&符号!

10)返回非标量值
子例程不仅可以返回标量值,只要在列表上下文中调用它,它就可以返回列表值:
#!/usr/bin/perl -w

sub list_from_fred_to_barney {

if ($fred < $barney) {

$fred..$barney;

} else {

reverse $barney..$fred;

}

}

$fred = 11;

$barney = 6;

@c = &list_from_fred_to_barney;

print "@c.n";

习题:
1)写一支名为&total的子例程,用来返回一串数字的总和:
#!/usr/bin/perl -w

use strict;

sub total {

my $sum;

foreach (@_) {

$sum += $_;

}

$sum;

}

my @fred = qw{ 1 3 5 7 9 };

my $fred_total = &total(@fred);

print "The total of @fred is $fred_total.n";

print "Enter some numbers on separate lines:";

my $user_total = &total();

print "The total of those number is $user_total.n";

2
)利用前一题的子例程,写一支程序来计算从11000的数值的总和:
#!/usr/bin/perl -w

use strict;

sub total {

my $sum;

foreach (@_) {

$sum += $_;

}

$sum;

}

my @fred = 1..1000 ;

my $fred_total = &total(@fred);

print "The total of @fred is $fred_total.n";
参考答案如下:
#!/usr/bin/perl -w

use strict;

sub total {

my $sum = 0;

foreach (@_) {

$sum += $_;

}

$sum;

}

print "The number from 1 to 1000 add up to:",&total(1..1000),".n";

3)写一支名为&above_average的子例程,用来传入一串数字并返回所有大于平均值的数字:
#!/usr/bin/perl -w

sub total {

my $sum = 0;

foreach (@_) {

$sum += $_;

}

$sum;

}

sub average {

if (@_ == 0) { return }

my $count = @_;

my $sum = &total(@_);

$sum/$count;

}

sub above_average {

my $average = &average(@_);

my @list;

foreach $element (@_) {

if ($element > $average) {

push @list, $element;

}

}

@list;

}

my @fred = &above_average(1..10);

print "@fred is @fredn";

print "(Should be 6 7 8 9 10)n";

my @barney = &above_average(100,1..10);

print "@barney is @barneyn";

print "(Should be just 100)n";


输入与输出
1)读取标准输入
在标量上下文中使用操作符时,将会返回标准输入的下一行:
#!/usr/bin/perl -w

while (defined($line = )) {

print "I saw $line";

}

#!/usr/bin/perl -w

while () {

print "I saw $_";

}

#!/usr/bin/perl -w

while (defined($_ = )) {

print "I saw $_";

}
在列表上下文中执行整行输入操作符,它会返回一个列表,其中包含(剩下来的)所有输入内容,每个列表的元素代表一行输入内容:
#!/usr/bin/perl -w

foreach () {

print "I saw $_";

}
while
循环和foreach循环的区别:在while循环里,Perl会读取一行输入,把它存入某个变量并且执行循环的主体,接下来,它会回头去寻找其他的输入行;但是在foreach循环里,整行输入操作符会在列表上下文中执行(因为foreach需要逐项处理列表的内容),为此,在循环能够开始执行之前,它必须先将输入内容全部读进来。因此,最好的做好是尽量使用while循环的简写,让它每次处理一行。

2)从钻石操作符输入
如果想写出用法像catsedawksortgreplpr等工具的Perl程序,钻石操作符”<>”将会是你的好帮手。程序的调用参数通常是命令行上跟在程序名称后面的几个“单词”:
$ ./my_program fred barney betty #命令行参数代表依次处理的数个文件的名称
若不提供任何调用参数,程序应该改写成处理标准输入流。但有个特例,如果某个参数是连字符(-),那也代表标准输入。让程序以这种方式运行的好处,就是你可以在运行时指定程序的输入来源。钻石操作符是整行输入操作符的特例,不过它并不是从键盘取得输入,而是从用户想要的输入来源取得:
#!/usr/bin/perl -w

while (defined($line = <>)) {

chomp($line);

print "It was $line that I saw!n";

}
简写:#!/usr/bin/perl -w

while (<>) {

chomp; #chomp的默认用法,不加参数时,chomp会直接作用在$_上。

print "It was $_ that I saw!n";

}

3
)调用参数
严格来说,钻石操作符其实不会真的去检查调用参数,它靠的是@ARGV数组。这个数组是由Perl解释器事先建立的特殊数组,其内容就是调用参数所组成的列表。钻石操作符如何决定该使用哪些文件名?方法如下:它会查找@ARGV,如果它找到的是空列表,就会改用标准输入流;否则,就会使用@ARGV里的文件列表。
4)输出至标准输出
#!/usr/bin/perl -w

$name = "Larry Wall" ;

print "Hello there,$name,did you know that 3+4 is",3+4,"?n";
输出数组和替换数组是两回事:
print @array; #输出列表中的项目,各个项目之间没有空格
print “@array”; #输出一个字符串(内容是数组替换的结果),项目间以空格隔开
一般来说,如果数组里的元素包含换行符号,那么只要直接将它们输出来就可以了;通常在使用引号的场合,字符串后面都应该加上n

print <>; #’cat’的源代码 print sort <>; #’sort’的源代码
print的调用看起来像函数调用,函数调用:print (2+3);
print
的返回值不是“真”就是“假”,代表print是否执行成功。除非发生了I/O错误,否则一定会执行成功:$result = print(“Hello world!n”);
#!/usr/bin/perl -w

$result = print("Hello world!n");

print "The result is : $result.n";
如果print(或其他函数名称)后面接着一个左括号,请务必在函数的所有参数之后也有相应的右括号。
省略括号的问题: print (2+3)*4 # print ((2+3)*4); #正确

5)用printf格式化输出结果
printf操作符的参数包括“格式字符串”及“所要输出的数据列表”,后面的列表里的项目的个数应该要和转换的数目一样多,否则就会无法正常运行。
printf可用的转换格式很多,如%g%s%f%d等:
#!/usr/bin/perl -w

printf "in %d days!n",17.85; #输出结果为in 17 days! 无条件舍去小数点后的数字


6)文件句柄
文件句柄就是Perl程序里的某个名称,代表Perl进程与外界之间的输入/输出联系。它是“联系”的名称,不一定是文件名。建议全部使用大写字母来命名文件句柄。Perl保留了6个具有特殊用途的文件句柄名称:STDINSTDOUTSTDERRDATAARGVARGVOUT
标准的STDERR通常是用户的屏幕,但可以将STDERR以如下的shell命令送到某个文件:
打开文件句柄
当你需要其他普通的文件句柄时,请使用open操作符告诉Perl,要求操作系统为你的程序和外界开启一座桥梁:open CONFIG, “,dino”; open LOG, “>>logfile”;open BEDROCK,”>fred”;
不良文件句柄
如果你尝试从不良文件句柄(即没有完全打开的文件句柄)来读取数据,将会立刻读到文件结尾。如果试图将数据写入不良文件句柄,这些数据将无声无息地被丢弃掉。可以避免:一开始就用-w选项,或者warnings编译命令来启用警告信息。
关闭文件句柄
当不再需要时,可以使用close操作符来关闭文件句柄:close BEDROCK;

7
)用die来处理严重错误
die函数会输出你指定的信息(到此类信息该去的标准错误流里),并且让你的程序在非零的状态下立即终止。通常,对于程序的结束状态来说,零代表成功,非零代表失败。
Perl特殊变量“$!”,程序错误信息。
if ( !open LOG,">>logfile") {

die "Cannot create logfile:$!";

}
一个经验法则就是,用来指示用法错误的信息里可以加上结尾的换行符,但若想在调试过程中追踪相关的错误,就不要加上换行的结尾符。请一定检查open的返回值,因为之后的程序代码必须在文件打开成功时才能顺利进行。
warn送出警告信息
warn函数不会停止程序的运行。

8)使用文件句柄
当文件句柄以读取模式打开后,你可以轻易的从它读取一行数据,就像从STDIN读取标准输入流一样。
以写入或添加模式打开的文件句柄,可以在printprintf函数中使用。使用时,请直接放在关键字之后,参数列表之前:print LOG “Captain’s log,stardate 3.14159n”;
改变默认的输出文件句柄
重新打开标准的文件句柄

习题
1)写一支功能跟cat类似的程序,但是将各行反序输出:
#!/usr/bin/perl -w

print "Please enter some lines,then press Ctrl+D!n";

print reverse<>;
参考答案如下:
#!/usr/bin/perl -w

print reverse<>;
2
)写一支程序,要求用户分行键入各个字符串,然后以20个字符宽、向右对齐的方式输出每个字符串:
#!/usr/bin/perl -w

print "Please enter some lines,each line for one,then press Ctrl+D:n";

chomp(my @lines = );

print "1234567890" x 7,"12345n";

foreach(@lines) {

printf "%20sn",$_;

}

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/21256317/viewspace-776983/,如需转载,请注明出处,否则将追究法律责任。

上一篇: 学习perl(2)
下一篇: 学习Perl(4)
请登录后发表评论 登录
全部评论

注册时间:2009-05-31

  • 博文量
    108
  • 访问量
    1523087