clojure.core / compare如何实现java.util.Comparator?

时间:2015-07-16 18:58:52

标签: clojure type-hinting clojure-java-interop

我最近在clojure.core中看到了这段代码。

(defn sort-by
  "Returns a sorted sequence of the items in coll, where the sort
  order is determined by comparing (keyfn item).  If no comparator is
  supplied, uses compare.  comparator must implement
  java.util.Comparator.  If coll is a Java array, it will be modified.
  To avoid this, sort a copy of the array."
  {:added "1.0"
   :static true}
  ([keyfn coll]
   (sort-by keyfn compare coll))
  ([keyfn ^java.util.Comparator comp coll]
   (sort (fn [x y] (. comp (compare (keyfn x) (keyfn y)))) coll)))

参数Comparator上有comp类型提示。但sort-by的两个论证版本将clojure.core/compare传递给它。这是如何运作的?

更新

我想知道clojure.core/compare如何实现java.util.Comparatorcompare看起来像这样:

(defn compare
  "Comparator. Returns a negative number, zero, or a positive number
  when x is logically 'less than', 'equal to', or 'greater than'
  y. Same as Java x.compareTo(y) except it also works for nil, and
  compares numbers and collections in a type-independent manner. x
  must implement Comparable"
  {
   :inline (fn [x y] `(. clojure.lang.Util compare ~x ~y))
   :added "1.0"}
  [x y] (. clojure.lang.Util (compare x y)))

这不仅仅是一个普通的clojure函数吗?

2 个答案:

答案 0 :(得分:4)

来自jvm/clojure/lang/AFunction.java

public abstract class AFunction extends AFn implements IObj, Comparator, Fn, Serializable {

/* ...omitted... */

public int compare(Object o1, Object o2){
        Object o = invoke(o1, o2);

        if(o instanceof Boolean)
                {
                if(RT.booleanCast(o))
                        return -1;
                return RT.booleanCast(invoke(o2,o1))? 1 : 0;
                }

        Number n = (Number) o;
        return n.intValue();
}
}

当Clojure编译器正在编译函数时,它要么将它们实现为RestFn(如果是可变参数)或AFunction(否则)的衍生物;但是,RestFn扩展了AFunction,所以它最终都在同一个地方。

所以:所有Clojure函数都直接或间接地通过AFunction实现Comparator。

答案 1 :(得分:1)

更新:以下是基于我的困惑的答案:我认为这个问题是关于3-arity重载而不是2-arity重载

我认为混淆来自于短语“sort-by的两个参数版本将clojure.core/compare传递给它”。那是不对的。我们来看看代码:

(. comp (compare (keyfn x) (keyfn y)))

它使用“点特殊形式”(请参阅​​.作为列表的第一个元素)。它在这里用作方法调用。它将使用args comparecomp表示的实例上调用方法(keyfn x) (keyfn y)clojure.core/compare在这里无关。从the various forms of dot expressions开始,这符合以下情况:

(. instance-expr (method-symbol args*))

关于类型提示:它只是一种性能优化,以避免在该方法调用中反映。