给定部分有序集,删除所有较小的项

时间:2013-03-22 23:59:23

标签: clojure

我正在努力寻找一种美观,惯用的方式来编写一个函数

(defn remove-smaller
  [coll partial-order-fn]
  ___
)

其中partial-order-fn接受两个参数并返回-1 0或1,它们是可比较的(分别为更小,相等,更大)或nil

remove-smaller的结果应该是coll,所有小于coll中任何其他项目的项目都会被删除。

示例:如果我们定义了一个部分订单,例如数字正常比较,字母也是如此,但字母和数字不具有可比性:

1 < 2    a < t    2 ? a

然后我们会:

(remove-smaller [1 9 a f 3 4 z])
==> [9 z]

3 个答案:

答案 0 :(得分:2)

(defn partial-compare [x y]
  (when (= (type x) (type y))
    (compare x y)))

(defn remove-smaller [coll partial-order-fn]
  (filter
    (fn [x] (every? #(let [p (partial-order-fn x %)]
                       (or (nil? p) (>= p 0)))
                    coll))
    coll))

(defn -main []
  (remove-smaller [1 9 \a \f 3 4 \z] partial-compare))

这会输出(9 \z),这是正确的,除非您希望返回值与coll的类型相同。

答案 1 :(得分:1)

在实践中我可能只使用汤姆的答案,因为没有算法可以保证比O(n ^ 2)最坏情况的性能更好并且它易于阅读。但是如果性能很重要,那么选择一个始终 n ^ 2的算法并不好,如果你能避免它;下面的解决方案避免重新迭代已知不是最大值的任何项目,因此如果该集合实际上是完全有序的,则可以与O(n)一样好。 (当然,这取决于有序关系的及物性,但因为你称之为隐含的部分顺序)

(defn remove-smaller [cmp coll]
  (reduce (fn [maxes x]
            (let [[acc keep-x]
                  ,,(reduce (fn [[acc keep-x] [max diff]]
                              (cond (neg? diff) [(conj acc max) false]
                                    (pos? diff) [acc keep-x]
                                    :else [(conj acc max) keep-x]))
                            [[] true], (map #(list % (or (cmp x %) 0))
                                            maxes))]
              (if keep-x
                (conj acc x)
                acc)))
          (), coll))

答案 2 :(得分:0)

(def data [1 9 \a \f 3 4 \z])

(defn my-fn [x y]
  (when (= (type x) (type y))
    (compare x y)))

(defn remove-smaller [coll partial-order-fn] 
  (mapv #(->> % (sort partial-order-fn) last) (vals (group-by type data))))

(remove-smaller data my-fn)
;=> [9 \z]

其余项目的顺序可能与输入集合不同,但是相等的“分区”之间没有顺序