Совпадение шаблонов с несколькими совпадениями
Рассмотрим следующий Scala код.
val a = "both"
a match {
case "both" | "foo" => println ("foo") // case 1
case "both" | "bar" => println ("bar") // case 2
}
Я бы хотел, чтобы match
работал так, что если a == "both"
, Scala выполнит оба случая. Возможно ли это или есть какие-то альтернативы для достижения того, чего я хочу?
Ответы
Ответ 1
Стандартное соответствие шаблону всегда будет соответствовать только одному случаю. Вы можете приблизиться к тому, что хотите, используя тот факт, что шаблоны можно рассматривать как частичные функции (см. Спецификация языка, раздел 8.5, шаблон Согласование анонимных функций) и путем определения вашего собственного оператора сопоставления:
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'
Синтаксис немного от обычного, но для меня такая конструкция все еще является свидетелем мощности Scala.
Ваш пример теперь написан как:
// prints both 'foo' and 'bar'
"both" matchAll (
{ case "both" | "foo" => println("foo") },
{ case "both" | "bar" => println("bar") }
)
( Изменить huynhjl указал, что он дал пугающе похожий ответ на этот вопрос.)
Ответ 2
Рискуя быть капитаном Очевидным, в таком случае проще всего забыть совпадение шаблонов и использовать if
.
if (a == "both" || a == "foo") println("foo")
if (a == "both" || a == "bar") println("bar")
Если повторение a ==
вас беспокоит, вы можете вместо этого написать
if (Set("both", "foo")(a)) println("foo")
if (Set("both", "bar")(a)) println("bar")
используя тот факт, что метод apply
на Set
делает то же самое, что и contains
, и немного короче.
Ответ 3
match
выполняет один и только один из случаев, поэтому вы не можете сделать это как or
в матче. Однако вы можете использовать список и 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")
})
И вы не дублируете какой-либо важный код (в данном случае println
s).
Ответ 4
Просто дважды:
val a = "both"
a match {
case "both" | "foo" => println ("foo") // Case 1
}
a match {
case "both" | "bar" => println ("bar") // Case 2
}
Ответ 5
Одним из возможных способов может быть:
val a = "both"
a match {
case "foo" => println ("foo") // Case 1
case "bar" => println ("bar") // Case 2
case "both" => println ("foo"); println ("bar")
}