在scala模式匹配的case子句中映射表达式

时间:2014-11-20 22:44:39

标签: scala pattern-matching

我的配置值与地图中的某个值匹配,并且根据匹配的值,我采取了操作。以下是我正在尝试做的一些示例代码

val x = 1         // or 2 or 3

val config = Map("c1"-> 1, "c2"-> 2, "c3"-> 3)

x match {
  case config("c1") =>
    println("1")
  case config("c2") =>
    println("2")
  case config("c3") =>
    println("3")
}

现在这应打印1,因为config("c1")评估为1,但它会出错

error: value config is not a case class, nor does it have an unapply/unapplySeq member
                case config("c1") =>

同样适用于其他2例。我为什么要在这里不申请?有什么指针吗?

3 个答案:

答案 0 :(得分:3)

这样的表达式看起来像extractor,因此有关unapply/unapplySeq方法的消息。如果您不想使用提取器但只想匹配普通值,则需要将该值存储在稳定的标识符中 - 您不能将任意表达式用作匹配大小写:

val case1 = config("c1")
x match {
  case case1 => println("1")
  ...
}

答案 1 :(得分:2)

据我所知,在Scala中,x match {case config("c1")被转换为config.unapply(x),其分支取决于unapply方法的结果。正如 Imm 在他的回答中已经提到的那样,稳定标识符(文字和val)的情况并非如此,我鼓励您使用他的解决方案。

尽管如此,为了向您展示如何使用提取器解决问题,我想发布一个不同的解决方案:

def main(args: Array[String]): Unit = {

  object config {
    val configData = Map("c1" -> 1, "c2" -> 2, "c3" -> 3)

    def unapply(value: Int): Option[String] = configData find (_._2 == value) map (_._1)
  }

  1 to 4 foreach {
    case config("c1") => println("1")
    case config("c2") => println("2")
    case config("c3") => println("3")
    case _ => println("no match")
  }
}

我更改了match foreach以显示不同的结果,但这对实施没有影响。这将打印出来:

1
2
3
no match

如您所见,case config("c1")现在调用unapply方法并检查结果是否为Some("c1")。请注意,这与您使用地图的方式相反:根据值搜索密钥。但是,这是有道理的:如果在地图中,"c1""c2"都映射到1,则1与两者匹配,_匹配所有内容的方式相同,在我们的例子中,即使4未配置。

这里也是一个非常简短的tutorial on extractors。我发现它并不是特别好,因为返回的类型和参数类型都是Int,但它可能有助于您了解正在发生的事情。

答案 2 :(得分:1)

正如其他人所说,使用x match { case config("c1") => ...,scala会查找名为config的提取器(带有unapply方法的内容,该方法采用单个值并返回Optional值) ;以这种方式进行模式匹配似乎是对模式的滥用,我不会为此使用提取器。

就个人而言,我会推荐以下其中一项:

if (x == config("c1"))
  println("1")
else if (x == config("c2"))
  println("2")
else ...

或者,如果您使用匹配语句设置,则可以使用以下条件:

x match {
  case _ if x == config("c1") =>
    println("1")
  case _ if x == config("c2") =>
    println("2")
  case _ if x == config("c3") =>
    println("3")
}

不那么干净;遗憾的是,没有一种方法可以在提取器的字面上调用方法调用。您可以使用反向标记来告诉scala"匹配此变量的值" (而不是默认行为,它将产生名为该变量的值):

val (c1,c2,c3) = (config("c1"), config("c2"), config("c3"))
x match {
  case `c1` =>
    println("1")
  case `c2` =>
    println("2")
  case `c3` =>
    println("3")
}

最后,如果你的目标是反向应用地图,可以试试这个吗?

scala> Map("a" -> 1).map { case (k,v) => (v,k) }
res0: scala.collection.immutable.Map[Int,String] = Map(1 -> a)