內置的列表函數有:數組
grep, join, map, qw//, reverse, sort, unpack
列表相關的內置函數並很少,有些東西實現起來須要本身寫邏輯。不過,有額外的列表工具模塊List::Util
,能夠從該模塊中獲取更完整、更豐富的列表處理工具。less
join函數用於將列表中各個元素用給定字符鏈接起來,和split的行爲有點相反。它返回一個列表。函數
join用法以下:工具
join $sep,$list
其中$sep
只能是字符串,這一點和split不同。性能
例如:測試
print join "-",a,b,c,d,efg; # 輸出:"a-b-c-d-efg"
能夠將split後的結果用join換一個分隔符鏈接起來:優化
$str="abc:def::1234:xyz"; @new_list = join(",",split /:/,$str); print "@new_list\n"; # 輸出:abc,def,,1234,xyz
對給定列表,執行一個或多個代碼塊,並返回一個通過操做後的新列表,因此在標量上下文中它返回的是新列表中元素的個數。ui
例如,分別取出一段數值之間的奇數、偶數:spa
@new_arr1 = grep {$_ % 2} (1..100); # 取奇數 print "@new_arr1"; @new_arr1 = grep {$_ % 2 eq 0} (1..100); # 取偶數 print "@new_arr2";
以上面取奇數爲例。它相似於下面的循環過程:操作系統
my @new_arr1; foreach (1..100) { push @new_arr1,$_ if $_ % 2; } print "@new_arr1";
在grep中,每次從列表中取得一個元素,$_
將引用這個元素,而後執行代碼塊(此處的$_ % 2
和$_ % 2 eq 0
),若是返回真,則放進新的列表中。
其中grep後面的大括號爲一段要執行的代碼塊,裏面能夠有任何想要實現的邏輯。若是要執行的代碼塊只有一句話(只有一個操做),則能夠省略大括號,使用逗號來傳遞參數,注意參數不要加引號。例如改寫爲:
@new_arr1 = grep $_ % 2,(1..100); print "@new_arr1";
在Linux中也一樣有一個很是好用的grep工具,用來篩選文本很是方便。perl的grep函數一樣能實現這些功能,且更強大。
例如,篩選文件(假如以參數的方式傳遞文件)中包含3個連續數字的行:
while (<>){ chomp; @new_line = grep { /\d{3,}/ } $_; print "@new_line\n"; }
若是my.txt文件內容爲:
aaa,bbb,123,ccc,ddd eee,fff,456,ggg,hhh iii,jjj,kkk,lll,mmm ooo,ppp,qqq,888,xxx
執行上述程序,將輸出:
aaa,bbb,123,ccc,ddd eee,fff,456,ggg,hhh ooo,ppp,qqq,888,xxx
上面的程序代碼中將$_
交給grep處理,這個$_
被看成是個只有單個元素的列表,因而對整行內容進行匹配,匹配到有3個數字連續的,就賦值給數組,而後被輸出。當某行匹配不到3個連續數字時,數組將被賦值爲undef。
若是要去掉上面的空行,能夠在grep的代碼塊里加個判斷邏輯:
while (<>){ chomp; @new_line = grep { next unless /\d{3,}/ } $_; print "@new_line\n"; }
將輸出:
aaa,bbb,123,ccc,ddd eee,fff,456,ggg,hhh ooo,ppp,qqq,888,xxx
注意,grep返回的是一個新的列表,因此將其放在標量上下文將返回新列表的元素個數。因此,@new_line
不能替換爲$line
,儘管每一個列表只有一個元素。
若是隻想取出被匹配的3個連續數字,其它字符不要,則能夠:
while (<>){ chomp; grep { next unless /(\d{3,})/; $line=$1; } $_; print "$line\n"; }
相對來講,這裏能夠不用grep來是實現,它實際上是多餘的。grep的主要目的仍是留下列表中匹配到的特定元素,因此,採用grep的行爲模式,來留下連續數字。
因爲本處給的文件比較規律,能夠將每行轉換成列表:
while (<>){ chomp; @line = split /,/,$_; @new_line = grep { /\d{3,}/ } @line; print "@new_line\n" if @new_line; }
若是文件數據不規律,沒法轉換成列表,則不建議使用grep來操做。
$_
,它能夠直接修改原始數據,例如正則的s替換,如無特殊需求,不建議修改原始數據例如:
while (<>){ chomp; @line = split /,/,$_; @new_line = grep { next unless /\d{3,}/ } @line; print "@new_line\n"; }
原意本是匹配@line
中的3個連續數值,若是沒有就跳過。可是問題出在next和unless上,若是@line
第一個元素就不匹配,這一個列表就直接跳過了,它並不會去測試後面是否還有能匹配的元素,甚至第一個元素就算符合匹配,也會由於grep的"中斷"而未產生新的列表,也就沒有成功去賦值。
map和grep的行爲很是類似,也是對列表的每一個元素(賦值給$_
)執行一段給定的代碼塊(通常來講是一個子程序或函數),並返回執行結果。
與grep不一樣的是:
須要注意,任何map和grep能實現的操做,都能經過其它循環結構來實現,只不過其它循環結構須要藉助臨時變量來保存臨時結果。
例如,將列表中的每一個元素複製3次:
@str=qw(abc def ghij); print map {$_ x 3} @str;
執行結果:
abcabcabcdefdefdefghijghijghij
上面的map首先對@str
的第一個元素複製3份,而後第二個元素、第三個元素都複製三份,各複製3份後,做爲一個列表返回給上下文。也就是說,上面的map實際返回的是以下列表:
@temp_arr= qw(abcabcabc defdefdef ghijghijghij);
看上去沒什麼問題,但想要控制map對每一個元素操做後的結果,卻不那麼容易,好比想讓@temp_arr
的每一個元素換行輸出。
@str=qw(abc def ghij); print map {($_ x 3)."\n"} @str;
或者,用sprintf:
@str=qw(abc def ghij); print "haha:\n",map {sprintf("%s\n",$_ x 3)} @str;
或者直接在map內部使用print:
@str=qw(abc def ghij); map {print $_ x 3,"\n"} @str;
本想只是複製3份,結果卻多出一些其它的邏輯和操做。但沒辦法,map就是有這樣的"元素縫接"問題。
當map的代碼塊只有一條語句(一個操做)時,能夠省略代碼塊的大括號,用逗號來傳遞參數,例如:
map $_ x 3,@str;
map的塊結構中能夠有更復雜的邏輯控制,而不像grep只建議在塊結構中放匹配條件。實際上,grep能實現的,map都能實現,map能實現的,都能轉換爲循環結構來實現,只不過用grep、map更簡潔、易讀。
例如,篩選出數值列表中每一個以4結尾的數值。
my @nums=qw(12 34 56 14 25 64 32 84); my @end_by_4=map { $_ if /4$/; } @nums;
注意,map每次迭代邏輯中,返回值和子程序相似,直接將值賦值出去便可,無需return的形式。正如上面的$_
,它前面並無任何操做。再例如,在map中進行一些判斷,某個判斷分支中,將空列表賦值給左值:
my @result =map { if(...){ $_; # 將$_賦值給@result }else{ (); # 將空列表賦值給@result } } @arr;
map還能夠在迭代每個元素的時候,產生多元素的結果,好比列表、數組、hash,這種功能在某些狀況下,做用巨大。
例如,每次迭代一個元素的時候,卻產生兩個元素保存到數組中:
use 5.010; my @name=qw(ma long shuai gao xiao fang); my @new_names=map {$_,$_ x 2} @name; say "@new_names";
當迭代第一個元素的時候,將生成(ma mama),保存到數組@new_names
中,迭代第二個元素的時候,將生成(long longlong),保存到數組@new_names
中。因此,最後@new_names
的結果是:
ma mama long longlong shuai shuaishuai gao gaogao xiao xiaoxiao fang fangfang
一樣,還能夠對每一個列表中的元素生成3個元素、4個元素,等等。
正由於如此,當對每一個元素生成兩個元素的時候,能夠將其賦值給hash:
use 5.010; my @name=qw(ma long shuai gao xiao fang); my %new_names=map {$_,$_ x 2} @name; while (my ($key,$value)=each %new_names){ say "$key --> $value"; }
輸出結果:
long --> longlong xiao --> xiaoxiao gao --> gaogao ma --> mama shuai --> shuaishuai fang --> fangfang
有些時候,會比較兩個列表或hash,看看某個列表中的哪些元素存在於另外一個列表中,哪些元素不存在於另外一個列表中(例如A列表中的元素,是否存在於B中)。這時可使用map,將目標列表(即列表B)改換成hash,由於hash有exists()函數。
my @name=qw(ma long shuai gao xiao fang); my @name1=qw(ma long gao xiao); my %name_hash=map {$_,1} @name; foreach (@name1){ print $_,"\n" if exists $name_hash{$_}; }
其實能夠不用exists函數,由於$HASH{KEY}
若是KEY不存在時,將直接返回undef,因此能夠做爲條件:
my @name=qw(ma long shuai gao xiao fang); my @name1=qw(ma long gao xiao); my %name_hash=map {$_,1} @name; foreach (@name1){ print $_,"\n" if $name_hash{$_}; }
上面,爲hash的每一個key賦值數值1。由於這個時候只須要用到hash的key,value並無任何用處,賦值爲1只是爲了將列表構建成hash形式而加入的。
reverse用於反轉列表、標量字符串、hash。
反轉列表:將元素反轉
@arr=qw(abc def ghi); @arr=reverse @arr; print "@arr"; # 輸出(ghi def abc) print join(",",reverse "hello","world") ; # 輸出:world,hello
標量上下文下:反轉字符串,即便反轉目標是列表
@arr=qw(aA bB cC dD ); print scalar reverse @arr; # 輸出:DdCcBbAa print "\n"; print @arr; # 輸出:aAbBcCdD
反轉字符串:
print scalar reverse "hello"; # 輸出olleh
反轉hash:會把value反轉成key,因此value重複時,將丟棄一部分鍵值
%arr=qw(aA bB cC dD ); %arr1=reverse %arr; while(($key,$value)=each %arr1){ print "$key -> ","$value","\n"; }
執行結果:
dD -> cC bB -> aA
sort函數能夠對列表進行排序,基本用法很簡單。它的強大之處在於,咱們能夠自定義排序規則(好比忽略大小寫),而後交給sort。至關於sort給咱們提供了一個接口。
sort SUBNAME LIST # (1).指定使用某個排序子程序來實現排序 sort CODE_BLOCK LIST # (2).自定義排序代碼塊 sort LIST # (3).默認排序方式:ASCII
注意,sort並無定義標量上下文中排序的行爲。
另外,sort不是直接做用在原始列表中的,而是返回一個新的有序列表。
首先看sort LIST
的排序用法。
它是默認的排序規則,默認是按照ASCII的排序方式進行排序,返回每一個元素的從小到大的有序列表。
可使用use locale
來指定對應的排序規則,而非使用默認ASCII排序方式。
如下是須要了解的ASCII順序:從上到小,順序依次變大
最小:空值(\0,undef,null等) 製表符(\t) 換行符(\n) 空格(space) 某些標點符號(主要考慮的是負號 - ) 數字(0-9) 大寫字母(A-Z) 小寫字母(a-z)
例如:
@str=qw(abc Abc ABc 123); @sorted=sort @str; print "@sorted"; # 123 ABc Abc abc
用法是這樣的:
sort subname list;
subname就是咱們自定義的排序子程序。
關於排序子程序,sort處理方式以下:
$a
和$b
$a
和$b
:
$a < $b
,即$a
在左,$b
在右,則須要讓這段比較代碼返回-1$a > $b
,即$a
在右,$b
在左,則須要讓這段比較代碼返回1$a = $b
,則須要讓這段比較代碼返回0$a
寫在操做符左邊,則表示從小到大的排序,若是$b
寫在左邊,則表示從大到小的順序-1 1 0
)來決定值的先後位置,也就是大小順序必須注意,$a
和$b
並不是來自列表元素的拷貝,而是直接引用元素,這樣能夠提升效率。也所以,不建議中途對列表元素進行修改,這可能會產生意想不到的結果。
例如,按照數值大小進行比較,能夠定義以下子程序:
sub by_num { if ($a < $b) { -1 } elsif ($a > $b) { 1 } else { 0 } }
而後在sort中調用這個排序子程序,注意,須要去掉調用子程序時的&
符號。
@sorted = sort by_num 1,2,44,22,33,10; print "@sorted"; # 1 2 10 22 33 44
在perl中提供了兩個對sort排序子程序很是友好的比較操做符:<=>
和cmp
,前者用於比較數值,後者用於比較字符串。它們都是三值返回邏輯:
這正好符合sort子程序的定義規則。
因此,改寫上面的排序子程序,會很是的簡單:
sub by_num { $a <=> $b }
sort中使用<=>
和cmp
有兩點須要注意:
<=>
比較的是兩個數值,若是有一方不是數值,將返回undef,在sort中,它們被看成最小值
因此,大多數時候咱們不須要專門使用cmp操做符,但有時候須要使用它實現特殊需求,如不區分大小寫的排序:
sub by_caseinsensitive { "\L$a" cmp "\L$b" }
若是要實現逆序排序(即從大到小),只需將子程序中$a
和$b
的位置對調便可。
sub by_num { $b <=> $a}
固然,對sort後的結果使用reverse也能夠實現逆序排序,它並不會像想象中那樣會下降性能。perl已經將reverse優化爲sort的修飾符(例如,將原來$a
引用的位置,換成$b
去引用),不會有多少額外的逆序行爲。
事實上,應該沒人去專門寫一個排序子程序,大多數時候都是將排序規則直接內嵌在一個代碼塊中。固然,若是排序規則比較複雜,則應該寫排序子程序。
@sorted = sort {$a <=> $b} @list;
當兩個元素被斷定爲相等,它們的順序如何?例如,忽略大小寫對字符串進行排序時,"abc"是在"Abc"的左邊仍是右邊?
sort的方式是保持列表中相等元素的原始位置,不管是正序仍是逆序排序。
例如:
@str=qw(Abc bbb bde abc); @sorted1 = sort {"\L$a" cmp "\L$b"} @str; # 正序排序 @sorted2 = sort {"\L$b" cmp "\L$a"} @str; # 逆序排序 print "@sorted1","\n"; print "@sorted2","\n";
上面@sorted1
和@sorted2
中,Abc都將在abc的左邊先出現。
例如,從指定第三個字符處開始排序,第三個相等則排序第四個。
由於要略過前兩個字符,因此須要對每一個元素都進行子串提取,取出第三個以及後面的字符,再進行排序。
如下是排序代碼的實現:
@str = qw(Abxx bbcda bdef ab); @sorted = sort {substr($a,2) cmp substr($b,2)} @str; print "@sorted";
其實很好理解,sort取出兩個元素,分別用$a
和$b
去引用它們。當排序代碼中$a
所在的表達式(上例中在cmp操做符的左邊)小於$b
所在的表達式時,就返回-1。sort不會管這個-1是怎麼來的,它只會根據-1的值對$a
和$b
進行排序。
因此,對於元素"Abxx"和"bbcda",後者第三個元素小於前者第三個元素,sort會將後者排在前者的前面。
sort並未提供對hash的排序方式。可是能夠採起一些技巧,就像上面的例子同樣。
例如,存放姓名和工資的hash,想要按照他們的工資進行排序,但輸出的是他們的姓名。也就是說,根據hash的value進行排序,但輸出的是對應的key。
能夠先使用keys獲取key列表,再經過$hash_name{key_name}
對每一個value做比較,從而獲得key的順序。
%name_salary = ( malong => 8000, xiaofang => 9000, longshuai => 6000, woniu => 10000); @sorted_key = sort { $name_salary{$a} <=> $name_salary{$b} } keys %name_salary; print "@sorted_key"; # 輸出:longshuai malong xiaofang woniu
若是,hash的某兩個或幾個value值相等,例如工資都是6000,該如何顯示?這時能夠採起默認的順序,也須要指定一個決勝屬性,明確誰先誰後。
以下hash:其中wugui的值和longshuai的值相等。
%name_salary = ( malong => 8000, wugui => 6000, xiaofang => 9000, longshuai => 6000, woniu => 10000);
在按照value進行排序的時候,最終輸出時是wugui在前仍是longshuai在前?
默認狀況下,因爲keys獲取的key順序是咱們人爲沒法肯定的,因此並不能老是清楚地肯定key的前後順序。但至少,當值相等的時候,先被抓取的元素老是先出現的。
@sorted1 = sort { $name_salary{$a} <=> $name_salary{$b} } keys %name_salary; @sorted2 = sort { $name_salary{$b} <=> $name_salary{$a} } keys %name_salary; print "@sorted1","\n"; # longshuai出如今wugui以前 print "@sorted2","\n"; # longshuai出如今wugui以前
其實,咱們更但願在兩個值相等的時候,能本身定義出現的前後順序。這時就須要指定另外一個用於決勝的比較行爲(加時賽)。
例如,當工資相等,就按照姓名的大小順序進行正向排序:
@sorted = sort { $name_salary{$a} <=> $name_salary{$b} or $a cmp $b } keys %name_salary; print "@sorted";
注意,or操做符是一個短路操做符,且返回的是表達式的值。因此,當value不等的時候,or前面的返回1或-1,它們都表示true,因而短路直接返回給sort;當value相等的時候,or前面的返回0,它表示false,因而比較or後面的,一樣返回一、-一、0給sort。
sort還能夠指定更多層次的決勝屬性,因此,sort幾乎能夠實現任何形式的排序。
unix操做系統中有一個sort命令,它是一個很是完美、且效率很是高的文本排序工具。
在perl中,sort默認只是對列表內元素進行排序。要想用perl對文本數據進行排序,除了調用unix中的sort命令,perl自身的sort也能夠實現,但處理大文件時,若是不作一些處理,效率會比較差。並且,unix的sort自己已經足夠完美,若是真的有需求,徹底能夠調用它來排序。
如下是一個簡單的寫法,將全部數據一次性加載並保存到列表中,而後進行排序。這樣的缺點是內存佔用較大。
print sort { $a cmp $b } <>;