你可以在Clojure中将插入排序表示为幺半群吗?

haw*_*eye 5 sorting haskell clojure insertion-sort monoids

这是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]
Run Code Online (Sandbox Code Playgroud)

这是在Haskell中作为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)
Run Code Online (Sandbox Code Playgroud)

这是如何在Clojure中编写一个monoid:

(def  mempty (+)) ;; 0
(def  mappend +)
(defn mconcat [ms]
    (reduce mappend mempty ms))

(mappend 3 4) ;; 7

(mconcat [2 3 4]) ;; 9
Run Code Online (Sandbox Code Playgroud)

我的问题是:你能否在Clojure中将插入排序表示为幺半群?

A. *_*ebb 2

这里,作为参考,是另一个版本,它将尾递归模 cons转换为带有累加器的尾递归。为了多样化,这也是部分模拟不存在的类型类的一种方法。

(defprotocol Monoid 
  (mempty [_] ) 
  (mappend [_ xs ys]))

(defn fold-map
  [monoid f xs]
  (reduce (partial mappend monoid) (mempty monoid) (map f xs)))
Run Code Online (Sandbox Code Playgroud)
(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 []))))
Run Code Online (Sandbox Code Playgroud)
(defn isort [xs] (fold-map Ord list xs))

(defn is-sorted? [xs] (apply < xs))

(is-sorted? (isort (shuffle (range 10000))))
;=> true (sometime later)
Run Code Online (Sandbox Code Playgroud)

  • Haskell 中的 @hawkeye Monoid 是一个类型类,是类型的契约。如您所知,Clojure 在语言级别避开了正式类型系统(逐渐在 [库](https://github.com/clojure/core.typed) 级别添加)。那么,你的一系列问题 - “你能在 Clojure 中使用类型类 Foo 做 X 吗?” ——回答起来有点尴尬。通常,最真实的答案是“是的,但是当你没有类型时为什么要麻烦呢?” 或者,“这对于 Clojure 来说不是惯用的。” Clojurian 倾向于推出自己的 DSL 来管理出现的复杂性,而不是从一开始就采用形式主义。 (3认同)
  • @hawkeye所以,在这里我使用了一个虚拟的第一个参数来调度我想要的 Monoid 类型,而其余的则保持无类型。您可以将协议扩展到 Clojure 中的类型,但类型可以通过多种不同方式成为 Monoid(例如 [Integer, +, 0]、[Integer, *, 1])。您需要某种方法来区分您正在谈论的内容。在 Haskell 中,您可以在类型签名中执行此操作。在这里,我让您在每次使用时指定所需的 Monoid。当这变得尴尬时,您可以引入一个糖宏,就像我在回答您的 Monad 问题之一时所做的那样。 (2认同)