【问题标题】:How to get Map default value if it exist如果存在,如何获取地图默认值
【发布时间】:2017-08-15 19:21:37
【问题描述】:

我想检索 Map 的默认值(如果存在)。这是我尝试过的:

def defaultValue[A, B](m: Map[A, B]): Option[B] = {
  m match {
    case m: Map.WithDefault[A, B] => Some(m.default(null))
    case _ => None
  }
}

这不起作用,因为null 不是A 的子类型。如何获取具有常量函数的Map.WithDefault的默认值?

我的尝试

产生A 类型的随机值?默认值是使用常量函数编码的,这就是它需要参数的原因,但在我的情况下,不应该使用参数。此外,当我输入Nothing 类型的??? 之类的内容时,它会在尝试返回常量值之前评估参数,从而引发异常。

我的问题与this question 有关,但它们没有提供直接检索默认值的方法。

【问题讨论】:

  • 为什么要依赖具有默认值的地图?为什么不map.get(key).getOrElse(myDefault)
  • 我正在将 scala 的集合转换为其他类型的集合,这些集合也可以具有默认值(但没有函数)。这就是为什么我需要检索默认值。我不需要特定键的值。
  • 没有默认值的地图。具有默认值的地图有一个默认函数,它可能是常数,也可能不是常数。这就是default 方法需要一个参数的原因。

标签: scala scala-collections


【解决方案1】:

您可以添加一个下限,将您的方法签名重写为:

def defaultValue[A >: Null <: AnyRef, B](m: Map[A, B]): Option[B]

或者您也可以将null 转换为Anull.asInstanceOf[A]

case m: Map.WithDefault[A, B] => Some(m.default(null.asInstanceOf[A]))

并不是说这个版本也适用于A &lt;: AnyVal(请记住,例如null.asInstanceOf[Int] == 0)。


还有一点需要注意:您可以匹配 case _: Map.WithDefault[_, _],因为无法在运行时检查此类型测试中的外部引用。

【讨论】:

  • 很好。当 A 可能是原始类型(例如 Integer、Boolean、Char 或其他任何类型)时,我将如何处理这种情况?
  • 只使用第二个版本,即带有null.asInstanceOf[A]的版本,不添加下限。
【解决方案2】:

我认为你可以通过在任意键上调用 #default 来做到这一点:

val map = Map(1 -> "a").withDefaultValue("b")

map(1)         // yields "a"
map.default(1) // yields "b"

虽然不是那么好,如果没有默认值会抛出异常,所以你需要将它包装在Try中,然后使用模式匹配来提取它。

Try(map.default(1)).toOption match {
  case Some(value) => // do something with the value
  case _           => // do something else
}

【讨论】:

  • 如果我知道类型,这是一个很好的解决方案。但是,我正在使用泛型,我没有泛型类型 A 的值。
  • 恐怕你做不到。考虑到如果A 至少是AnyRef 的子类型,你现在不能将null 转换为实例,并且由于Nothing 没有实例,我认为你不能这样做。
【解决方案3】:

您可以使用类型类模式来处理您的问题。您收到错误,因为 A 可以是 AnyVal 类,如 Int、Float 等。但您可以简单地克服此限制,请考虑以下事项:

trait DummyProvider[T] {
  def dummy: T
}

implicit def anyRefDummy[T >: Null] = new DummyProvider[T] {
  def dummy: T = null
}

implicit def numericDummy[T](implicit ev: Numeric[T]) = new DummyProvider[T] {
  def dummy: T = ev.zero
}
//probably needs to add instance for boolean

现在你需要在你的代码中做这样的事情:

def defaultValue[A, B](m: Map[A, B])(implicit ev: DummyProvider[A]): Option[B] = {
  m match {
    case m: Map.WithDefault[A, B] => Some(m.default(ev.dummy))
    case _ => None
  }
}

【讨论】:

  • 还不错,但总是很难将隐式置于范围内。
【解决方案4】:

这是不可能的

一般来说这是不可能的,因为默认的“值”实际上是一个函数。所以一般情况下没有单一的默认值。

以下来源来自 Scala 2.12.*。

scala.collection.immutable.Map中存在以下内容:

  /** The same map with a given default function.
   *  Note: `get`, `contains`, `iterator`, `keys`, etc are not affected by `withDefault`.
   *
   *  Invoking transformer methods (e.g. `map`) will not preserve the default value.
   *
   *  @param d     the function mapping keys to values, used for non-present keys
   *  @return      a wrapper of the map with a default value
   */
def withDefault[V1 >: V](d: K => V1): immutable.Map[K, V1] = new Map.WithDefault[K, V1](this, d)

这是来自scala.collection.Map

  /** An abstract shell used by { mutable, immutable }.Map but not by collection.Map
   *  because of variance issues.
   */
  abstract class WithDefault[K, +V](underlying: Map[K, V], d: K => V) extends AbstractMap[K, V] with Map[K, V] with Serializable {
    override def size               = underlying.size
    def get(key: K)                 = underlying.get(key) // removed in 2.9: orElse Some(default(key))
    def iterator                    = underlying.iterator
    override def default(key: K): V = d(key)
}

【讨论】:

  • 感谢您的解释,但我更多的是在寻找解决方法,而不是说这是不可能的。查看接受的答案。
猜你喜欢
  • 2011-06-26
  • 2023-03-03
  • 2018-03-11
  • 2022-12-15
  • 2021-09-18
  • 2014-08-07
  • 2012-07-31
  • 2012-03-16
  • 2019-09-26
相关资源
最近更新 更多