Perl 陣列的排序
這篇文章將描述在 Perl 程式中如何排序數字陣列或字串陣列。
Perl 的內建序式 sort 可以直接排序陣列,這不另人意外。而其最簡明的使用形式,便是傳入一個陣列,它好會傳回排好的新陣列:@sorted = sort @original。
依 ASCII 次序來排
#!/usr/bin/perl use strict; use warnings; use 5.010; use Data::Dumper qw(Dumper); my @words = qw(foo bar zorg moo); say Dumper \@words; my @sorted_words = sort @words; say Dumper \@sorted_words;
前例程式的輸出如下:
$VAR1 = [ 'foo', 'bar', 'zorg', 'moo' ]; $VAR1 = [ 'bar', 'foo', 'moo', 'zorg' ];
第一段,是陣列內容在還未排序之前的結果,第二段則是排好之後的結果。
這是最簡單的使用形式,不過未必能達到想要的效果。如果某些單字的字首大寫的話,要怎麼排?
my @words = qw(foo bar Zorg moo);
@sorted_words 的結果會變成:
$VAR1 = [ 'Zorg', 'bar', 'foo', 'moo' ];
如例所示,由大寫字母開頭的字被排到第一位了。那是因為,sort 的預設次序是 依 ASCII 表格順,而在那表個中,所有大寫字母都在小寫字母之前的緣故。
比較函式
Perl 中 sort 函式的運作原理是,它會一一取出原陣列的元素,兩兩一組,左方 為 $a、右方為 $b。接著呼叫比較函式。這個「比較函式」,應 去比較 $a 與 $b 的內容,若 $a 應該排在左邊,則傳回 1, 如果 $b 應被排在左邊,則傳回 -1,而若兩者的次序前後無所謂的話,則傳回 0。
如果看到程式碼中的 sort 函式後面沒帶上這個比較函式的話,那就表示使用預設的 ASCII 表格順序來排,明確寫出來的話如下:
sort { $a cmp $b } @words;
跟省略函式區塊的 sort @words 寫法同義。
這樣看來,Perl 預設的比較函式裡,也使用了 cmp。恰巧因為,cmp 的作用就是 比較兩邊的字串,如果左邊「小於」右邊,那就傳回 1,如果左邊「比較大」,那就傳回 0, 而如果兩邊相同,就傳回 0。
依字母表的次序來排
如果要依照字母表的次序來排,那就是在排序時不要計較字母大小寫的不同,如下所示:
my @sorted_words = sort { lc($a) cmp lc($b) } @words;
在範例中,為了讓比較函式達到想要的效果,使用了 lc 函式。這函式會傳回其參 數轉換成小寫之後的樣子。而讓 cmp 所比較的的兩方,都是轉成的小寫 字串。
結果如下:
$VAR1 = [ 'bar', 'foo', 'moo', 'Zorg' ];
Perl 中依數字大小排序的方法
如果直接用預設的排序法來排數字陣列的話,會讓人覺得大錯特錯。
my @numbers = (14, 3, 12, 2, 23); my @sorted_numbers = sort @numbers; say Dumper \@sorted_numbers;
$VAR1 = [ 12, 14, 2, 23, 3 ];
但仔細端詳,其實這是很有條理的。預設的比較函式在拿到 12 與 3 的時後,是把兩者當成字串在比較。也就是說,兩方的第一個 字符也先被拿出來比,是 "1" 對 "3"。在 ASCII 表格中, "1" 在 "3" 前面,所以 "12" 當然就會被排在 "3" 前面了。
這也表示,Perl 沒有辨法猜中這時要被排序的是一群數字。
這也無妨,只要另外提供個比較函式來正確地比較數值就行了。為此目的,可以使用 <=>,亦稱 太空船算符,此算符 能比較左右兩數值,並得出 1、-1 或 0。
my @sorted_numbers = sort { $a <=> $b } @numbers;
結果如:
$VAR1 = [ 2, 3, 12, 14, 23 ];

Published on 2013-05-22