簡體   English   中英

Comparator是一個類型類嗎?

[英]Is Comparator a type class?

我一直在閱讀Scala中的類型類,並認為我對它有很好的把握,直到我記得Java的java.util.Comparator

如果我理解正確, Ordering就是類型類的典型示例。 我在ComparatorOrdering實例之間能夠想到的唯一區別是比較器必然是顯式的,而排序可以是,而且往往是隱含的。

Comparator是一個類型類嗎? 我得到(錯誤的?)印象,Java實際上沒有類型類。 這是否意味着類型類需要能夠隱式? 我認為類型類的隱式轉換主要是語法糖 - 實際上很棒,它“簡單地”給編譯器足夠的提示 - 我錯過了什么?


下面的代碼示例顯示了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);
}

我不想特別談論類型類,而是談論Scala中的類型類模式 ; 原因是當你開始詢問“什么是類型類”時,你最終得出的結論是它只是一個以特定方式使用的接口。

(在Haskell中,將特定構造稱為類型類更有意義。)

類型類模式由三個基本部分組成(但為方便起見,通常還有一些部分)。 第一種是由單一類型參數化的接口,它在參數化類型上抽象出某種能力。 java.util.Comparator是一個很好的例子:它提供了一個比較接口。 我們就這樣用吧。

您需要的第二件事是使用該參數化的方法,您可以在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
}

好的,但你從哪里得到那個比較器? 這是第三個必要的部分。 默認情況下,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

現在你已經提供的功能Int (隱含的),編譯器將在隱含參數填寫ordered ,使其工作。 如果您尚未提供該功能,則會出錯:

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

直到你提供該功能:

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

那么,我們是否將java.util.Comparator稱為類型類? 它當然與處理類型類模式的等效部分的Scala特征一樣起作用。 因此,即使類型類模式在Java中不能正常工作(因為您必須顯式指定要使用的實例而不是隱式查找它),從Scala的角度來看, java.util.Comparator是一個類型類什么都有。

術語類型類來自Haskell,因為它們是語言的一部分。 在scala中,它不是,它更像是一種模式,恰好在scala中有很多語言支持(主要是隱含)。 即使沒有這種語法支持,模式也是有意義的,例如在java中,我會說Comparator就是那種模式的典型例子(雖然在java中沒有使用術語類型類)。

從面向對象的角度來看,模式包括Comparator而不是Comparable 最基本的對象思想是在對象中使用比較服務,比如class String implements Comparable<String> 但是,提取它有很多優點:

  • 您可以為無法更改其代碼的類提供服務(例如,數組)
  • 您可以提供不同的服務實現(有很多方法來比較字符串(不區分大小寫和很多語言相關的)。人們可能按照他們的名字,年齡,等等進行排序。而且,您可能只想要一個訂購逆轉。

這兩個原因足以在java中使用Comparable ,並在排序集合中使用它們(例如TreeSet )保留Comparable ,因為它提供了一個方便的默認值(當你想要“默認”比較時不需要傳遞Comparator,並且它更容易調用(x.compareTo(y)而不是comparator.compare(x,y))。在scala中,帶有implicits,這一點都沒有引人注目(與java的互操作性仍然是實現Ordered / Comparable的一個原因在斯卡拉)。

鍵入類還有其他不太明顯的優點。 其中 :

  • 類型類實現可用,即使您沒有操作類型的實例,它也可能很有用。 考慮操作sum(list) 它要求在列表的元素上有某種可用的附加內容。 這可能在元素本身中可用。 假設他們可能是一些Addable[T]def add(other: T): T 但是如果你將空列表傳遞給sum,它應該返回列表類型類型的“零”(0表示整數,空字符串表示字符串......)。 有一個def zero: TAddable[T]將是無用的,因為在那一刻,你沒有Addable 但這適用於類型類,如NumericMonoid
  • 由於類型類是被賦予的(它們是對象而不是方法)它們是第一類,你可以組合它們,轉換它們。 一個非常簡單的例子是反轉Ordering(你也可以在Comparable上實現它,在java中可能是靜態方法)。 您可以組合IntString的順序以在對(Int, String)上定義排序,或者在T上給出Ordering ,在List[T]上構建排序。 Scala用implicits做到了這一點,但它在java中仍然有意義。

一個更復雜的例子:

// 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;
      }
   }
}

(在scala中可能更簡單,但同樣,這在java中很有用)

還有一些小的缺點(在Java中比在scala中更多,但仍然存在)

  • 您必須將類型類實例從方法傳遞給方法。 使用隱式參數或[T:Comparable]約束在scala中更容易,但是,如果不在調用站點,則必須在方法定義中編寫某些內容,並且在運行時,必須傳遞參數。
  • 所有內容都必須在編譯時設置(即使在scala中它是隱式設置的。所以當你可以嘗試if(x is Comparable<?>) {do some sorting} ,這對於Comparator是不可能的。

沒有java.util.Comparator是一個接口

public interface Comparator<T>

比較函數,它對某些對象集合施加總排序。 可以將比較器傳遞給排序方法(例如Collections.sort或Arrays.sort),以便精確控制排序順序。 比較器還可用於控制某些數據結構的順序(例如有序集或有序映射),或者為不具有自然順序的對象集合提供排序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM