此頁面說明集合管線模式中的操作。如需更多背景資訊,請閱讀

排序

輸出是根據提供的比較器排序的輸入副本

sort 操作最簡單的應用是沒有參數的簡單呼叫。

ruby…
[3, 1, 4, 2].sort
# => [1, 2, 3, 4]
clojure…
(sort [3 1 4 2])
;; => (1 2 3 4)

像這樣沒有任何參數的排序適用於知道如何排序的事物,並提出預先定義的排序順序,在這些情況下為遞增。

變更排序順序最簡單的方法是將排序與反轉組合。

ruby…
[3, 1, 4, 2].sort.reverse
# => [4, 3, 2, 1]
clojure…
(reverse(sort [3 1 4 2]))
;; => (4 3 2 1)

但通常沒有標準的方式來排序您需要排序的內容,或者您想要以不同於標準的方式進行排序。執行此操作最基本的方法是將比較器函式傳遞到排序中。比較器函式會取得兩個參數並指出排序順序。

ruby…
["10", "8", "300"].sort {|a,b| a.to_i <=> b.to_i}
# => ["8", "10", "300"]
clojure…
(sort #(< (read-string %1) (read-string %2)) ["8" "10" "300"])
;; => ("8" "10" "300")

這裡有一個明顯的重複部分。幾乎在您想要對某個項目進行排序時,您都會對兩個參數呼叫相同的函式以取得排序金鑰。在上述範例中,此函式是字串轉換為數字。因此,只需指定該函式一次即可。

ruby…
["10", "8", "300"].sort_by {|a| a.to_i}
# => ["8", "10", "300"]
clojure…
(sort-by read-string ["8" "10" "300"])
;; => ("8" "10" "300")

為了更深入地思考這個問題,我認為在指定排序時需要兩個函數。一個函數是比較兩個值並產生排序順序指示的比較器,另一個函數是針對要排序的集合的每個元素執行的鍵提取函數,並提取傳遞給比較器的值。

有兩種樣式的比較器。第一個是布林比較器,例如「<」,它會告訴你第一個引數是否小於第二個引數。若要取得排序,此類比較器可能必須在每個比較中雙向使用,才能判斷測試值對之間的關係是小於、大於或等於。(這就是你不能使用「<=」的原因。)

第二個形式的比較器會傳回一個整數,其中負數表示小於、零表示相等,正數表示大於。許多語言使用的「星艦」運算子「<=>」就是一個很好的例子。此三值比較器僅需要對每個測試值對使用一次。

大多數平台都會有一個預設比較器,適用於各種基本類型,例如數字和字串。

如果你想排序其他東西,例如 T 恤尺寸「S」、「M」、「L」和「XL」呢?你有兩個途徑,可以在 T 恤尺寸類型上實作預設比較器,或使用鍵提取函數,將 T 恤尺寸轉換成可以比較的東西(例如數字)。

當你需要依據多個鍵排序時,粗略的方法是將所有鍵比較納入一個比較器 lambda。

ruby…
["J.C. Bach", "J.S Bach", "C.P.E. Bach", "T Albinoni"].sort do |a,b|
   first = a.split.last <=> b.split.last
   (0 != first) ? first : a <=> b
end
# => ["T Albinoni", "C.P.E. Bach", "J.C. Bach", "J.S Bach"]

但這顯然很麻煩。更好的方法是將鍵提取函數清單傳遞給排序方法,然後它可以依序嘗試這些函數。

ruby…
["J.C. Bach", "J.S Bach", 
   "C.P.E. Bach", "T Albinoni"].
   sort_by{|s| [s.split.last, s]}
# => ["T Albinoni", "C.P.E. Bach", "J.C. Bach", "J.S Bach"]
clojure…
(sort-by 
   (juxt #(last (clojure.string/split % #"\s+")) identity) 
   ["J.C. Bach" "J.S Bach" "C.P.E. Bach" "T Albinoni"])
;; => ("T Albinoni" "C.P.E. Bach" "J.C. Bach" "J.S Bach")