Использование случая совпадения Scala со списками - PullRequest
1 голос
/ 16 октября 2019

Я хотел бы использовать оператор match case, используя списки. Вот мой пример использования:

val test: String = "test3"
val option: String = getMyOption(test).getOrElse("undefined")
test match {
  case `test1` | `test2` => doSomething(test, option, param1)
  case `test3` | `test4` | `test5` => doSomethingElse(test, param1, param2)
  case `test6` => doMyJob()
  case `test7` => doMyJob(param2, param3)
  case other =>
    throw getUnsupportedTestOperation(other)
}

Я попробовал следующее, но кажется, что функции проверяются перед сопоставлением, потому что я получаю NullPointerException от функции doSomething(test, option) (что не предполагалосьдля вызова в случае test3).

def matchList[T](string: String, cases: => Map[Array[String], T], exception: => Exception): T = {
  val matched: Option[T] = cases collectFirst {
    case (list, matching) if list.contains(string) => matching
  }
  if(matched.isDefined) matched.get else throw exception
}

val mapping1: Array[String] = Array("test1", "test2")
val mapping2: Array[String] = Array("test3", "test4", "test5")
val mapping3: Array[String] = Array("test6")
val mapping4: Array[String] = Array("test7")
val option: String = getMyOption(test).getOrElse("undefined")
val testMapping: Map[Array[String], Unit] = Map(
  mapping1 -> doSomething(test, option, param1),
  mapping2 -> doSomethingElse(test, param1, param2),
  mapping3 -> doMyJob(),
  mapping4 -> doMyJob(param2, param3)
)
matchList(test, testMapping, getUnsupportedTestOperation(other))

В этом случае я передаю Unit функции, такие как обратные вызовы, но я также хотел бы использовать другие типы (например: String, Array[String] ..). Что мне не хватает? Как можно избежать оценки содержания Map перед сопоставлением? Можно ли использовать списки в операторе match case? Или есть более простой способ добиться этого? (Я бы хотел избежать использования оператора if внутри match case.)

1 Ответ

2 голосов
/ 16 октября 2019

Все ваши doXXX вызовы будут происходить при создании testMapping, если вы фактически не сделаете их значениями функций типа () => ().

Вот простой способ достичь того, что вы хотите:

import scala.collection.immutable.ListMap

class A
class B1 extends A
class B2 extends A

def do1(value: String): A = new B1
def do2(value: String, anotherValue: String): B2 = new B2
def do3(): A = new A

val matchClause = ListMap(
  Set("test1", "test2") -> do1 _,
  Set("test3") -> (do2(_: String, "your other value"))
   Set("test4") -> (_: String) => do3()
)

val test = "test1"
val a: Option[A] = matchClause.find(_._1.contains(test)).map { case (_, method) => method(test) }

Обратите внимание на использование ListMap вместо Map. Необходимо убедиться, что перекрывающиеся случаи выполняются в том порядке, в котором были объявлены предложения, как это было бы для обычного сопоставления с образцом.

Конечно, все это очень уродливо, и я бы советовал против этого, если толькоВаш исходный образец соответствия становится действительно невозможным для чтения. Сравните мой ответ с вашим первым блоком кода: простой match является намного яснее , а также весьма вероятен более эффективен .

...