perl中grep,map用法總結

簡簡單單講map
(一)map函數
map BLOCK LIST
map EXPR, 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裏的元素。(所謂左值,就是在某個表達式左邊的變量。)
(二)Map vs. grep vs. foreach
map跟grep一樣,從數組裏選擇元素。下列2句是一樣的:
@selected = grep EXPR, @input;
@selected = map { if (EXPR) { $_ } } @input;
另外,map也是foreach陳述的特殊形式。假如@transformed數組當前未定義或爲空,那麼下列2句亦相等:
foreach (@input) { push @transformed, EXPR; }
@transformed = map EXPR, @input;
通常,用grep來從數組裏選擇元素,用map來從數組裏轉換元素。當然,數組處理也能使用標準的循環語句來完成(foreach, for, while, until, do while, do until, redo)。
(三)map用法示例
1. 轉換文件名爲文件大小
@sizes = map { -s $_ } @file_names;
-s是個文件測試操作符,它返回某個文件的size。所以上面這句就返回@file_names數組裏每個文件的大小,結果也是個數組。
2. 轉換數組到hash:找到某個數組值的索引
代替重複的搜索數組,我們可以用map來轉換數組到hash,並通過hash關鍵字來進行直接查找。如下的map用法相對於重複的數組搜索,更簡單高效。
@teams = qw(Miami Oregon Florida Tennessee Texas
Oklahoma Nebraska LSU Colorado Maryland);
%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(cat dog man woman hat glove);
@words = qw(dog kat wimen hat man gloove);
foreach $word (@words) {
if (not $dictionary{$word}) {
print "Possible misspelled word: $word/n";
}
}
打印結果是:
Possible misspelled word: kat
Possible misspelled word: wimen
Possible misspelled word: 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的最便利的方法。
use CGI qw(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顯得很簡潔。
(話外一句:偶在Cornell讀書時,偶的師兄們很喜歡這種用法,他們往往在中間多次使用map,grep,sort進行堆疊,結果產生的code也許高效,但不容易看懂。讀這樣的code時,你要從右往左讀,因爲右邊表達式產生的臨時列表,是左邊表達式的輸入條件。)
5. 產生隨機密碼
@a = (0 .. 9, 'a' .. 'z');
$password = join '', map { $a[int rand @a] } 0 .. 7;
print "$password/n";
每次運行它會得到不同的結果,但長度總是8位,由0 .. 7這個決定。如下是可能的輸出:
y2ti3dal
它是個隨機值,也許你能用它來做密碼。
這裏,需要先明白幾個函數,rand產生一個隨機值,它後面的@a其實是個標量哦,表示@a數組的長度,rand @a的結果可能是個小數,所以再用int函數來取整。int rand @a的結果是個整數,它>;=0但小於@a的長度。所以$a[int rand @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. 打印"just another perl hacker",讓你暈到家
print map( { chr }
('10611711511603209711011111610410111' .
'4032112101114108032104097099107101114')
=~ /.../g
), "/n";
打印的結果是:
just another perl hacker
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";
打印結果是:
1 2 3
4 5 6
7 8 9
1 4 7
2 5 8
3 6 9
這裏稍微有點複雜哦,讓我們分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]就可以訪問到匿名數組裏的元素了。所以會打印出:
1 2 3
4 5 6
7 8 9
@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]的列表。
還不明白呀?那偶也講不清了,自己多想想,:P
9. 查找質數:警示用法
foreach $num (1 .. 1000) {
@expr = map { '$_ % ' . $_ . ' &&' } 2 .. int sqrt $num;
if (eval "grep { @expr 1 } $num") { print "$num " }
}
打印結果是:
1 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 ...
該code能工作,但它如此麻煩,違背了程序最基本的明晰法則。用如下直觀的code代替它就可以了呀:
CANDIDATE: foreach $num (1 .. 1000) {
foreach $factor (2 .. int sqrt $num) {
unless ($num % $factor) { next CANDIDATE }
}
print "$num ";
}
記住,讓你的Code簡潔哦~~

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/eroswang/archive/2008/12/02/3431897.aspx


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章