2011-09-27 16 views
11

Należy rozważyć następujący kod Scala.Dopasowywanie wzorców z więcej niż jednym dopasowaniem

val a = "both" 

a match { 
    case "both" | "foo" => println ("foo") // case 1 
    case "both" | "bar" => println ("bar") // case 2 
} 

Chciałbym match do pracy tak, że jeśli a == "both", Scala wykona oba przypadki. Czy to możliwe, czy istnieją alternatywy, aby osiągnąć to, czego chcę?

+0

możliwe duplikat [Match "fallthrough": wykonywanie tego samego fragmentu kodu na więcej niż jednym przypadku] (http://stackoverflow.com/questions/2325863/match- przewrót-wykonanie-ten sam kawałek-za-kod-za-więcej niż jeden-przypadek) – nawfal

Odpowiedz

25

Standardowe dopasowanie do wzoru będzie zawsze pasować tylko na dokładnie jednym przypadku. można zbliżyć się do tego, co chcesz, korzystając z faktu, że wzory mogą być traktowane jako funkcja częściowa (patrz Language Specification sekcja 8.5, Pattern Matching anonimowe Funkcje) oraz poprzez zdefiniowanie własnego operatora dopasowania, choć:

class MatchAll[S](scrutinee : =>S) { 
    def matchAll[R](patterns : PartialFunction[S,R]*) : Seq[R] = { 
    val evald : S = scrutinee 
    patterns.flatMap(_.lift(evald)) 
    } 
} 

implicit def anyToMatchAll[S](scrut : =>S) : MatchAll[S] = new MatchAll[S](scrut) 

def testAll(x : Int) : Seq[String] = x matchAll (
    { case 2 => "two" }, 
    { case x if x % 2 == 0 => "even" }, 
    { case x if x % 2 == 1 => "neither" } 
) 

println(testAll(42).mkString(",")) // prints 'even' 
println(testAll(2).mkString(",")) // prints 'two,even' 
println(testAll(1).mkString(",")) // prints 'neither' 

Składnia jest nieco inna niż zwykle, ale dla mnie taka konstrukcja jest wciąż świadkiem mocy Scali.

Twój przykład jest teraz zapisana jako:

// prints both 'foo' and 'bar' 
"both" matchAll (
    { case "both" | "foo" => println("foo") }, 
    { case "both" | "bar" => println("bar") } 
) 

(Edytujhuynhjl wskazał, że dał przerażająco podobną odpowiedź this question.)

+1

To jest niezwykle czyste rozwiązanie. Dobra robota! –

+1

+1 dla "scrutinee" –

+1

To przypomina mi http://stackoverflow.com/questions/6720205/idiomatic-way-to-convert-a-seqb/6720659#6720659. Jakie są zalety nazwy "=> S'? – huynhjl

0

Jedną z możliwości może być:

val a = "both" 

a match { 
    case "foo" => println ("foo") // Case 1 
    case "bar" => println ("bar") // Case 2 
    case "both" => println ("foo"); println ("bar") 
} 
3

match wykonuje jeden, i tylko jeden z przypadków, więc nie można tego zrobić jako or w meczu. Można jednak skorzystać z listy i map/foreach:

val a = "both" 
(a match { 
    case "both" => List("foo", "bar") 
    case x => List(x) 
}) foreach(_ match { 
    case "foo" => println("foo") 
    case "bar" => println("bar") 
}) 

i nie jesteś powielanie któregokolwiek z ważnego kodu (w tym przypadku println ów).

6

zagrożonych Captain Obvious, w przypadku w ten sposób najprościej byłoby zapomnieć o dopasowywaniu wzorców i użyć if.

if (a == "both" || a == "foo") println("foo") 
if (a == "both" || a == "bar") println("bar") 

Jeśli powtórzenie a == cię martwi, można zamiast pisać

if (Set("both", "foo")(a)) println("foo") 
if (Set("both", "bar")(a)) println("bar") 

wykorzystując fakt, że metoda apply na Set działa tak samo jak contains i jest nieco krótszy.

1

Wystarczy dopasować dwukrotnie:

val a = "both" 

a match { 
    case "both" | "foo" => println ("foo") // Case 1 
} 
a match { 
    case "both" | "bar" => println ("bar") // Case 2 
} 
Powiązane problemy