perl map

perl map指的是perl的map函式。

這裡講述的是perl的map函式。

mapBLOCK,LIST
mapexpr,LIST

map函式對LIST里的每個元素按BLOCK或EXPR進行計算,遍歷LIST時,臨時將LIST里的每個元素賦值給$_變數。map對每次的計算返回一個結果列表,它在列表上下文裡計算BLOCK或EXPR。每個LIST元素可能在輸出列表里產生0個,1個,或多個元素。

註:上文是說遍歷每個LIST元素時產生一個結果列表,而不是說總的map結果是個列表。

在標量上下文裡,map返回結果列表的元素數量。
HASH上下文裡,輸出列表(a,b,c,d...)會變成這樣的形式:(a=>;b,c=>;d,...)。假如輸出列表的元素數量非對稱,那么最後的hash元素的值就是undef了。

避免在BLOCK或EXPR里修改$_,因為這會修改LIST里的元素。另外,避免使用map返回的的列表作為左值,因為這也會修改LIST里的元素。

Mapvs.grepvs.foreach

map跟grep一樣,從數組裡選擇元素。下列2句是一樣的:

@selected=grepEXPR,@input;
@selected=map{if(EXPR){$_}}@input;

另外,map也是foreach陳述的特殊形式。假如@transformed數組當前未定義或為空,那么下列2句亦相等:

foreach(@input){push@transformed,EXPR;}
@transformed=mapEXPR,@input;

通常,用grep來從數組裡選擇元素,用map來從數組裡轉換元素。當然,數組處理也能使用標準的循環語句來完成(foreach,for,while,until,dowhile,dountil,redo)。

map用法示例

1.轉換檔案名稱為檔案大小

@sizes=map{-s$_}@file_names;

-s是個檔案測試操作符,它返回某個檔案的size。所以上面這句就返回@file_names數組裡每個檔案的大小,結果也是個數組。

2.轉換數組到hash:找到某個數組值的索引

代替重複的搜尋數組,我們可以用map來轉換數組到hash,並通過hash關鍵字來進行直接查找。如下的map用法相對於重複的數組搜尋,更簡單高效。

@teams=qw(MiamiOregonFloridaTennesseeTexas
OklahomaNebraskaLSUColoradoMaryland);
%rank=map{$teams[$_],$_+1}0..$#teams;
print"Colorado:$rank{Colorado}\n";
print"Texas:$rank{Texas}(hook'em,Horns!)\n";

列印結果是:
Colorado:9
Texas:5(hook'em,Horns!)

上述code容易理解喔,0..$#teams是個列表,$#teams代表@teams最後一個元素的下標值(這裡是9),所以這個列表就是0-9這幾個數了。map遍歷上述列表,將每個列表元素臨時設定為$_,並對$_在中間的{}里進行計算;{$teams[$_],$_+1},這裡每次計算後返回一個2元素的列表,列表結果是某個數組值和對應的數組下標加1,明白了呀?

由於對每個LIST元素進行計算時,都產生一個2元素的列表,所以總的map結果就可看作一個hash了。hash關鍵字就是數組元素,hash值是對應的數組下標加1。

3.轉換數組到hash:查找拼錯單詞

轉換數組到hash是map的最普遍用法。在本示例里,hash的值是無關緊要的,我們僅檢查hash關鍵字是否存在。

%dictionary=map{$_,1}qw(catdogmanwomanhatglove);
@words=qw(dogkatwimenhatmangloove);
foreach$word(@words){
if(not$dictionary{$word}){
print"Possiblemisspelledword:$word\n";
}
}

列印結果是:
Possiblemisspelledword:kat
Possiblemisspelledword:wimen
Possiblemisspelledword:gloove

看看第1句的map用法,它跟前面示例里的差不多喔。qw()這裡是個列表,map對這個列表里的每個元素進行{$_,1}計算,每次計算的結果返回一個2元素的列表,換句話說,就是%dictionary的key和value呀。所以map最終的結果就是一個hash了,關鍵字是qw()里的元素,值總是1,無關緊要的。

然後下面的foreach語句就容易了喔,如果@words里的元素不構成%dictionary的關鍵字的話,就列印一條出錯訊息。如果把%dictionary看成標準字典的話,那么就可用它來檢驗你自己的@words字型檔里是否有錯字了呀。

4.轉換數組到hash:存儲選中的CGI參數

hash通常是存儲傳遞給程式或子函式的參數的最便利的方法,而map通常是創建這個hash的最便利的方法。

useCGIqw(param);
%params=map{$_,(param($_))[0]}
grep{lc($_)ne'submit'}param();

這裡要了解一下CGI模組的基本知識。param()調用返回CGI參數名的列表;param($_)調用返回指定的CGI參數名的值。假如param($_)返回某個CGI參數的多個值,那么(param($_))[0]只取第一個值,以便hash仍被良好定義。

上述code的意思是,將param()的結果作為輸入列表,它的元素是多個CGI參數名,然後從這些參數名里grep出參數名不等於'submit'的,結果是一個臨時列表,map的{$_,(param($_))[0]}語句再次遍歷這個臨時列表,並獲取到參數名,和對應的參數值,將結果賦給%params。所以%params里就存儲了頁面提交過來的,除了submit外的其他CGI參數名和參數值(只取第1個)。

很巧妙的用法,是不是?它結合用了map和grep,使code顯得很簡潔。

5.產生隨機密碼

@a=(0..9,'a'..'z');
$password=join'',map{$a[intrand@a]}0..7;
print"$password\n";

每次運行它會得到不同的結果,但長度總是8位,由0..7這個決定。如下是可能的輸出:

y2ti3dal

它是個隨機值,也許你能用它來做密碼。

這裡,需要先明白幾個函式,rand產生一個隨機值,它後面的@a其實是個標量喔,表示@a數組的長度,rand@a的結果可能是個小數,所以再用int函式來取整。intrand@a的結果是個整數,它>;=0但小於@a的長度。所以$a[intrand@a]就表示從@a數組裡隨機取出一個字元了。0..7表示總共取8次,返回的結果再用join連線起來,就構成一個8位隨機密碼了呀。

當然,(0..9,'a'..'z')數組元素太少了,你可以修改它,使其包含大小寫字元,數字和標點符號,這樣密碼強度就高些。

6.從數組元素里剝離數字

不要在EXPR里修改LIST值。如下做法是不好的:

@digitless=map{tr/0-9//d;$_}@array;

它雖然從數組元素里剝離了數字,但同樣破壞了該數組,:(

如下做法是good:

@digitless=map{($x=$_)=~tr/0-9//d;
$x;
}@array;

它將tr的結果賦給臨時變數$x,並返回$x的值,這樣就保護數組了呀。

7.列印"justanotherperlhacker"

printmap({chr}
('10611711511603209711011111610410111'.
'4032112101114108032104097099107101114')
=~/.../g
),"\n";

列印的結果是:
justanotherperlhacker

chr函式將單個數字轉換到相應的ASCII字元。()=~/.../g語法以3個數字長度為單位,分割數字串到新的串列表。

比較無聊的用法,還不如用pack()和unpack(),:P

8.轉置矩陣

@matrix=([1,2,3],[4,5,6],[7,8,9]);
foreach$xyz(@matrix){
print"$xyz->;[0]$xyz->;[1]$xyz->;[2]\n";
}
@transposed=
map{$x=$_;
[map{$matrix[$_][$x]}0..$#matrix];
}0..$#{$matrix[0]};
print"\n";
foreach$xyz(@transposed){
print"$xyz->;[0]$xyz->;[1]$xyz->;[2]\n";

列印結果是:

123
456
789

147
258
369

這裡稍微有點複雜喔,讓我們分2步看看。

@matrix=([1,2,3],[4,5,6],[7,8,9]);
foreach$xyz(@matrix){
print"$xyz->;[0]$xyz->;[1]$xyz->;[2]\n";
}

這裡不難明白,([1,2,3],[4,5,6],[7,8,9])是個數組,它的每個元素又是個匿名數組,這樣在$xyz遍歷數組時,$xyz->;[0],$xyz->;[1],$xyz->;[2]就可以訪問到匿名數組裡的元素了。所以會列印出:

123
456
789

@transposed=
map{$x=$_;
[map{$matrix[$_][$x]}0..$#matrix];
}0..$#{$matrix[0]};

這裡複雜點,0..$#{$matrix[0]}是個列表,$#{$matrix[0]}表示$matrix[0]這個匿名數組的最大下標值,0..$#{$matrix[0]}表示矩陣的橫向。$x=$_;這裡將$_的值賦給$x,為什麼呢?因為它後面又有個map嘛,$_的值會改變的,所以要先存儲起來。外圍的map返回的值是[]里的map計算出來的一個列表,以[]匿名數組形式返回。[]裡面的map是這樣的,它的輸入LIST是0..$#matrix,表示矩陣的縱向了。$matrix[$_][$x]這裡先縱再橫,就把矩陣值置換了一下。所以返回的結果列表@transposed就包含置換後的矩陣了喔。

是否有點糊塗?那舉例看看。這樣看可能好點:

[1,2,3],
[4,5,6],
[7,8,9]

外圍的map遍歷時,先是橫向下標遍歷,停留在橫向0位。然後第二個map,就是縱向下標遍歷了,它要遍歷所有縱向下標,這樣在橫向0位,就先返回[1,4,7]的列表了,然後在橫向1位,又返回[2,5,8]的列表,最後在橫向2位,返回[3,6,9]的列表。

9.查找質數:警示用法

foreach$num(1..1000){
@expr=map{'$_%'.$_.'&&'}2..intsqrt$num;
if(eval"grep{@expr1}$num"){print"$num"}
}

列印結果是:
12357111317192329313741434753596167...

該code能工作,但它如此麻煩,違背了程式最基本的明晰法則。用如下直觀的code代替它就可以了呀:

CANDIDATE:foreach$num(1..1000){
foreach$factor(2..intsqrt$num){
unless($num%$factor){nextCANDIDATE}
}
print"$num";
}

相關詞條

相關搜尋

熱門詞條

聯絡我們