這篇文章將描述在 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
      ];