Сопоставление с более чем одним совпадением - PullRequest
11 голосов
/ 27 сентября 2011

Рассмотрим следующий код 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 ]

25 голосов
/ 27 сентября 2011

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

( Edit huynhjl указал, что он дал пугающе похожий ответ на этот вопрос .)

6 голосов
/ 27 сентября 2011

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

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

1 голос
/ 30 июля 2012

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

val a = "both"

a match {
    case "both" | "foo" => println ("foo")   // Case 1
}
a match {
    case "both" | "bar" => println ("bar")   // Case 2
}
0 голосов
/ 27 сентября 2011

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

val a = "both"

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