1.子程序
1).创建子程序:使用关键字sub
sub marine {
print "Hello, world\n";
}
calling subroutines,使用&号
&marine
(2).返回值:所有的子程序最后一个表达式的运算结果都当作返回值。所以子程序只有“”有用返回值“和无用返回值之分。所以让一个类似print的函数的结果来充当返回值十分糟糕。比较合适的是最后一行有个if分支。
(3).参数
调用的时候加上列表表达式就行。例如&max(10, 15)。多余的变量都会被忽略,超过数组@_边界的变量将会是undef。
Perl在子程序中用@_数组存储参数。第一个参数是$_[0],第二哥存储在$_[1]...
注意:这些变量仅是数组@_的一部分,与标量$_无关。这和任意变量$arr与@arr,$arr[0]无关的道理一样。
sub max {
if($_[0] > $_[1]) {
$_[0];
} else {
$_[1];
}
}
@_是子程序的私有变量,如果有了一个全局变量@_,调用前会先被存起来,并在子程序返回时恢复原本的值。
(4).Perl子程序的参数的传递是按引用传。
#!/usr/bin/perl -w
sub marine{
$_[0]="sub1";
$_[1]="sub2";
}
$m="out1";
$n="out2";
print marine($m,$n)."\n";
print "$m\n$n";
则会输出
sub2
sub1
sub2
(5).由于perl的子程序参数是可变长度的参数列表,可以用if来检查
if(@_ != 2) {
print "WARNING, this subroutine should get exactly two parameters!\n"
}
块的最后一条语句可以省略;号。无论在控制结构的块还是在子程序的块中。
(6).return $_;立即返回某个值。
(7).何时可以省略调用子程序时的&:当定义在调用之前,或者用()来传递参数时就可以省略&
(8).返回非标量值,最后一行为列表即可
sub marine{
#...
reverse 1..10;
}
(9).采用高水位标记算法来处理任意数量参数的max
print &max(3,5,6,1,2,12);
#或者直接传入一个列表
#@numbers=(3,5,6,1,2,12);
#print &max(@numbers);
sub max {
my($max_so_far)=shift @_;
foreach (@_) {
if($_ > $max_so_far) {
$max_so_far=$_
}
}
$max_so_far
}
2.perl范围声明
our,"把名字限于某个范围“,其实就是明确声明一个"全局变量",虽然是在某个模块或者函数里面定义的,外面的也可以访问,如果已经声明过了,再次用"our",表示此处用的是全局的那个,不是同名的私有或者局部变量
our $PROGRAM_NAME = "waiter";
{
my $PROGRAM_NAME = "something";
our $PROGRAM_NAME = "server"; #这里的our和外面的相同,和前句不同。
# 这里调用的代码看到的是"server"
}
# 这里执行的代码看到的仍然是"server".
my ,"把名字和值都限于限于某个范围",简单说,就是只能本层模块或者函数可以看到这个变量,高一层的或者低一层的都看不到的。
sub greeting1{
my ($hello) = "How are you do?";
greeting2();
}
sub greeting2{
print "$hello\n";
}
$hello = "How are you doing?";
greeting2();
greeting1();
greeting2();
运行结果:
How are you doing?
How are you doing?
How are you doing?
--------------------------
一个 How are you do? 都没有,在greeting1中call greeting2时,greeting2看不到greeting1的私有 $hello变量,只能看到外面的全局变量$hello
local,"把值局限于某个范围",也有叫"动态词法范围",有点不好懂。我的理解,就是本层和本层下层的函数可以看到本层的变量,但是本层上一层的不可以。到底范围是多少,不仅取决于本层的函数,还要看下一层的程序长度和深度,所以叫"动态范围"。
sub greeting1{
local ($hello) = "How are you do?";
greeting2();
}
sub greeting2{
print "$hello\n";
}
$hello = "How are you doing?";
greeting2();
greeting1();
greeting2();
运行结果:
How are you doing?
How are you do?
How are you doing?
-----------------------
跟用 my 时不一样了吧? 此时在greeting1调用greeting2时,greeting2可以看到greeting1的局部变量$hello,外部的全局变量当然就隐藏了。
范围操作符不会改变变量赋值时的上下文。
例如
my($num)=@_;
#列表上下文,等同于($num)=@_,$num是第一个元素的值
my $num=@_;
#标量上下文,等同于$num=@_,$num是列表的长度
3. 使用use strict;强制使用一些良好的程序规范,例如不能操作未初始化的变量。可以有效防止变量名打错的情况。
4. 钻石操作符
钻石操作符<>是perl的读取文件(文件名通过参数传入,即@ARGV数组。如果@ARGV为空,则改用标准输入流)每一行的操作符,让perl程序也能像cat,sed等应用程序直接处理文本文件。是一个整行输入操作符的特例。
#!/usr/bin/perl -w
while (defined ($line=<>)){
chomp ($line);
print "$line\n";
}
或者简写成
while (<>) {
chomp;
print "$_\n";
}
print <> ; cat 的perl实现,
print sort <> ; sort的perl实现
<>的另一个例子,通过打开的文件描述符
open(HANDLE,"c:/test.txt") or die "文件不能打开";
while (<HANDLE>) #注:这就是对文件句柄读取一行并存入变量 $_
{
print; #注:打印变量 $_
}
5.输出至标准输出
print
注意:print (2+3)*4;会打印5,然后print的返回值1(成功)会被乘以4. 小心这种带括号的操作符用法。
printf和C语言的printf类似,%g是输出恰当的数值形式。自动输出整数,浮点数和指数形式。例如5/2, 51**17会输出2.5,1.0683e+29
生成格式字符串
my @items=qw(wilma dino pebbles)
my $format="The items are:\n".("%10s\n" x @items)
#使用x来复制字符串,@items为3,即“%10s\n”三遍
printf $format, @items
6.文件句柄
(1).打开文件句柄
通常文件句柄用大写标识。
Perl提供3种文件句柄:STDIN STDOUT STDERR由Perl的父进程(可能是Shell)提供。当使用其他的文件句柄时,使用open操作符告诉Perl。
例如:
open CONFIG,"dino"
open CONFIG,"<dino"
#同上即默认行为,表示这个文件是提供输入数据的。不能创建则返回失败0
open BEDROCK,">fred"
#如果已经有一个文件存在,则清楚原有内容并以新内容取代之
open LOG,">>LogFile"
#如果不存在,则创建。如果存在,则追加
检测文件句柄的有效性
my $success=open LOG,“>>logfile”
unless ($success) {
#open失败
}
比如可以直接使用die中止运行或者warning送出警告信息。perl程序的exit status:0成功,1语法错误,2处理某程序发生错误,3可能找不到某个细节配置文件等
if(!open LOG, ">>logfile") {
warning "can not create file"
}
if(@ARGV < 2)
die "Not enough arguments.\n"
另:open CONFIG, "logfile" or die "not such file";
关闭文件描述符:
close LOG
(2).使用文件句柄
print LOG “...”;
printf STDERR "...";
open PASSWD, "/etc/passwd"
or die "cannot open passwd file"
while (<PASSWD>) {
chomp;
......
}
或者放到列表里
open PASSWD, '</etc/passwd';
@lines = <PASSWD>;
$line=shift @lines;
#print $line,"\n";
foreach $temp (@lines) {
print $temp;
}
(3).用select改变默认的输出文件句柄
select LOG;
print “<INFO>, ....”;
#就会输出到文件句柄LOG中而不是STDOUT
select STDOUT;
默认的情况,写出的文件都会经过缓冲处理。只要将变量$|设置为1,就会在每次输出操作后,立即清除缓冲区(flush)
(4).重新打开标准文件句柄
open STDERR, “>>/home/luffy/.error.log”
#die和warn的信息会输送到STDERR中(默认是屏幕)
7.散列
(1).哈希是一种数据结构,类似数组。但是不像数组以数字下标进行检索,而是以检索的键(即唯一的字符串标识)进行检索。哈希相当于一些键/值队的集合,没有顺序。
键一定要是字符串,如果以50/20为键,则会被转换为一个含有三个字符的字符串”2.5”.
哈希没有大小上限,可以是任意大小。
相对于其他脚本语言的哈希(如awk),Perl的哈希不会因为变大而产生性能问题。
哈希的值可以是数字,字符串,undef或者这些类型的组合。
哈希中的元素,赋值时会自动增加。
当键的字符串严格符合变量定义要求,或者定义时使用=>来定义。就可以免去键的引号。例如$family_name{fred};
例如:
$family_name{“fred”}=”flintstone”;
foreach $person (qw <barney fred>) {
print “I‘ve heard of person $person $family_name{$person}”
}
(2).引用整个哈希,用%号
%myhash=("abc",1,"bcd",2,"cde",3);
#以键,值,键,值,键,值存储为hash
%myhash=(%myhash,"def",4);
%myhash=(
"abc"=>1,
"bcd"=>2,
"cde"=>3
);
@myarr=%myhash;
#展开为列表
%new_hash=%myhash;
#哈希的赋值,注意这一行代码会让%myhash展开为列表,然后再复制给%new_hash
%new_hash=reverse %myhash;
#同上,先展开为列表再做reverse运算。注意reverse操作不仅会使位置倒置,而且会使键/值互换
#这里注意一点:由于hash中不能有重复的键。如果展开的列表后面有重复的键,它的值会覆盖掉前面的!
(3). 散列函数
my $k = keys %myhash;
my $v = values %myhash;
if (%hash) {
#直接作为bool判断
print “exists one kv pair”;
}
对散列进行迭代,可以使用each函数获得下一组kv队,直到所有元素被访问到。例如
while(($key,$value)=each %myhash) {
#each会返回包含一组键值对的列表,直到each返回空列表,$key和$value都会是undef
print "$key=>$value"."\n";
}
if(exists $books{"Thinking in Java"})
#exists函数!
print "the book exists";
delete $book{"thinking in Java"}
#delete函数!
}
哈希元素的内插,需要用循环来以此输出。不支持整个哈希列表的内插
my %hash = ("a"=>1, "b"=>2, "c"=>3, "d"=>4);
foreach $key (sort keys %hash)
{
print "$key => $hash{$key}\n";
}
参考:perl范围声明our,my,local http://blogold.chinaunix.net/u1/51156/showart_441696.html
Textbook:learning perl