perl基础-2

函数参数

perl 函数参数为$$,$$$,$@

Perl 可以通过函数元型在编译期进行有限的参数类型检验。

如果你声明 sub mypush (+@)那么 mypush() 对参数的处理就同内置的 push() 完全一样了。

函数声明必须要在编译相应函数调用之前告知编译器(编译器在编译函数调用时会对相应函数用 prototype来查询它的元型来进行参数检验,并决定怎样编译此函数调用)。

元型只在不用 & 调用函数的时候起作用。就是说在语法上如果你想像内置函数一样调用,它就表现的像内置函数一样。如果想用过时的风格通过 & 调用,那么编译器就无视函数声明。

另外元型在函数引用如 &foo 和间接调用如 &{$subref} 和 $subref->() 时也不起作用。方法调用也不受元型影响,因为实际调用的函数无法在编译期决定,它是由继承关系决定的。因为这个特性最初的目的是使你可以像内置函数那样调用自己的函数,所以下面就给出等价于内置函数调用方式的函数元型。

声明 调用方式
sub mylink ($$) mylink $old, $new
sub myvec ($$$) myvec $var, $offset, 1
sub myindex ($$;$) myindex &getstring, "substr"
sub mysyswrite ($$$;$) mysyswrite $buf, 0, length($buf) - $off, $off
sub myreverse (@) myreverse $a, $b, $c
sub myjoin ($@) myjoin ':', $a, $b, $c
sub mypop (+) mypop @array
sub mysplice (+$$@) mysplice @array, 0, 2, @pushme
sub mykeys (+) mykeys %
sub myopen (*;$) myopen HANDLE, $name
sub mypipe (**) mypipe READHANDLE, WRITEHANDLE
sub mygrep (&@) mygrep { /foo/ } $a, $b, $c
sub myrand (;$) myrand 42
sub mytime () mytime

任何 \ 跟着的函数元型中的字符代表着实际的参数必须由相应字符开头(参数前可跟my our local 声明).

只有 $ 例外,它可以接收并不以 $ 开头的 hash 和数组的元素,比如 my_function()->[0]。

传给 @_ 的参数将会是相应实际参数的引用,即对它加 \。你可以用 [] 来表示多个可用的类型。比如: sub myref ([$@%&*])

上面的函数声明允许像下面这样调用 myref() 这个函数

myref $var
myref @array
myref %hash
myref &sub
myref *glob

传入函数 myref 的第一个参数将分别是一个 scalar、数组、hash、函数、glob 的引用。

函数元型中前面不跟 \ 的字符有特殊意义。

任何不跟 \ 的 @ % 将代表剩下的所有参数,并提供 list context。

而 $ 将提供 scalar context。

& 表示需要一个匿名函数(即sub { } 这样的结构,不能是变量),

当用作第一个参数时可以省掉 sub 关键字(如果省掉 sub 则后面跟的逗号也必须要省掉).

* 表明可以接收一个 bareword、常量、scalar 表达式、typeglob或 typeglob 的引用。

传入函数的参数要么是一个简单的 scalar 要么是 typeglob 的引用(后两种情况)。

如果你总是想要一个 typeglob 的引用可以用 Symbol::qualify_to_ref() 将名字转换成相应的 typeglob 的引用:

use symbol 'qualify_to_ref';
sub foo (*) {
     my $fh = qualify_to_ref(shift, caller);
     ...
}  

+ 类似于 $ 但是当遇到数组变量或 hash 变量时表示 [@%],在其它情况下总是提供scalar context。它适用于可以接收数组变量或数组引用为参数的函数:

sub mypush (+@) { # 5.14 中 push 第一个参数可以为数组的引用
    my $aref = shift;
    die "Not an arrayref" unless ref $aref eq 'ARRAY';
    push @$aref, @_;
}  

当用 + 时函数必须要检验实际的参数是否是自己需要的类型,因为它不区分 @ %。

分号 ; 用来分隔必须的参数和可选的参数。它必须在 @ % 之前,因为它们代表剩下的所有参数。

在元型最后或在 ; 之前可以用 _ 来代替 $:它表示如果没有提供这个参数会传递 $_作为对应的参数,它可以用来实现默认参数的语法。

注意上面列表最后3个例子,mygrep() 表现的就像列表操作符,myrand() 表现的就像rand() 一样为一元操作符,mytime() 就像 time() 一样完全不需要参数。

如果你这么用: mytime + 2;你将会得到 mytime() + 2,而不是 mytime(2),没有函数元型根本无法实现这样的效果。

有意思的是你可以把 & 用在最开始的位置来创造新语法:

sub try (&@) {
    my ($try, $catch) = @_;
    eval { &$try };
    if ($@) {
        local $_ = $@;
        &$catch;
    }
}
sub catch (&) { $_[0] }
try {
    die "phooey";
} catch {
/phooey/ and print "unphooey\n";
};  

上面的代码会打印 "unphooey",即是 Try::Tiny 的实现方法。(当然用 &$catch 会将 @_ 暴露给 $catch 但这里并不是我们要考虑的)。

让我们重新实现下 Perl 的 grep 操作符:

sub mygrep (&@) { # 无法实现 grep EXPR,LIST 这个语法
    my $code = shift;
    my @result;
    foreach $_ (@_) {
        push @result, $_ if &$code;
    }
    @result;
}  

请不在要函数元型中使用字母或数字,它们被保留作它用,或许在将来用于实现完整的参数列表。

不要为老的代码添加上函数元型,因为有时会改变语意出来奇怪的结果。比如:

sub func ($) { my $n = shift; print "you ave me $n\n"; }  

某人在代码中这么调用它: func(@foo); func(split /😕);

只是声明了函数 func 只接收一个 scalar 参数却带来了灾难性的结果,原来参数所处的list context 被改为 scalar context,传入的参数变成 @foo 的元素个数,和分割的元素个数。

元型很强大也很危险,请小心慎用

特殊参数

1. $ENV{"AUTOCTL_HOME"}
环境变量。服务器获取从客户端发来的请求,并把其中的GET和POST提交的参数设置为CGI进程的环境变量,这里perl就是通过$ENV使用了这个变量。当然这个环境变量在不同的应用中是有不同作用的,你这个CGI脚本应该是这个用处
实际上是%ENV,perl中的哈希变量,里面保存的是环境变量。键是环境变量名,值是环境变量值。例如,有一个环境变量是PATH,其值为C:\windows,那么,打印这个环境变量的方法就是:
print($ENV{PATH});


2. $^O

perl 中有一些内部变量:

$^O 就是一个内部变量,他的含义就是操作系统名

perl 中 =~ 为匹配绑定
上面的表达式 为对 $line 做正则匹配, // 中的内容为正则表达式内容

$^O =~ /mswin32/
所以这个表达式的意义就和一楼的回答一样,判断操作系统是否匹配mswin32


3. @INC

当调用这个那个文件或模块来完成一些特定的工作时,Perl怎么知道去哪里找呢,答案就是这个@INC。
@INC 是perl 的一个特殊列表标量,他储存着你当前版本的Perl模块的路径。编译时,Perl会根据@INC存储的路径去查询用户所调用的模块。具体的是那些目录。我 们可以通过命令行键入perl -V 来查看,我们同样可以再BEGIN代码块里对这个@INC进行操作。(一些Perl Hacker就经常这么做)


4. unshift()

unshift 和shift 对一个数组的开头进行操作

shift(array) 除去数组第一个元素并取出

unshift(array,other) 将other插入到array的第一个元素之前


5. ${ETL::ETL_BIN}

::代表调用模块里的函数


6. next if m#^##; chomp;

如果 $_ 是 # 开头的就continue,回到循环开始;如果不是,继续执行下面语句,去除$_ 后面的 \n


7. my ( $k, $v ) = split

split ; 即等於 split /\s+/, $_;

split // 是每一个字符做分割

split / /; 是每一个空格做分割。所以

my @str=split(/ /,$_) 是不等於 my @str = split; 的

$_ = 'chr1 1111 A T';

my @str=split(/ /,$_); # 这个只有空格

print ">$_$/" foreach @str;

my @str=split; # \s 包括了 space, tab 等等'空白'的字符

print ">$_$/" foreach @str;


8. -f $logname

-e 该“文件名”是否存在
-f 该“文件名”是否为文件
-d 该“文件名”是否为目录
-b 该“文件名”是否为一个块设备
-c 该“文件名”是否为一个字符设备
-S 该“文件名”是否为一个套接字文件
-p 该“文件名”是否为一个FIFO文件
-L 该“文件名”是否为一个连接文件
-r 检测该文件名是否具有“可读”属性
-w 检测该文件名是否具有“可写”属性
-x 检测该文件名是否具有“可执行”属性
-u 检测该文件名是否具有“SUID”属性
-g 检测该文件名是否具有“SGID”属性
-k 检测该文件名是否具有“Sticky bit”属性
-s 检测该文件名是否为“非空白文件”


9. opendir(DIRHANDLE,EXPR)

打开目录EXPR,并将它与DIRHANDLE关联进行处理

posted @ 2018-11-29 16:37  枫子_dan  阅读(266)  评论(0编辑  收藏  举报