这是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中的the insertion sort formulated as a monoid:
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)
This is how在Clojure写一个monoid:
(def mempty (+)) ;; 0
(def mappend +)
(defn mconcat [ms]
(reduce mappend mempty ms))
(mappend 3 4) ;; 7
(mconcat [2 3 4]) ;; 9
我的问题是:你能否在Clojure中将插入排序表示为幺半群?
答案 0 :(得分:3)
这是我的尝试,可能不是最好的尝试:)
这是Haskell幺半群的直接翻译。由于我们在Clojure中没有自动currying,因此我需要创建一个特殊的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)
这是一篇关于morphisms in sorts的非常好的论文的链接。
如果我们想要使用Reducers样式monoid,那么我们可以在我们的“mempty
”中嵌入“mappend
”作为零arity分支。一旦我们这样做,我们就可以立即使用我们的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))
答案 1 :(得分:2)
这里,作为参考,是另一个版本,它使用累加器将tail recursion modulo cons转换为尾递归。为了多样性,这里也是部分模拟缺少类型类的一种方法。
(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)