2013-03-01 75 views
8

以下不起作用。與集合匹配的Scala模式

object Foo { 
    def union(s: Set[Int], t: Set[Int]): Set[Int] = t match { 
     case isEmpty => s 
     case (x:xs) => union(s + x, xs) 
     case _  => throw new Error("bad input") 
    } 
} 

error: not found: type xs

如何能在一組I模式匹配?

回答

15

好,x:xs意味着xsx,所以這是行不通的。但是,唉,你不能模式匹配集,因爲集沒有定義的順序。或者更實際一些,因爲Set上沒有提取器。

你總是可以定義自己,雖然:

object SetExtractor { 
    def unapplySeq[T](s: Set[T]): Option[Seq[T]] = Some(s.toSeq) 
} 

例如:

scala> Set(1, 2, 3) match { 
    | case SetExtractor(x, xs @ _*) => println(s"x: $x\nxs: $xs") 
    | } 
x: 1 
xs: ArrayBuffer(2, 3) 
+3

不錯。對於Kevin來說很清楚:因爲在Sets上沒有定義的順序,你應該*不*使用SetExtractor的實際值,例如'Set SetExtractor(1,xs @ _ *)=> ...';它恰好與'Set(1,2,3)'一起工作,但通常不起作用,例如用'Set(1,2,3,4,5)'。丹尼爾提供這種方式可以讓解構綁定從集合中挑選任意元素。還要注意其餘的xs是一個ArrayBuffer,所以如果你想把它作爲一個Set使用'xs.toSet'。 – AmigoNico 2013-03-02 19:40:15

4

首先,您的isEmpty將會捕獲每個Set,因爲它在這種情況下是一個變量。常量以Scala中的大寫字母開頭,如果此條件成立,則只將其作爲常量處理。所以小寫將指派任何SetisEmpty(您尋找的EmptySet?)

可以看出here,似乎是模式匹配不是Set,希望能非常理想的。你應該明確地轉換SetListSeqtoList/toSeq

object Foo { 
    def union(s: Set[Int], t: Set[Int]): Set[Int] = t.toList match { 
     case Nil => s 
     case (x::xs) => union(s + x, xs.toSet) 
     case _  => throw new Error("bad input") 
    } 
} 
+0

另外也backticked東西這也符合價值。 – 2013-03-01 18:02:43

+0

由於將'Set'轉換爲'List',是否有任何緩慢? – 2013-03-01 18:14:51

+0

是的。出於性能考慮,您可以使用寶塔的解決方案。我只是想告訴你如何用模式匹配來完成它。順便說一句,在我的例子中,'_'事件永遠不會到達 – Danyel 2013-03-01 18:40:19

5

Set不是case class並沒有一個unapply方法。

這兩件事意味着你不能直接在Set上模式匹配。
更新:除非你定義自己的提取Set,如丹尼爾正確地顯示在他的回答)

你應該找到一個替代方案,我建議使用摺疊功能

def union(s: Set[Int], t: Set[Int]): Set[Int] = 
    (s foldLeft t) {case (t: Set[Int], x: Int) => t + x} 

這將積累s元素超過t,逐一添加它們


摺疊

下面是docs爲摺疊操作中,如果需要的話以供參考:

foldLeft[B](z: B)(op: (B, A) ⇒ B): B 

適用的二進制運算符的開始值,並且該組中的所有元素,從左到右。

注意:除非訂購了基礎集合類型,否則可能會針對不同的運行返回不同的結果。或者操作員是關聯和交換的。

B the result type of the binary operator. 
z the start value. 
op the binary operator. 
returns the result of inserting op between consecutive elements of this set, going left to right with the start value z on the left: 

op(...op(z, x_1), x_2, ..., x_n) 
where x1, ..., xn are the elements of this set. 
+0

,所以如果'Set'類沒有'unappy()'方法,那麼它不能通過匹配來分解嗎? – 2013-03-01 18:14:15

+1

正確,具有'unapply(...)'方法的對象稱爲*提取器*,其目的是定義如何在「匹配/大小寫」塊中提取其參數。這是爲'case classes'自動生成的。如果可能的話,你應該看到「Programming in Scala 2nd ed。」的* ch.26 *。以供參考。 – 2013-03-01 18:22:31

1

這是我能想出:

object Contains { 
    class Unapplier[T](val t: T) { 
    def unapply(s: Set[T]): Option[Boolean] = Some(s contains t) 
    } 
    def apply[T](t: T) = new Unapplier(t) 
} 

object SET { 
    class Unapplier[T](val set: Set[T]) { 
    def unapply(s: Set[T]): Option[Unit] = if (set == s) Some(Unit) else None 
    } 
    def apply[T](ts: T*) = new Unapplier(ts.toSet) 
} 

val Contains2 = Contains(2) 
val SET123 = SET(1, 2, 3) 

Set(1, 2, 3) match { 
    case SET123()   => println("123") 
    case Contains2(true) => println("jippy") 
    case Contains2(false) => println("ohh noo") 
} 
+0

另請參閱:[編碼風格:如何在戰壕中揮舞斯卡拉](http://parleys.com/play/52a11b33e4b039ad2298ca78/chapter58/about)在0:39:45 – 2014-06-18 16:15:47