2014-07-11 122 views
4

有没有办法检查Map是否有定义的默认值?我想是的myMap.getOrElse(x, y)一些等价其中,如果关键x是不是在地图上,如何判断Map是否有默认值?

  • 如果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])。

有没有办法做到这一点,不涉及捕捉异常?

回答

3

您可以检查地图是否是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 

无论这种反思的是比任何使用异常的非更好特殊的控制流程是一个开放的问题。

+0

哦,'Map.WithDefault'就是我一直在寻找的东西。谢谢! – Walfie

+0

请注意,这仍然是一种恶心 - 地图不是静态类型为“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 
+0

它绝对看起来更好!尽管我希望有一种方法,我不必在可能抛出异常的情况下调用'apply'(如果这甚至可能的话) – Walfie

+0

有人可能会纠正我,如果我错了,但它看起来不可能。它是引发异常的'Map'的'default'方法。如果只是它返回一个'Option',取而代之。无论哪种方式,你仍然会做某种检查。 –