Сопоставление с более чем одним совпадением

Рассмотрим следующий код Scala.

val a = "both"

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

мне бы хотелось match работать так, что если a == "both"Scala выполнит оба случая. Возможно ли это или есть альтернативы для достижения того, чего я хочу?

5 ответов

Решение

Стандартное сопоставление с образцом всегда будет совпадать только в одном случае. Вы можете приблизиться к тому, что вы хотите, используя тот факт, что шаблоны можно рассматривать как частичные функции (см. Спецификация языка, раздел 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 указала, что он дал пугающе похожий ответ на этот вопрос.)

Рискнув быть капитаном Очевидным, в таком случае было бы проще всего забыть о сопоставлении с образцом и его использовании. 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и немного короче.

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с).

Просто сравните дважды:

val a = "both"

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

Одним из возможных способов может быть:

val a = "both"

a match {
  case "foo" => println ("foo")   // Case 1
  case "bar" => println ("bar")   // Case 2
  case "both" => println ("foo"); println ("bar")
}
Другие вопросы по тегам