Scala 如何匹配一组可能的值?

Scala 如何匹配一组可能的值?,scala,Scala,我有一个集合,我想将另一个变量与其任何一个元素进行匹配。我知道我可以像这样手动执行此操作: fruits = Set("a", "b", "c", "d") toMatch = ("a", "fruit") toMatch match { case (("a" | "b" | "c" | "d", irrelevant)) => true } 但是有没有办法在match语句中使用水果,这样我就不必手动扩展它了 编辑:我目前正在使用if条件来实现这一点,我想知道是否有一些语法糖可以用

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

fruits = Set("a", "b", "c", "d")
toMatch = ("a", "fruit")
toMatch match {
   case (("a" | "b" | "c" | "d", irrelevant)) => true
}
但是有没有办法在match语句中使用水果,这样我就不必手动扩展它了

编辑:我目前正在使用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
}

我想将这两种情况结合起来,这样每个返回类型都有一个条件

您可以使用match中的'if'语句来实现

  val fruits = Set("a", "b", "c", "d")
  val toMatch = ("a", "otherVar")

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

如果您只需要这两种情况,那么使用“If”或@Eugene answer就足够了,但对于更多情况,我会使用地图:

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

您可以编写自定义提取器来获取所需的语法糖:

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
    依赖于
    水果
    蔬菜
    集合,以及
    水果
    。原因:我们想从
    字符串
    中提取
    集合[String]
    (因为我们匹配集合),所以我们需要找到一个包含该字符串的集合,即
    水果
    蔬菜
    。由于这两个集合不明显,我们还需要知道在
    匹配的情况下返回哪个集合。两个集合中都包含_1
    (此处:
    “d”
    )。在这种情况下,我们需要查看
    toMatch.\u 2

  • CustomExtractor
    仍然需要实现“unvency”匹配(尽管您可以比我做的更优雅)

  • 您的
    case
    -语句被对象标识符
    CustomExtractor
    (或任何您想称之为它的东西)所“污染”
<>我只会考虑这个解决方案,如果你有很多<代码>匹配< /代码> -这些值匹配的语句。在这种情况下,只需编写一次提取器,就可以重用它


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

这里有另一个自定义的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!")
  }
}
对象自定义提取器{
案例类组(标签:String,值:Set[String])
}
类自定义提取器{
private[this]val labelMap=mutable.HashMap[String,CustomExtractor.Group]()
def newMatcher(标签:String,值:String*):CustomExtractor.Group={
if(标签映射包含(标签)){
抛出新的IllegalArgumentException(s“标签已映射:${labelMap(标签)}”)
}
labelMap.GetOrelsUpdate(标签,新CustomExtractor.Group(标签,集合(值:*))
}
def unapply(toMatch:(String,String)):选项[CustomExtractor.Group]={
val(值、标签)=t匹配
labelMap.get(label.filter)(u.values.contains(value))
}
}
val提取器=新的CustomExtractor
val fruits=提取器.newMatcher(“水果”、“a”、“b”、“c”、“d”)
val蔬菜=提取器.newMatcher(“蔬菜”、“d”、“e”、“f”)
val测试=列表(“a”->“水果”、“a”->“蔬菜”、“d”->“水果”、“d”->“蔬菜”、“e”->“水果”、“f”->“蔬菜”)
对于(test println(s“$test绝对是一个水果”)
案例提取器(组)=>println(s“$test与${group.label}匹配。”)
案例=>println(s“$test不匹配!”)
}
}

我目前使用if语句来做这件事,但我想知道是否有一些语法上的糖分来做这件事,你的意思是像
水果.contains(“a”)
(或其他什么)?我目前使用一个if语句来做这件事,但我想知道是否有一些语法糖来做这件事!“if”语句正是它的语法糖
水果(水果)
应该等同于
水果。包含(水果)
问题的原因主要是这样做的,如果toMatch.\u 1匹配集合水果,并且不相关=“a”或toMatch._2与另一组蔬菜和蔬菜匹配“回答正确。对于if语句,我需要使用两个返回相同结果的案例。如果不存在更好的解决方案,我会将您的案例标记为解决方案。实际上,我不确定我是否理解了问题。如果提取独立于第二个元素,那么您可以将
的返回类型unapply
更改为
选项[Set][String]
并删除对
toMatch.\u 2
的检查。显然,这使得第二个元组元素不相关,
“a”->“蔬菜”
将与
水果
以及
“d”->“蔬菜”
“a”->“甚至没有食物”
匹配。