2011-09-27 5 views
11

Considérons le code Scala suivant.Motif correspondant à plusieurs correspondances

val a = "both" 

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

Je voudrais match travailler de sorte que si a == "both", Scala exécutera les deux cas. Est-ce possible ou existe-t-il des alternatives pour réaliser ce que je veux?

+0

double possible de [Match "fallthrough": l'exécution même morceau de code pour plus d'un cas] (http://stackoverflow.com/questions/2325863/match- fallthrough-execution-same-piece-of-code-for-more-one-case) – nawfal

Répondre

25

La correspondance de motif standard correspond toujours à exactement un cas. Vous pouvez obtenir à ce que vous voulez en utilisant le fait que les modèles peuvent être traités comme des fonctions partielles (voir Language Specification, Section 8.5, Pattern Matching fonctions anonymes) et en définissant votre propre opérateur de correspondance, si:

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' 

La syntaxe est légèrement différente de l'habituel, mais pour moi une telle construction est encore un témoin de la puissance de Scala.

Votre exemple est maintenant écrit:

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

(Modifierhuynhjl a fait remarquer qu'il a donné une réponse effroyablement similaire à this question.)

+1

C'est une solution incroyablement propre. Bien joué! –

+1

+1 pour "scrutinee" –

+1

Cela me rappelle http://stackoverflow.com/questions/6720205/idiomatic-way-to-convert-a-seqb/6720659#6720659. Quel est l'avantage du nom par '=> S'? – huynhjl

0

Une façon possible pourrait être:

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 exécute un, et un seul, des cas, alors vous ne pouvez pas le faire comme or dans le match. Vous pouvez toutefois utiliser une liste et 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") 
}) 

Et vous n'êtes pas dupliquer tout le code importante (dans ce cas, les println s).

6

Au risque d'être le capitaine Obvious, dans un cas comme cela, il serait plus simple d'oublier la correspondance de formes et d'utiliser if.

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

Si la répétition des soucis que vous a ==, vous pouvez écrire à la place

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

utilisant le fait que la méthode apply sur Set fait la même chose que contains, et est un peu plus court.

1

Il suffit de jumeler deux fois:

val a = "both" 

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