Scala模式匹配:如何匹配列表中的元素?

时间:2013-02-01 09:51:41

标签: list scala pattern-matching

是否可以使用Scala模式匹配重写以下代码?

val ls: List[String] = ??? // some list of strings

val res = if (ls.contains("foo")) FOO
     else if (ls.contains("bar")) BAR
     else SOMETHING_ELSE

5 个答案:

答案 0 :(得分:16)

您可以将if条件添加到匹配项中:

ls match {
  case x if x.contains("foo") => // FOO
  case x if x.contains("bar") => // BAR
  case _ => // ELSE
}

然而,这不是最好的方式,因为每个if检查都需要遍历列表,因此这不能很好地扩展。有各种不同的方法来处理这个问题,但我们需要更多地了解你的内涵,因为通常运行时语义会与你的代码不同(例如,你可以递归遍历列表寻找“foo”或“ bar“,但是假设你在列表中只有一个。”

答案 1 :(得分:8)

您可以使用类似

的功能实现此功能
def onContains[T](xs: Seq[String], actionMappings: (String, T)*): Option[T] = {
  actionMappings collectFirst {
    case (str, v) if xs contains str => v
  }
}

并像这样使用它:

val x = onContains(items,
  "foo" -> FOO,
  "bar" -> BAR
)

答案 2 :(得分:2)

正如弗兰克的回答所说,这是有可能的,但如果你这样做的话会很昂贵。

这取决于你想做什么。你想要返回“foo”或“bar”的索引(例如)吗?然后你会做这样的事情:

def indexOf[T]: (List[T], T) => Int = (ls, x) => ls match {
    case Nil => -1
    case e::es if( e.equals(x) ) => 0
    case e::es => val i = indexOf( es, x ); if( i < 0 ) i else i + 1
}

此代码未经过测试,但您明白了。

答案 3 :(得分:0)

如果你需要的是某种带有优先级的命令执行我可以建议

def executeCommand(input: List[String]): Option[Unit] = {

  val priorities = Map(
    "foo" -> 1,
    "bar" -> 2,
    "baz" -> 3) withDefault(_ => 4)

  def extractCommand(cmds: List[String]): Option[String] = 
    (cmds sortBy priorities).headOption

  extractCommand(input) map {
    case "foo" => println("found foo")
    case "bar" => println("found bar")
    case "baz" => println("found baz")
    case _     => println("no known command")
  }

}

在这个特定的实现中,没有返回有意义的结果(你只是为了副作用),但如果你的情况应该返回一些值,你会发现它包含在Option作为方法结果。


<强>已更新
基于您的附加评论

def execute(input: List[String]): Option[String] = {
  val commands: PartialFunction[String, String] = {
    case "foo" => "result for foo"
    case "bar" => "result for bar"
    case "baz" => "result for baz"
  }

  (input find commands.isDefinedAt) map commands

}

仅当您的命令是独占命令时才有效,只有一个命令应该在input列表

答案 4 :(得分:0)

val ls = List[String]("bar", "foo", "baz")  // stuff to check against
val mappy = Map[String, String]("foo" -> "FOO", "bar" -> "BAR")  // conversions go here

val res = ls.flatMap{
  case x: String => mappy.get(x)
} match {
  case Seq(y) => y
  case Nil => "SOMETHING_ELSE"  // the `else` case goes here
  case _ => new Exception("could be more than one thing")  // handle this however you want
}

我相信这是最可靠的Scalaesque方式。案例及其结果之间的关系在Map中简明扼要地说明,您可以根据需要选择处理多个结果。你确实说过

  

列表很短(最多4个或5个项目),并且只能包含其中一个搜索值

但有些人可能需要处理这种可能性。如果你真的,真的不关心多重比赛,你可以做到

val res = ls.flatMap(mappy.get).headOption.getOrElse("SOMETHING_ELSE")

在任何一种情况下,它只遍历列表一次。享受!