2011-04-19 60 views
2

爲Ordered.compare匹配,我有以下的Scala類:使用模式在斯卡拉

case class Person(firstName: String, lastName: String, age: Int) 
    extends Ordered[Person] { 
    def compare(that: Person): Int = { 
    if (this.lastName < that.lastName) -1 
    else if (this.lastName > that.lastName) 1 
    else if (this.firstName < that.firstName) -1 
    else if (this.firstName > that.firstName) 1 
    else this.age compare that.age 
    } 
} 

允許通過姓氏,名字,和年齡排序。

如何使用模式匹配來編寫此代碼?我已經提出了以下內容,但有沒有更好的方法?

case class Person(firstName: String, lastName: String, age: Int) 
    extends Ordered[Person] { 
    def compare(that: Person): Int = { 
    that match { 
     case Person(_, thatLastName, _) if this.lastName < thatFile => -1 
     case Person(_, thatLastName, _) if this.lastName > thatFile => 1 

     case Person(thatFirstName, _, _) if this.firstName < thatFirstName => -1 
     case Person(thatFirstName, _, _) if this.firstName > thatFirstName => 1 

     case Person(_, _, thatAge) => this.age compare thatAge 
    } 
    } 
} 

UPDATE:改爲使用Ordering[A]按照Landei的回答是:

implicit val personOrdering = new Ordering[Person] { 
    def compare(first: Person, second:Person): Int = { 
    second match { 
     case Person(_, thatLastName, _) if first.lastName < thatLastName => -1 
     case Person(_, thatLastName, _) if first.lastName > thatLastName => 1 

     case Person(thatFirstName, _, _) if first.firstName < thatFirstName => -1 
     case Person(thatFirstName, _, _) if first.firstName > thatFirstName => 1 

     case Person(_, _, thatAge) => first.age compare thatAge 
    } 
    } 
} 

case class Person(firstName: String, lastName: String, age: Int) 

但似乎尷尬,我只匹配second。我怎樣才能讓它更「優雅」?

+0

對我來說,更好的辦法顯然是無圖案在此匹配案例... – 2011-04-19 11:32:55

+1

@ Jean-Philippe Pellet:但是當你需要排列更多列時會發生什麼?我認爲(希望)模式匹配版本更具可讀性,也許稍微使用空格(請參閱更新)。 – Ralph 2011-04-19 11:34:47

回答

10

Scala中的首選方法是提供一個隱式的Ordering而不是Ordered,它更加靈活,並且不會引發關於繼承的頭痛問題。

關於模式匹配,我沒有看到更好的方法,因爲比較方法的結果是Int,它們不能保證爲-1,0,1。Haskell解決方案返回「枚舉」對象(LT, EQ,GT)更清晰且模式匹配,但似乎Scala遵循C++/Java傳統,出於兼容性原因。

當然你可以推出自己比較「框架」作者:

abstract sealed class CompResult(val toInt:Int) { 
    def andThen(next: => CompResult): CompResult 
} 
case object LT extends CompResult(-1) { 
def andThen(next: => CompResult) = LT 
} 
case object EQ extends CompResult(0) { 
    def andThen(next: => CompResult) = next 
} 
case object GT extends CompResult(1) { 
    def andThen(next: => CompResult) = GT 
} 

implicit def int2Comp(n:Int) = 
    if (n == 0) EQ else if (n < 0) LT else GT 


(("sdkfhs" compareTo "fldgkjdfl"):CompResult) match { 
    case LT => println("less") 
    case EQ => println("same") 
    case GT => println("more") 
} 

你的情況,你可以寫:

case class Person(firstName: String, lastName: String, age: Int) 
    extends Ordered[Person] { 
    def compare(that: Person): Int = { 
    (this.lastName compareTo that.lastName). 
    andThen (this.firstName compareTo that.firstName). 
    andThen (this.age compare that.age).toInt 
    } 
} 
+0

酷!用於比較的DSL。但與「人物」對象相比,它似乎有點「沉重」:-)。 – Ralph 2011-04-19 12:43:52

+0

鑑於Scala不支持'Int'枚舉,擁有對象而不是整數會產生性能後果。例如,-1,0和1 _模式可匹配,並且它們可以轉換爲開關,而'LT','EQ'和'GT'不會。 – 2011-04-19 13:17:32

+0

@Daniel:一如既往,在可讀性和性能之間有一個折衷,但至少你有*選擇(例如與Java相比)。當然,我不會推薦我的解決方案用於任何性能關鍵的代碼。順便說一句,對於更嚴重的目的,我們可以「欺騙」並使用LT,EQ和GT的Java枚舉(但IMO實際上「太重」了)。 – Landei 2011-04-19 13:56:05