2016-08-17 73 views
2

我有一个案例类,有多个参数,其中一些是选项。这里是一个简化的例子:案例分类有选项参数和案例匹配

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

我希望能够匹配foobar案件,其中b和/或c不是无。例如,一种情况可能是:

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") 
    } 
}) 
+0

你为什么会想要在案例类中匹配变量名?我不认为这是必要的。你的第一种方法有什么问题? –

+0

我没有匹配变量名称。我试图将模式定义移到匹配块之外,因为实际的代码更加复杂,我需要以编程方式分配模式。 – beetea

+0

'val a = Foo(x)'是intantiaing类的语法。这里不能使用'_'这样的通配符模式匹配器。 –

回答

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") 
} 
+0

我认为这是我需要的,但我试图理解这是如何工作的。具体来说,'myFunction'被定义为'Foobar(s,None,None)'的一个实例,所以这并不意味着'checker.isDefinedAt(myFunction(input))'只有在最后两个Options为None ?编辑:哦,那只是一个例子。我想我现在明白了。 – beetea

+0

在这种情况下是的。但是你的函数返回依赖于“输入”的Foobar类的动态结果。它可以是Foobar类的任何实例。 – user1303559

+0

是的,我现在明白了。谢谢。我仍然在学习Scala,并且没有在函数中使用“返回”语句,但这些语句仍然不时出现。 – beetea

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") 
    } 
    } 
}) 
+0

谢谢,这将变得复杂的案例类与许多参数,与多个选项(即我需要创建大量的TestExtractor对象)。但是,这确实给了我另一个想法。我想我应该把这些模式包装在一个验证函数中。 – beetea