如何匹配一组可能的值?

时间:2014-10-29 21:40:16

标签: scala

我有一个集合,我希望将另一个变量与其任何一个元素相匹配。我知道我可以这样手动执行此操作:

fruits = Set("a", "b", "c", "d")
toMatch = ("a", "fruit")
toMatch match {
   case (("a" | "b" | "c" | "d", irrelevant)) => true
}

但是有没有办法在比赛声明中使用水果,所以我不必手动扩展它

编辑:我目前正在使用if条件来执行此操作,我想知道是否有一些语法糖我可以用它来内联

fruits = Set("a", "b", "c", "d")
toMatch = ("a", "fruit")
toMatch match {
   case ((label, irrelevant)) if fruits.contains(label) => true
}

如果没有其他答案,我会将第一个回复的人标记为解决方案!抱歉,那里缺乏清晰度。

EDIT2:如果你想知道的原因是

fruits = Set("a", "b", "c", "d")
vegetables = Set("d", "e", "f")
toMatch = ("a", "fruit")
toMatch match {
   case ((label, "fruit")) if fruits.contains(label) => true
   case ((label, "vegetable")) if vegetables.contains(label) => true
}

我想结合这两种情况,因此我对每种返回类型都有一个条件

4 个答案:

答案 0 :(得分:1)

您可以使用匹配

中的'if'语句来执行此操作
  val fruits = Set("a", "b", "c", "d")
  val toMatch = ("a", "otherVar")

  toMatch match {
    case (fruit, _) if fruits.contains(fruit) => true
  }

答案 1 :(得分:1)

如果您只需要这两种情况,那么使用'如果'或@Eugene答案就足够了,但对于更多情况我会使用Map:

val fruits = Set("a", "b", "c", "d")
val vegetables = Set("d", "e", "f")
val meats = Set("q", "w")

val food = Map("fruits" -> fruits, "vegetables" -> vegetables, "meats" -> meats)

val toMatch = ("a", "fruit")

scala> food(toMatch._2)(toMatch._1)
res3: Boolean = true

答案 2 :(得分:1)

你可以编写一个自定义提取器来获得你想要的语法糖:

object FruitAndVegetable {
  val fruits = Set("a", "b", "c", "d")
  val vegetables = Set("d", "e", "f")

  def main(args: Array[String]): Unit = {
    List(
      "a" -> "fruit",
      "a" -> "vegetable",
      "d" -> "fruit",
      "d" -> "vegetable",
      "e" -> "fruit",
      "f" -> "vegetable"
    ) foreach {
      toMatch =>
        val result = toMatch match {
          case CustomExtractor(`fruits`, "fruit") => "found a fruit"
          case CustomExtractor(`vegetables`, "vegetable") => "veggy"
          case _ => "Neither fruit nor veggy"
        }
        println(result)
    }
  }

  object CustomExtractor {
    def unapply(toMatch: (String, String)): Option[(Set[String], String)] =
      if ((fruits contains toMatch._1) && toMatch._2 == "fruit") Some(fruits -> toMatch._2)
      else if (vegetables contains toMatch._1) Some(vegetables -> toMatch._2)
      else None
  }

}

但请注意,这种方法存在三个问题(可能更多我无法想到的):

  • CustomExtractor依赖于fruitsvegetables集合,以及"fruit"。原因:我们想从Set[String]中提取String(因为我们与集合匹配),因此我们需要找到一个包含字符串的集合,即fruits和{{ 1}}。由于集合不是不同的,我们还需要知道在vegetables包含在两个集合中的情况下返回哪个集合(此处:toMatch._1)。在这种情况下,我们需要查看"d"

  • toMatch._2仍然需要实施"不公平"匹配(虽然你可以比我做的更优雅)

  • 您的CustomExtractor - 陈述受到污染"通过对象标识符case(或任何你想要的名称)

如果您有很多CustomExtractor - 与这些值匹配的语句,我只会考虑这个解决方案。在这种情况下,您只需要编写一次提取器并可以重复使用它。

PS :我对提取器不是很熟悉,所以也许你可以想出一个更好的方法来解决这个问题,使用提取器。

答案 3 :(得分:0)

这是您可以使用的另一种自定义unapply方法,它更安全。

object CustomExtractor {
  case class Group(label: String, values: Set[String])
}

class CustomExtractor {
  private[this] val labelMap = mutable.HashMap[String, CustomExtractor.Group]()

  def newMatcher(label: String, values: String*): CustomExtractor.Group = {
    if (labelMap.contains(label)) {
      throw new IllegalArgumentException(s"Label Already Mapped: ${labelMap(label)}")
    }

    labelMap.getOrElseUpdate(label, new CustomExtractor.Group(label, Set(values: _*)))
  }

  def unapply(toMatch: (String, String)): Option[CustomExtractor.Group] = {
    val (value, label) = toMatch
    labelMap.get(label).filter(_.values.contains(value))
  }
}

val extractor = new CustomExtractor
val fruits = extractor.newMatcher("fruit", "a", "b", "c", "d")
val vegetables = extractor.newMatcher("vegetable", "d", "e", "f")

val tests = List("a" -> "fruit", "a" -> "vegetable", "d" -> "fruit", "d" -> "vegetable", "e" -> "fruit", "f" -> "vegetable")

for (test <- tests) {
  test match {
    case extractor(`fruits`) => println(s"$test is definitely a fruit.")
    case extractor(group) => println(s"$test was matched to ${group.label}.")
    case _ => println(s"$test was not matched!")
  }
}