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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14643567/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 04:54:43  来源:igfitidea点击:

Scala pattern matching: How to match on an element inside a list?

listscalapattern-matching

提问by Alex Vayda

Is it possible to rewrite the following code using 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

回答by Frank

You can add ifconditions to matches like this:

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

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

However, it is not the nicest way, as each ifcheck needs to traverse the list, so this doesn't scale well. There are various different ways to deal with this problem, but we would need to know more about your intensions, as normally the runtime semantics would differ from your code (for example, you could recursively traverse the list looking for either "foo" or "bar", but that would assume you only have either one in the list).

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

回答by Marius Danila

You could implement this using a function like

您可以使用类似的函数来实现这一点

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

And use it like this:

并像这样使用它:

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

回答by Danyel

As Frank's answer says, it is possible, but expensive if you would do it the dirty way.

正如弗兰克的回答所说,这是可能的,但如果您以肮脏的方式进行操作,则成本会很高。

It depends on what you want to do. Do you want to return the index of that "foo" or "bar" (for example)? Then you would do something like this:

这取决于你想做什么。你想返回那个“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
}

This code is not tested, but you get the idea.

这段代码没有经过测试,但你明白了。

回答by pagoda_5b

If what you need is some sort of command execution with prioritizationI can suggest

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

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")
  }

}

In this specific implementation no meaningful result is returned (you only go for side effects), but if your cases should return some value, you would find it wrapped in an Optionas the method result.

在这个特定的实现中,没有返回任何有意义的结果(你只是去寻找副作用),但是如果你的案例应该返回一些值,你会发现它被包裹在Option方法结果中。



UPDATED
based on your additional comment

更新
根据您的附加评论

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

}

This works only if your commands are exclusive, only one should be in the inputList

这仅适用于您的命令是独占的,只有一个应该在input列表中

回答by einnocent

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
}

I believe this to be the most Scalaesque way to do it. The relationship between the cases and their results is concisely stated in the Map, and you have the option of dealing with multiple results however you want. You did say

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

The list is short (up to 4 or 5 items) and can only contain one of the seeking values

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

But some may need to deal with that possibility. If you really, really don't care about multiple matches, you can do

但有些人可能需要处理这种可能性。如果你真的,真的不关心多个匹配,你可以做

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

In either case, it traverses the list only once. Enjoy!

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