[英]Can you formulate the insertion sort as a monoid in Clojure?
這是Clojure中插入排序的代碼:
(defn in-sort! [data]
(letfn [(insert ([raw x](insert [] raw x))
([sorted [y & raw] x]
(if (nil? y) (conj sorted x)
(if (<= x y ) (concat sorted [x,y] raw)
(recur (conj sorted y) raw x )))))]
(reduce insert [] data)))
;Usage:(in-sort! [6,8,5,9,3,2,1,4,7])
;Returns: [1 2 3 4 5 6 7 8 9]
這是在Haskell中公式化為等式的插入排序 :
newtype OL x = OL [x]
instance Ord x => Monoid (OL x) where
mempty = OL []
mappend (OL xs) (OL ys) = OL (merge xs ys) where
merge [] ys = ys
merge xs [] = xs
merge xs@(x : xs') ys@(y : ys')
| x <= y = x : merge xs' ys
| otherwise = y : merge xs ys'
isort :: Ord x => [x] -> OL x
isort = foldMap (OL . pure)
這是在Clojure中編寫一個monoid的方法:
(def mempty (+)) ;; 0
(def mappend +)
(defn mconcat [ms]
(reduce mappend mempty ms))
(mappend 3 4) ;; 7
(mconcat [2 3 4]) ;; 9
我的問題是: 您能否在Clojure中將插入排序表述為類對稱?
這是我的嘗試,雖然可能不是最好的方法:)
這是Haskell monoid的直接翻譯。 由於Clojure中沒有自動循環功能,因此我需要制作一個特殊的comp-2
函數。
(defn comp-2 [f g]
(fn [x y] (f (g x) (g y))))
(defn pure-list [x]
(cond
(sequential? x) (if (empty? x) '() (seq x))
:else (list x)))
(def OL-mempty (list))
(defn OL-mappend [xs ys]
(letfn [(merge [xs ys]
(cond
(empty? xs) ys
(empty? ys) xs
:else (let [[x & xs'] xs
[y & ys'] ys]
(if (<= x y)
(cons x (lazy-seq (merge xs' ys)))
(cons y (lazy-seq (merge xs ys')))))))]
(doall (merge xs ys))))
(defn foldmap [mempty mappend l]
(reduce mappend mempty l))
(def i-sort (partial foldmap OL-mempty (comp-2 OL-mappend pure-list)))
(i-sort (list 5 3 4 1 2 6)) ;; (1 2 3 4 5 6)
這是指向有關態射的非常好的論文的鏈接。
如果我們要使用mempty
風格的類mempty
則可以將“ mappend
”作為零mappend
分支嵌入到“ mempty
”中。 完成此操作后,我們可以立即將Monoid放入Reducers庫中:
(require '[clojure.core.reducers :as re])
(defn pure-list [x]
(cond
(sequential? x) (if (empty? x) '() (seq x))
:else (list x)))
(defn sort-monoid
([] '()) ;; mempty
([xs ys] ;; mappend
(letfn [(merge [xs ys]
(cond
(empty? xs) ys
(empty? ys) xs
:else (let [[x & xs'] xs
[y & ys'] ys]
(if (<= x y)
(cons x (lazy-seq (merge xs' ys)))
(cons y (lazy-seq (merge xs ys')))))))]
(doall (merge (pure-list xs) (pure-list ys))))))
(re/reduce sort-monoid (list 2 4 1 2 5))
作為參考,這里是另一個版本,該版本使用累加器將尾遞歸模態轉換為尾遞歸。 出於多樣性考慮,這也是部分模擬不存在的類型類的一種方法。
(defprotocol Monoid
(mempty [_] )
(mappend [_ xs ys]))
(defn fold-map
[monoid f xs]
(reduce (partial mappend monoid) (mempty monoid) (map f xs)))
(defn- ord-mappend*
[[x & rx :as xs] [y & ry :as ys] a]
(cond
(empty? xs) (concat a ys)
(empty? ys) (concat a xs)
:else (if (< x y)
(recur rx ys (conj a x))
(recur xs ry (conj a y)))))
(def Ord
(reify Monoid
(mempty [_] (list))
(mappend [_ xs ys] (ord-mappend* xs ys []))))
(defn isort [xs] (fold-map Ord list xs))
(defn is-sorted? [xs] (apply < xs))
(is-sorted? (isort (shuffle (range 10000))))
;=> true (sometime later)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.