2013-03-01 9 views
8

Quanto segue non funziona.Modello di scala che corrisponde ai set

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

come posso Pattern Match su un set?

risposta

15

Beh, x:xs significa x di tipo xs, in modo che non avrebbe funzionato. Ma, ahimè, non puoi modellare i set di corrispondenze, perché gli insiemi non hanno un ordine definito. O, più pragmaticamente, perché non c'è nessun estrattore su Set.

È sempre possibile definire il proprio, però:

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

Ad esempio:

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

Bello. Giusto per essere chiari per Kevin: poiché non esiste un ordine definito sui Set, dovresti * non * usare SetExtractor con valori reali, ad es. 'caso SetExtractor (1, xs @ _ *) => ...'; capita di lavorare con 'Set (1,2,3)' ma non funzionerà in generale, ad es. con 'Set (1,2,3,4,5)'. Daniel offre questo come un modo per consentire a un legatore decostruttivo di scegliere un elemento arbitrario da un Set. Nota anche che il resto, xs, è un ArrayBuffer, quindi se lo vuoi come Set usa 'xs.toSet'. – AmigoNico

4

Prima di tutto, il tuo isEmpty catturerà ogni Set poiché è una variabile in questo contesto. Le costanti iniziano con una lettera maiuscola in Scala e vengono trattate solo come costanti se questa condizione è valida. Così minuscole assegnerà alcun Set-isEmpty (stavi cercando EmptySet?)

Come si è visto here, sembra che il pattern matching non è molto preferibile per Set s. Probabilmente si dovrebbe convertire in modo esplicito il Set ad un List o Seq (toList/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

C'è anche backticked che corrisponde anche per valore. –

+0

ci sono dei rallentamenti dovuti alla conversione di un 'Set' in un' Elenco'? –

+0

Sì. È possibile utilizzare la soluzione pagoda a fini di prestazioni. Volevo solo mostrarti come farlo con il pattern matching. A proposito, nel mio esempio, il caso '_' non viene mai raggiunto – Danyel

5

Set non è un case class e non avere un metodo unapply.

Queste due cose implicano che non è possibile eseguire lo schema di corrispondenza direttamente su Set.
(aggiornamento: a meno che non si definisce il proprio estrattore per Set, come Daniel mostra correttamente nella sua risposta)

Si dovrebbe trovare un'alternativa, io suggerirei utilizzando una funzione di piegatura

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

Questo si accumulano gli elementi di s sopra t, aggiungendo uno per uno


pieghevole

Ecco la docs per l'operazione di piegatura, se necessario per riferimento:

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

applica un operatore binario a un valore iniziale e tutti gli elementi di questa serie, andando da sinistra a destra.

Nota: potrebbe restituire risultati diversi per esecuzioni diverse, a meno che il tipo di raccolta sottostante non sia ordinato. o l'operatore è associativo e commutativo.

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

quindi se la classe' Set' non ha il metodo 'unappy()', allora non può essere scomposta tramite una corrispondenza? –

+1

Corretto, un oggetto che ha il metodo 'unapply (...)' è chiamato * extractor *, e il suo scopo è definire come estrarne i parametri in un blocco 'match/case'. Questo è automatico per le "classi del caso". Se possibile, dovresti vedere * ch.26 * di "Programming in Scala 2nd ed." per riferimento. –

1

Questo è quello che posso venire con:

cose
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

vedi anche: [Coding in Style: Come brandire Scala nelle trincee] (http://parleys.com/play/52a11b33e4b039ad2298ca78/chapter58/about) alle 0:39:45 –

Problemi correlati