「数字混じり文字列ソートをClojureで」を改良してみた
一昨日のコードがどうも納得いかなかったので改良しました。
元ネタはどう書く?.org 「数字混じり文字列ソート」
;; clojure 1.2.1 (use 'clojure.contrib.str-utils 'clojure.contrib.core) (defn apply-flip [f a b] (f b a)) (defn parse [s] (->> (re-partition #"\d+" s) (apply-flip concat [nil]) (partition 2) (mapcat (juxt first #(-?> % second Integer/parseInt))))) (defn mixed-compare [parse a b] (let [pa (parse a) pb (parse b) len (max (count pa) (count pb))] (compare (->> (concat pa (repeat nil)) (take len) vec) (->> (concat pb (repeat nil)) (take len) vec)))) (defn mixed-sort [xs] (sort (partial mixed-compare parse) xs))
parse で出題リストの中の文字列を (文字列 数値 文字列 数値 ...) というシーケンスに変換。mixed-compare は sort に渡せる comparator です。
ただし mixed-compare はそのまま sort に渡すのではなく、第一引数に parse 関数を部分適用する必要があります。この parse 関数を使って文字列 a b を「文字列-数値 シーケンス」にしてから2つのコレクションを compare します。
なお compare する前に。
- シーケンスの長さをそろえる
- シーケンスをベクタに変換する
という処理も行っています。
長さをそろえる処理を parse の前ではなく後に行うことで、前回あった無駄を最小限に抑えることができている(はず)です。
また、Lazy Seq を compare しようとすると。
java.lang.ClassCastException: clojure.lang.LazySeq cannot be cast to java.lang.Comparable [Thrown class java.lang.RuntimeException]
と怒られるので Persistent なコレクション(今回はベクタ)にしてます。