在 Scala 匿名函数中是否有更简洁的模式匹配方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6458423/
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 03:08:02  来源:igfitidea点击:

Is there a cleaner way to pattern-match in Scala anonymous functions?

scalapattern-matching

提问by Owen

I find myself writing code like the following:

我发现自己编写的代码如下:

val b = a map (entry =>
    entry match {
        case ((x,y), u) => ((y,x), u)
    }
)

I would like to write it differently, if only this worked:

我想以不同的方式写它,如果这有效的话:

val c = a map (((x,y) -> u) =>
    (y,x) -> u
)

Is there any way I can get something close to this?

有什么办法可以让我接近这个吗?

回答by sblundy

Believe it or not, this works:

信不信由你,这是有效的:

val b = List(1, 2)
b map {
  case 1 => "one"
  case 2 => "two"
}

You can skip the p => p matchin simple cases. So this should work:

p => p match在简单的情况下,您可以跳过。所以这应该有效:

val c = a map {
  case ((x,y) -> u) => (y,x) -> u
}

回答by Aaron Novstrup

In your example, there are three subtly different semantics that you may be going for.

在您的示例中,您可能需要三种微妙不同的语义。

  1. Map over the collection, transforming each element that matches a pattern. Throw an exception if any element does not match. These semantics are achieved with

    val b = a map { case ((x, y), u) => ((y, x), u) }
    
  2. Map over the collection, transforming each element that matches a pattern. Silently discard elements that do not match:

    val b = a collect { case ((x, y), u) => ((y, x), u) }
    
  3. Map over the collection, safely destructuring and then transforming each element. These are the semantics that I would expect for an expression like

    val b = a map (((x, y), u) => ((y, x), u)))  
    

    Unfortunately, there is no concise syntax to achieve these semantics in Scala. Instead, you have to destructure yourself:

    val b = a map { p => ((p._1._2, p._1._1), p._2) }
    

    One might be tempted to use a value definition for destructuring:

    val b = a map { p => val ((x,y), u) = p; ((y, x), u) }
    

    However, this version is no more safe than the one that uses explicit pattern matching. For this reason, if you want the safe destructuring semantics, the most concise solution is to explicitly type your collection to prevent unintended widening and use explicit pattern matching:

    val a: List[((Int, Int), Int)] = // ...
    // ...
    val b = a map { case ((x, y), u) => ((y, x), u) }
    

    If a's definition appears far from its use (e.g. in a separate compilation unit), you can minimize the risk by ascribing its type in the map call:

    val b = (a: List[((Int, Int), Int)]) map { case ((x, y), u) => ((y, x), u) }
    
  1. 在集合上映射,转换与模式匹配的每个元素。如果任何元素不匹配,则抛出异常。这些语义是通过

    val b = a map { case ((x, y), u) => ((y, x), u) }
    
  2. 在集合上映射,转换与模式匹配的每个元素。静默丢弃不匹配的元素:

    val b = a collect { case ((x, y), u) => ((y, x), u) }
    
  3. 映射集合,安全地解构然后转换每个元素。这些是我期望的表达式的语义

    val b = a map (((x, y), u) => ((y, x), u)))  
    

    不幸的是,Scala 中没有简洁的语法来实现这些语义。相反,你必须自己解构:

    val b = a map { p => ((p._1._2, p._1._1), p._2) }
    

    人们可能会倾向于使用值定义进行解构:

    val b = a map { p => val ((x,y), u) = p; ((y, x), u) }
    

    然而,这个版本并不比使用显式模式匹配的版本更安全。出于这个原因,如果您想要安全的解构语义,最简洁的解决方案是显式键入您的集合以防止意外扩大并使用显式模式匹配:

    val a: List[((Int, Int), Int)] = // ...
    // ...
    val b = a map { case ((x, y), u) => ((y, x), u) }
    

    如果a的定义与它的使用相去甚远(例如在单独的编译单元中),您可以通过在 map 调用中归因其类型来最小化风险:

    val b = (a: List[((Int, Int), Int)]) map { case ((x, y), u) => ((y, x), u) }
    

回答by Kevin Wright

In your quoted example, the cleanest solution is:

在您引用的示例中,最干净的解决方案是:

val xs = List((1,2)->3,(4,5)->6,(7,8)->9)
xs map { case (a,b) => (a.swap, b) }

回答by Daniel C. Sobral

val b = a map { case ((x,y), u) => ((y,x), u) }