【问题标题】:How to tell if a Map has a default value?如何判断 Map 是否有默认值?
【发布时间】:2014-09-02 08:18:03
【问题描述】:

有没有办法检查Map 是否有定义的默认值?我想要的是 myMap.getOrElse(x, y) 的等价物,如果键 x 不在地图中,

  • 如果myMap 有默认值,则返回该值
  • 否则返回y

一个人为的问题示例:

scala> def f(m: Map[String, String]) = m.getOrElse("hello", "world")
f: (m: Map[String,String])String

scala> val myMap = Map("a" -> "A").withDefaultValue("Z")
myMap: scala.collection.immutable.Map[String,String] = Map(a -> A)

scala> f(myMap)
res0: String = world

在这种情况下,我希望 res0"Z" 而不是 "world",因为 myMap 是用它作为默认值定义的。但是getOrElse 不是这样工作的。


我可以使用m.apply 代替m.getOrElse,但不保证映射具有默认值,因此它可能会引发异常(我可以捕获异常,但这并不理想)。

scala> def f(m: Map[String, String]) = try {
     |   m("hello")
     | } catch {
     |   case e: java.util.NoSuchElementException => "world"
     | }
f: (m: Map[String,String])String

scala> val myMap = Map("a" -> "A").withDefaultValue("Z")
myMap: scala.collection.immutable.Map[String,String] = Map(a -> A)

scala> f(myMap)
res0: String = Z

scala> val mapWithNoDefault = Map("a" -> "A")
mapWithNoDefault: scala.collection.immutable.Map[String,String] = Map(a -> A)

scala> f(mapWithNoDefault)
res1: String = world

以上产生了预期值,但看起来很混乱。我无法根据地图是否具有默认值进行模式匹配和调用applygetOrElse,因为无论默认值如何,类型都是相同的(scala.collection.immutable.Map[String,String])。

有没有不涉及捕获异常的方法?

【问题讨论】:

    标签: scala map default-value


    【解决方案1】:

    可以检查地图是否为Map.WithDefault的实例:

    implicit class EnrichedMap[K, V](m: Map[K, V]) {
      def getOrDefaultOrElse(k: K, v: => V) =
        if (m.isInstanceOf[Map.WithDefault[K, V]]) m(k) else m.getOrElse(k, v)
    }
    

    然后:

    scala> val myMap = Map("a" -> "A").withDefaultValue("Z")
    myMap: scala.collection.immutable.Map[String,String] = Map(a -> A)
    
    scala> myMap.getOrDefaultOrElse("hello", "world")
    res11: String = Z
    
    scala> val myDefaultlessMap = Map("a" -> "A")
    myDefaultlessMap: scala.collection.immutable.Map[String,String] = Map(a -> A)
    
    scala> myDefaultlessMap.getOrDefaultOrElse("hello", "world")
    res12: String = world
    

    这种反射是否比对非异常控制流使用异常更好是一个悬而未决的问题。

    【讨论】:

    • 哦,Map.WithDefault 正是我要找的。谢谢!
    • 请注意,这仍然有点棘手——地图不是静态类型为WithDefault,所以我们必须使用反射。这种反射不会造成显着的性能损失,但仍远非理想。
    【解决方案2】:

    您可以使用 Try 代替 try/catch,这样看起来会更干净一些。

    val m = Map(1 -> 2, 3 -> 4)
    
    import scala.util.Try 
    
    Try(m(10)).getOrElse(0)
    
    res0: Int = 0
    
    val m = Map(1 -> 2, 3 -> 4).withDefaultValue(100)
    
    Try(m(10)).getOrElse(0)
    
    res1: Int = 100
    

    【讨论】:

    • 它看起来确实更好看!虽然我希望有一种方法可以让我不必在可能引发异常的情况下调用apply(如果可能的话)
    • 如果我错了,有人可能会纠正我,但看起来不可能。引发异常的是Mapdefault 方法。如果只是它返回一个Option,而不是。无论哪种方式,您仍然需要进行某种检查。
    猜你喜欢
    • 2016-04-27
    • 2013-01-17
    • 2012-01-05
    • 1970-01-01
    • 2012-01-29
    • 2019-04-26
    • 1970-01-01
    • 2018-09-15
    • 2018-05-06
    相关资源
    最近更新 更多