案例类带选项参数&案例匹配

时间:2016-08-17 04:17:41

标签: scala

我有一个包含多个参数的案例类,其中一些是Options。这是一个简化的例子:

case class Foobar(a: String, b: Option[String], c: Option[CustomClass])

我希望能够匹配Foobar的情况,其中b和/或c不是None。例如,一个案例可能是:

testResult match {
    case Foobar("str1", Some(_), None) => "good"
    case Foobar("str2", None, Some(_)) => "ok"
    case _ => "bad"
}

此外,我想通过变量引用案例模式,这就是我被困的地方。我想做类似以下的事情:

val goodPat = Foobar("str1", Some(_), None) // compile fail
val okPat = Foobar("str2", None, Some(_)) // compile fail
testResult match {
    case `goodPat` => "good"
    case `okPat` => "ok"
    case _ => "bad"
}

这样的事情可能吗?还有另一种方法来指定"不是没有"?有没有其他方法可以解决这个问题?

编辑:我在问题中添加了更多细节和背景信息。我有一个2元组的大型列表,表示特定函数的单元测试。 2元组表示输入和预期输出。例如

// imagine this list is much bigger and Foobar contains more Option parameters
val tests = List(
    ("test1", Foobar("idkfa", None, None)),
    // I know these fail to compile but I need to do something like this
    ("test2", Foobar("idclip", Some("baz"), Some(_)),
    ("test3", Foobar("iddqd", Some(_), None)
)

tests.foreach(test => {
    val (input, expected) = test
    myFunction(input) match {
        case `expected` => println("ok")
        case _ => println("bad")
    }
})

2 个答案:

答案 0 :(得分:2)

我认为你在寻找类似的东西:

case class DoomOpt(s: String)
case class Foobar(a: String, b: Option[String], c: Option[DoomOpt])

def myFunction(s: String): Foobar = { // your code here }

val tests = Map[String, PartialFunction[Foobar, Unit]](
    "idkfa" → { case Foobar("idkfa", None, None) ⇒ },
    "test2" → { case Foobar("idclip", Some("baz"), Some(_)) ⇒ },
    "test3" → { case Foobar("iddqd", Some(_), None) ⇒ },
    "idspispopd" → { case Foobar("idspispopd", Some(_), None) ⇒ }
  )

tests.foreach { case (input, checker) =>
  if (checker.isDefinedAt(myFunction(input)))
    println("ok")
  else
    println("bad")
}

答案 1 :(得分:1)

模式匹配使用提取器,它提供unapply函数来解构对象。所以......在这种情况下你可以提供自定义提取器。创建这些提取器测试用例的列表并逐个应用它们。

case class Foobar(s: String, o: Option[Int])

trait TestExtractor {
  def unapply(fbar: Foobar): Boolean
}

object somePatExtractor extends TestExtractor {
  def unapply(fbar: Foobar): Boolean = fbar match {
    case Foobar("yes", Some(_)) => true
    case _ => false
  }
}

object nonePatExtractor extends TestExtractor {
  def unapply(fbar: Foobar): Boolean = fbar match {
    case Foobar("yes", None) => true
    case _ => false
  }
}

object bazPatExtractor extends TestExtractor {
  def unapply(fbar: Foobar): Boolean = fbar match {
    case Foobar("yes", Some("baz")) => true
    case _ => false
  }
}


val testList: List[(String, TestExtractor)] = List(("test1", nonePatExtractor), ("test2", bazPatExtractor), ("test3", somePatExtractor))

val fooToTest = Foobar("yes", Some(5))

testList.foreach({
  case (testName, extractor) => {
    fooToTest match {
      case pat @ extractor() => println("testName :: " + testName + ", Result :: ok")
      case _ => println("testName :: " + testName + ", Result :: bad")
    }
  }
})

如果您正在寻找更加可扩展的方法,那么您可以考虑以下内容,

case class Foobar(s: String, o1: Option[Int], o2: Option[String])

case class TestCondition(df: Foobar => Boolean) {
  def test(foobar: Foobar): Boolean = df(foobar)
}

val o1IsNone = TestCondition(f => f.o1.isEmpty)
val o1IsSome = TestCondition(f => f.o1.isDefined)

val o2IsNone = TestCondition(f => f.o2.isEmpty)
val o2IsSome = TestCondition(f => f.o2.isDefined)

case class TestCase(tcs: List[TestCondition]) {
  def test(foobar: Foobar) = tcs.foldLeft(true)({ case (acc, tc) => acc && tc.test(foobar) })
}

val testList = List[(String, TestCase)](
  ("test1", TestCase(List(o1IsSome, o2IsSome))),
  ("test2", TestCase(List(o1IsSome, o2IsNone))),
  ("test3", TestCase(List(o1IsNone, o2IsSome))),
  ("test4", TestCase(List(o1IsNone, o2IsNone)))
)

val foobarToTest = Foobar("yes", Some(5), None)

testList.foreach({
  case (testName, testCase) => {
    foobarToTest match {
      case foobar: Foobar if testCase.test(foobar) => println("testName :: " + testName + ", Result :: ok")
      case _ => println("testName :: " + testName + ", Result :: bad")
    }
  }
})