2011-03-11 61 views
2

我正在嘗試製作一個可與其他序列比較的序列(例如,其他集合類型也是可行的)。Seq [A]擴展順序[Seq [A]]

class RichSeq[A](val seq: Seq[A]) extends Ordered[RichSeq[A]] 

當然有在refered包對象中的隱式轉換:

implicit def seq2RichSeq[A](s: Seq[A]) = new RichSeq(s) 

比較裝置,第一尺寸比事項每個元素。代碼清楚:

class RichSeq[A](val seq: Seq[A]) extends Ordered[RichSeq[A]] { 
    def compare(s: RichSeq[A]) = { 
    seq.size compare s.seq.size match { 
     case 0 => seq.view.zip(s.seq).map { case (x,y) => ord.compare(x,y) }.dropWhile(_ == 0).headOption.getOrElse(0) 
     case x => x 
    } 
    } 
} 

但是,這不`噸編譯(當然),因爲一個需要排序的元素比較,所以我試過了:

class RichSeq[A](val seq: Seq[A]) extends Ordered[RichSeq[A]] { 
    def compare(s: RichSeq[A])(implicit ord: Ordering[A]) = { 
    // ... 
    } 
} 

現在的簽名比較方法是不適合的,所以我提出的隱式ord到類簽名(並且適於隱式轉換):

implicit def seq2RichSeq[A](s: Seq[A])(implicit ord: Ordering[A]) = new RichSeq(s) 
class RichSeq[A](val seq: Seq[A])(implicit ord: Ordering[A]) extends Ordered[RichSeq[A]] { 
    def compare(s: RichSeq[A]) = { 
     // ... 
    } 
    } 

但現在我有一個問題,即在的所有其它方法,我想通過implicitSeq[A]使用也需要一個隱含的Ordering[A],我不能總是提供一個。有時候我使用我的RichSeq通過沒有排序的方法和有時使用比較方法。

例如,有時我叫

def distinctBy[B](f: A => B): Seq[A] = { 
    seq.foldLeft { (Buffer[A](),MutMap[B,A]()) } { 
    case ((b,m),x) if m contains f(x) => (b,m) 
    case ((b,m),x) => 
     m += f(x) -> x 
     b += x 
     (b,m) 
    }._1 
} 

同時我不能定義一個Ordering[A]

我看到有兩個不同的類(有兩個隱式轉換)一個解決辦法:

class RichSeqOrderable[A](val seq: Seq[A])(implicit ord: Ordering[A]) extends Ordered[RichSeqOrderable[A]] 

class RichSeq[A](val seq: Seq[A]) 

但我認爲,打破讓所有的東西在一起的念頭?!?

+0

我會去兩個班,但在同一個文件。 – Anonymous 2011-03-11 18:00:17

+1

http://stackoverflow.com/questions/4493242/why-dont-scala-lists-have-an-ordering – 2011-03-11 21:20:38

回答

1

我去類似paulp的建議的內容:

class RichSeq[A](val seq: Seq[A])(implicit optionalOrd: Option[Ordering[A]] = None) extends Ordered[RichSeq[A]] { 
    def compare(s: RichSeq[A]) = { 
    seq.size compare s.seq.size match { 
     case 0 => seq.view.zip(s.seq).map { case (x,y) => optionalOrd.map(_.compare(x,y)).getOrElse(0) }.dropWhile(_ == 0).headOption.getOrElse(0) 
     case x => x 
    } 
    } 
} 

object RichSeq { 
    implicit def orderingToSome[A](implicit ord: Ordering[A] = null) = Option(ord) 
    implicit def seq2RichSeq[A](s: Seq[A])(implicit ord: Option[Ordering[A]]) = new RichSeq(s) 
} 

這不是一件好事,有太多implicits,特別是在標準庫類型。不過,我認爲Ordering[A] => Option[Ordering[A]]大概是安全的。

在implicits

斯卡拉的鏈接對自動轉換的限制添加的方法,這是它不會試圖找到方法應用於超過一次的轉換。例如:

class A(val n: Int) 
class B(val m: Int, val n: Int) 
class C(val m: Int, val n: Int, val o: Int) { 
    def total = m + n + o 
} 

// This demonstrates implicit conversion chaining restrictions 
object T1 { // to make it easy to test on REPL 
    implicit def toA(n: Int) = new A(n) 
    implicit def aToB(a: A) = new B(a.n, a.n) 
    implicit def bToC(b: B) = new C(b.m, b.n, b.m + b.n) 

    // won't work 
    println(5.total) 
    println(new A(5).total) 

    // works 
    println(new B(5, 5).total) 
    println(new C(5, 5, 10).total) 
} 

但是,如果一個隱含的定義需要本身就是一個隱含參數,斯卡拉找,只要需要額外的隱含價值。繼續從最後一個例子:

// def m[A <% B](m: A) is the same thing as 
// def m[A](m: A)(implicit ev: A => B) 

object T2 { 
    implicit def toA(n: Int) = new A(n) 
    implicit def aToB[A1 <% A](a: A1) = new B(a.n, a.n) 
    implicit def bToC[B1 <% B](b: B1) = new C(b.m, b.n, b.m + b.n) 

    // works 
    println(5.total) 
    println(new A(5).total) 
    println(new B(5, 5).total) 
    println(new C(5, 5, 10).total) 
} 

「魔術!」,你可能會說。並非如此。以下是編譯器如何轉化每一個:

object T1Translated { 
    implicit def toA(n: Int) = new A(n) 
    implicit def aToB(a: A) = new B(a.n, a.n) 
    implicit def bToC(b: B) = new C(b.m, b.n, b.m + b.n) 

    // Scala won't do this 
    println(bToC(aToB(toA(5))).total) 
    println(bToC(aToB(new A(5))).total) 

    // Just this 
    println(bToC(new B(5, 5)).total) 

    // No implicits required 
    println(new C(5, 5, 10).total) 
} 

object T2Translated { 
    implicit def toA(n: Int) = new A(n) 
    implicit def aToB[A1 <% A](a: A1) = new B(a.n, a.n) 
    implicit def bToC[B1 <% B](b: B1) = new C(b.m, b.n, b.m + b.n) 

    // Scala does this 
    println(bToC(5)(x => aToB(x)(y => toA(y))).total) 
    println(bToC(new A(5))(x => aTo(B(x)(identity _)).total) 
    println(bToC(new B(5, 5))(identity _).total) 

    // no implicits required 
    println(new C(5, 5, 10).total) 
} 

因此,雖然bToC被用作一個隱式轉換,aToBtoA正在傳遞的隱含參數,而不是被鏈隱式轉換。

+0

因爲implicits沒有鏈接,所以你必須顯式地將'Ordering [。]'傳遞給'seq2RichSeq',它被隱式轉換爲'Option [Ordering [。]]',對嗎?但是,如何明確地將某些內容傳遞給隱式轉換? – Raphael 2011-03-16 11:42:23

+0

無法鏈接的內容是隱式轉換以允許使用方法。例如,如果我有從'A => B'和'B => C'的轉換,但是我不能在'A'上調用'C'方法。然而,在這裏,只有一個隱式轉換正在發生:'seq2RichSeq'。另一方面,允許隱含參數的鏈接。所以它需要一個隱含的'Option [Ordering [A]]',它是由'orderedToSome'提供的,它帶有一個隱式的Ordering [A],它本身是隱式提供的,甚至可以通過隱式的'A =>排序[A]'。 – 2011-03-16 12:51:06

+0

呵呵。這是否意味着我不能鏈式隱式方法,但*可以鏈式隱式函數? *劃傷他的頭*啊,不,等等:「鏈接」是在這裏使用的錯誤詞。你在嵌套,而不是鏈接,對嗎?我想知道是否可以通過使用嵌套來規避鏈接限制,從某種意義上說,通過嵌套其他幾個嵌套來構建一個隱式轉換。 – Raphael 2011-03-16 13:15:04

0

我沒有編寫它完全,但你爲什麼不這樣做:

class RichSeq[A <: Ordered[A]](val seq: Seq[A]) extends Ordered[RichSeq[A]] { 
    import Ordering.ordered 

    ... 
} 

導入的隱式轉換會給你Ordering[A]必要時。

+0

然後,我有同樣的問題:'A'必須是'有序[A]',那並不總是可能的。有時候,我既沒有「訂購[A]」也沒有訂購[A]'。 – 2011-03-11 19:26:55

+0

然後,在創建該類型的RichSeq時沒有用處,至少與RichSeq Raphael 2011-03-11 22:09:04

3

我通常的前言說我不一定會用這種方式做事,而是用這個問題作爲藉口來說明一些鮮爲人知的特徵:在這裏,如果有任何隱式排序可用,它將使用它,否則它會命令他們通過hashcode。

package object foo { 
    implicit def seq2RichSeq[A](s: Seq[A])(implicit ord: Ordering[A] = Ordering[Int].on((_: A).##)) = new RichSeq(s) 
} 
package foo { 
    class RichSeq[A](val seq: Seq[A])(implicit ord: Ordering[A]) extends Ordered[RichSeq[A]] { 
    def compare(s: RichSeq[A]) = { 
     seq.size compare s.seq.size match { 
     case 0 => seq.view.zip(s.seq).map { case (x,y) => ord.compare(x,y) }.dropWhile(_ == 0).headOption.getOrElse(0) 
     case x => x 
     } 
    } 
    } 
} 
+1

不錯。另一個想法是默認比較字符串表示,希望它們以某種方式緊密地代表所討論的對象。儘管如此,哈希代碼通常是一個更乾淨的解決方案,但具有較少的*含義*。特別是,它可能會將兩個具有相同內容的對象放在相距很遠的地方,因爲對於具有相同內容的不同對象,哈希代碼不必相同。 – Raphael 2011-03-13 10:22:12

+0

不錯!哈希碼的默認排序!我非常喜歡接受你的回答,但丹尼爾斯對我的回答非常有吸引力。 – 2011-04-27 17:07:51