8

是否可以使用 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
4

5 回答 5

19

您可以if向匹配项添加条件,如下所示:

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

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

于 2013-02-01T09:59:02.640 回答
9

您可以使用类似的函数来实现它

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
)
于 2013-02-01T15:16:52.413 回答
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
}

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

于 2013-02-01T10:18:08.323 回答
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列表中

于 2013-02-01T10:46:32.623 回答
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")

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

于 2015-10-28T20:49:37.483 回答