1. 程式人生 > >perl基礎-2

perl基礎-2

函式引數

perl 函式引數為$$,$$$,[email protected]

Perl 可以通過函式元型在編譯期進行有限的引數型別檢驗。

如果你宣告 sub mypush ([email protected])那麼 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 ([email protected]) myjoin ':', $a, $b, $c
sub mypop (+) mypop @array
sub mysplice ([email protected]) mysplice @array, 0, 2, @pushme
sub mykeys (+) mykeys %{$hashref}
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 ([[email protected]%&*])

上面的函式宣告允許像下面這樣呼叫 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 ([email protected]) { # 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 ([email protected]) {
        local $_ = [email protected];
        &$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關聯進行處理