Clojure核心库中的compare函数如何实现java.util.Comparator?

3

最近我在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)))

参数 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函数吗?

1
它到底是如何工作的?compare实现了java.util.Comparator。你是在问(a)它是如何实现的吗?(b)二元和三元定义是如何工作的?(c)类型提示有什么帮助?(d)在三元情况下,sort-by函数本身是如何工作的?(e)还是其他什么? - JohnJ
我扩展了我的问题。它是(a)。 - Finn
将标题调整为实际问题的要点。 - Charles Duffy
2个回答

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
更新:以下是基于我的困惑所作出的回答:我以为问题是关于三元重载而不是二元重载。
我认为混淆来自于短语“sort-by 的两个参数版本将 clojure.core/compare 传递给它”。这是不正确的。让我们看看代码:
(. comp (compare (keyfn x) (keyfn y)))

它使用了“点特殊形式”(见列表中的第一个元素.)。这里它被用作方法调用。它将在由comp表示的实例上使用参数(keyfn x) (keyfn y)调用compare方法。clojure.core/compare与此无关。从点表达式的各种形式中,这符合以下情况:
(. instance-expr (method-symbol args*))

关于类型提示:它只是一种性能优化,可以避免在方法调用中进行反射操作。

是的,没错,但是当我调用(sort-by key-fn coll)(两个参数版本)时,它会委托给(sort-by key-fn compare coll)。传递给三个参数版本的compare也不是在clojure.core中定义的函数吗? - Finn
被调用的是clojure.core/compare函数的二元重载上的compare方法,所以你说它在那里没有任何作用是不正确的。- @Finn,我认为你的观察是正确的。 - Leon Grapenthin
是的,我的错,我误读了问题,以为是关于三元重载的。我会编辑我的答案。 - nberger
已更新此答案。正确答案来自Charles Duffy。我保留这个回答是因为我不确定删除答案的礼仪,并且我认为它包含有用的信息(尽管是针对另一个问题)。 - nberger
由于这个答案可以为网站增加价值,我认为你不删除它是正确的:它并没有事实错误;只是离题了——但其他人发现它有趣并点赞了。 - Charles Duffy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接