简体   繁体   English

Comparator是一个类型类吗?

[英]Is Comparator a type class?

I've been reading up on type classes in Scala and thought I had a good grasp on it, until I remembered Java's java.util.Comparator . 我一直在阅读Scala中的类型类,并认为我对它有很好的把握,直到我记得Java的java.util.Comparator

If I understand properly, Ordering is the prototypical example of a type class. 如果我理解正确, Ordering就是类型类的典型示例。 The only difference I can think of between a Comparator and an instance of Ordering is that comparators are necessarily explicit, while orderings can be, and often are, implicit. 我在ComparatorOrdering实例之间能够想到的唯一区别是比较器必然是显式的,而排序可以是,而且往往是隐含的。

Is Comparator a type class? Comparator是一个类型类吗? I get the (mistaken?) impression that Java does not actually have type classes. 我得到(错误的?)印象,Java实际上没有类型类。 Does this mean that a type class needs to be able to be implicit? 这是否意味着类型类需要能够隐式? I considered implicit conversions of type classes to be mostly syntactic sugar - awesome as it is, it's "simply" giving the compiler enough hint - was I missing something? 我认为类型类的隐式转换主要是语法糖 - 实际上很棒,它“简单地”给编译器足够的提示 - 我错过了什么?


The following code example shows how Comparator adds an ordering operation to a type that didn't have it, without having to modify said type. 下面的代码示例显示了Comparator如何将排序操作添加到没有它的类型,而不必修改所述类型。

// Comparator used to retroactively fit the MyExample class with an ordering operation.
public static class MyExampleComparator implements Comparator<MyExample> {
    public static final Comparator<MyExample> SINGLETON = new MyExampleComparator();

    private MyExampleComparator() {}

    public int compare(MyExample a, MyExample b) {
        return a.value - b.value;
    }
}

// Custom type, its only purpose is to show that Comparator can add an ordering operation to it when it doesn't
// have one to begin with.
public static class MyExample {
    private final int value;

    public MyExample(int v) {
        value = v;
    }

    public String toString() {
        return Integer.toString(value);
    }
}

public static void main(String... args) {
    List<MyExample> list = new ArrayList<MyExample>();

    for(int i = 0; i < 10; i++)
        list.add(new MyExample(-i));

    // Sorts the list without having had to modify MyExample to implement an interface.
    Collections.sort(list, MyExampleComparator.SINGLETON);

    // Prints the expected [-9, -8, -7, -6, -5, -4, -3, -2, -1, 0]
    System.out.println(list);
}

I prefer not to talk specifically about type classes but about the type class pattern in Scala; 我不想特别谈论类型类,而是谈论Scala中的类型类模式 ; the reason is that when you start asking "what is the type class", you end up concluding that it is just an interface used in a particular way. 原因是当你开始询问“什么是类型类”时,你最终得出的结论是它只是一个以特定方式使用的接口。

(In Haskell it makes more sense to call a specific construct a type class .) (在Haskell中,将特定构造称为类型类更有意义。)

The type class pattern consists of three essential parts (but there are usually a couple more for convenience). 类型类模式由三个基本部分组成(但为方便起见,通常还有一些部分)。 The first is an interface parameterized by a single type that abstracts some sort of capability on the parameterized type. 第一种是由单一类型参数化的接口,它在参数化类型上抽象出某种能力。 java.util.Comparator is a perfect example: it provides an interface for comparison. java.util.Comparator是一个很好的例子:它提供了一个比较接口。 Let's just use that. 我们就这样用吧。

The second thing you need is a method that makes use of that parameterization, which you can specify with short-hand notation in Scala: 您需要的第二件事是使用该参数化的方法,您可以在Scala中使用简写符号指定:

// Short signature
//             v------------------- "We must be able to find a Comparator for A"
def ordered[A: java.util.Comparator](a0: A, a1: A, a2: A) = {
  val cmp = implicitly[java.util.Comparator[A]]   // This is the Comparator
  cmp.compare(a0, a1) <= 0 && cmp.compare(a1, a2) <= 0
}

// Long signature version
def ordered[A](a0: A, a1: A, a2: A)(implicit cmp: java.util.Comparator[A]) = {
  cmp.compare(a0, a1) <= 0 && cmp.compare(a1, a2) <= 0
}

Okay, but where do you get that comparator from? 好的,但你从哪里得到那个比较器? That's the third necessary piece. 这是第三个必要的部分。 By default, Scala doesn't give you Comparator s for the classes you might like, but you can define your own: 默认情况下,Scala不会为您可能喜欢的类提供Comparator ,但您可以定义自己的类:

implicit object IntComp extends java.util.Comparator[Int] {
  def compare(a: Int, b: Int) = a.compareTo(b)
}

scala> ordered(1,2,3)
res5: Boolean = true

scala> ordered(1,3,2)
res6: Boolean = false

Now that you've provided the functionality for Int (implicitly), the compiler will fill in the implicit parameter to ordered to make it work. 现在你已经提供的功能Int (隐含的),编译器将在隐含参数填写ordered ,使其工作。 If you haven't yet provided the functionality, it gives an error: 如果您尚未提供该功能,则会出错:

scala> ordered("fish","wish","dish")
<console>:12: error: could not find implicit value
for parameter cmp: java.util.Comparator[String]
          ordered("fish","wish","dish")

until you supply that functionality: 直到你提供该功能:

implicit object StringComp extends java.util.Comparator[String] {
  def compare(a: String, b: String) = a.compareTo(b)
}

scala> ordered("fish","wish","dish")
res11: Boolean = false

So, do we call java.util.Comparator a type class? 那么,我们是否将java.util.Comparator称为类型类? It certainly functions just as well as a Scala trait that handles the equivalent part of the type class pattern. 它当然与处理类型类模式的等效部分的Scala特征一样起作用。 So even though the type class pattern doesn't work as well in Java (since you have to explicitly specify the instance to use instead of having it implicitly looked up), from a Scala perspective java.util.Comparator is as much a type class as anything. 因此,即使类型类模式在Java中不能正常工作(因为您必须显式指定要使用的实例而不是隐式查找它),从Scala的角度来看, java.util.Comparator是一个类型类什么都有。

The term type class comes from Haskell were they are part of the language. 术语类型类来自Haskell,因为它们是语言的一部分。 In scala, it is not, it is more of a pattern, which happens to have a lot of language support in scala (implicits, mostly). 在scala中,它不是,它更像是一种模式,恰好在scala中有很多语言支持(主要是隐含)。 The pattern makes sense even without this syntactic support, for instance in java, and I would say that Comparator is a typical example of that pattern there (although the term type class is not used in java). 即使没有这种语法支持,模式也是有意义的,例如在java中,我会说Comparator就是那种模式的典型例子(虽然在java中没有使用术语类型类)。

From an object oriented perspective, the pattern consist in having Comparator rather than Comparable . 从面向对象的角度来看,模式包括Comparator而不是Comparable The most basic object thinking would have the comparison service in the object, say class String implements Comparable<String> . 最基本的对象思想是在对象中使用比较服务,比如class String implements Comparable<String> However, extracting it has numerous advantages: 但是,提取它有很多优点:

  • You can provide the service for a class whose code you cannot change (for instance, arrays) 您可以为无法更改其代码的类提供服务(例如,数组)
  • You can provide different implementation of the service (there are zillion ways to compare strings (case insensitive and a lot of language dependent one). People may be sorted by their name, their age, whatever. And also, you may simply want an ordering reversed. 您可以提供不同的服务实现(有很多方法来比较字符串(不区分大小写和很多语言相关的)。人们可能按照他们的名字,年龄,等等进行排序。而且,您可能只想要一个订购逆转。

These two reasons are enough to have Comparable in java, and to use them in in sorted collections (eg TreeSet ) Comparable is kept, as it gives a convenient default (no need to pass a Comparator when you want the "default" comparison, and it is easier to call (x.compareTo(y) rather than comparator.compare(x,y)). In scala, with implicits, none of this reason is compelling (interoperability with java would still be a reason to implement Ordered/Comparable in scala). 这两个原因足以在java中使用Comparable ,并在排序集合中使用它们(例如TreeSet )保留Comparable ,因为它提供了一个方便的默认值(当你想要“默认”比较时不需要传递Comparator,并且它更容易调用(x.compareTo(y)而不是comparator.compare(x,y))。在scala中,带有implicits,这一点都没有引人注目(与java的互操作性仍然是实现Ordered / Comparable的一个原因在斯卡拉)。

There are other, less obvious advantages to type classes. 键入类还有其他不太明显的优点。 Among them : 其中 :

  • A type class implementation is available and may be useful even when you have no instance of the type it operates on. 类型类实现可用,即使您没有操作类型的实例,它也可能很有用。 Consider the operation sum(list) . 考虑操作sum(list) It requires that there is some sort of addition available on the elements of the list. 它要求在列表的元素上有某种可用的附加内容。 This might be available in the element themselves. 这可能在元素本身中可用。 Say they could be some Addable[T] with def add(other: T): T . 假设他们可能是一些Addable[T]def add(other: T): T But if you pass the empty list to sum, it should return the "zero" of the type of the type of the list (0 for ints, the empty string for strings...). 但是如果你将空列表传递给sum,它应该返回列表类型类型的“零”(0表示整数,空字符串表示字符串......)。 Having a def zero: T in Addable[T] would be useless, as at that moment, you have no Addable around. 有一个def zero: TAddable[T]将是无用的,因为在那一刻,你没有Addable But this works fine with a type class such as Numeric or Monoid . 但这适用于类型类,如NumericMonoid
  • As the type class are reified (they are objects rather than methods) they are first class, you can combine them, transform them. 由于类型类是被赋予的(它们是对象而不是方法)它们是第一类,你可以组合它们,转换它们。 A very simple example is reversing an Ordering (you could implement that on Comparable too, in java probably in a static method). 一个非常简单的例子是反转Ordering(你也可以在Comparable上实现它,在java中可能是静态方法)。 You can combine the ordering of Int and String to have an ordering defined on the pair (Int, String) , or given an Ordering on T , build an ordering on List[T] . 您可以组合IntString的顺序以在对(Int, String)上定义排序,或者在T上给出Ordering ,在List[T]上构建排序。 Scala does that with implicits, but it still makes sense in java, explicitly. Scala用implicits做到了这一点,但它在java中仍然有意义。

A more sophisticated example: 一个更复杂的例子:

// Comparison  by the first comparator which finds the two elements different. 
public static Comparator<T> lexicographic<T>(final Comparator<T>... comparators) {
   return new Comparator<T>() {
      public int compare(T t1, T t2) {
         for(comparator : comparators) {
            int result = comparator.compare(t1, t2);
            if (result != 0) return result;
         }
         return 0;
      }
   }
}

(might be simpler in scala, but again, this is of interest in java) (在scala中可能更简单,但同样,这在java中很有用)

There are some small disadvantages too (much more so in java than in scala, but still) 还有一些小的缺点(在Java中比在scala中更多,但仍然存在)

  • You must pass around the type class instance from method to method. 您必须将类型类实例从方法传递给方法。 Much easier in scala with implicit parameter, or the [T : Comparable] constraint, but still, something has to be written in the methods definitions if not at call site, and at run time, the parameter has to be passed around. 使用隐式参数或[T:Comparable]约束在scala中更容易,但是,如果不在调用站点,则必须在方法定义中编写某些内容,并且在运行时,必须传递参数。
  • Everything must be set at compile time (even in scala where it is set implicitly So while you can try if(x is Comparable<?>) {do some sorting} , this would not be possible with a Comparator. 所有内容都必须在编译时设置(即使在scala中它是隐式设置的。所以当你可以尝试if(x is Comparable<?>) {do some sorting} ,这对于Comparator是不可能的。

No java.util.Comparator is an interface 没有java.util.Comparator是一个接口

public interface Comparator<T>

A comparison function, which imposes a total ordering on some collection of objects. 比较函数,它对某些对象集合施加总排序。 Comparators can be passed to a sort method (such as Collections.sort or Arrays.sort) to allow precise control over the sort order. 可以将比较器传递给排序方法(例如Collections.sort或Arrays.sort),以便精确控制排序顺序。 Comparators can also be used to control the order of certain data structures (such as sorted sets or sorted maps), or to provide an ordering for collections of objects that don't have a natural ordering. 比较器还可用于控制某些数据结构的顺序(例如有序集或有序映射),或者为不具有自然顺序的对象集合提供排序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM