我正在尝试实施default valued map,我希望DefaultingMap
上的过滤器,地图等能够尽可能地生成DefaultingMap
。这是我的初步实施:
class DefaultingMap[K, V](defaultValue: => V)
extends mutable.HashMap[K, V]
with mutable.MapLike[K, V, DefaultingMap[K, V]] {
override def empty = new DefaultingMap[K, V](defaultValue)
override def default(key: K): V = {
val result = this.defaultValue
this(key) = result
result
}
}
当我使用DefaultingMap
时,我会获得filter
类型的对象,但在使用map
时却没有:
scala> val counter = new DefaultingMap[Char, Int](0)
counter: DefaultingMap[Char,Int] = Map()
scala> for (c <- "ababcbbb") counter(c) += 1
scala> counter.filter{case (k, v) => v > 1}
res1: DefaultingMap[Char,Int] = Map((a,2), (b,5))
scala> counter.map{case (k, v) => (k, v * 2)}
res2: scala.collection.mutable.HashMap[Char,Int] = Map((a,4), (c,2), (b,10))
这两种方法之间的区别似乎是map
采用隐式CanBuildFrom
。所以我认为我需要在某个地方implicit def
提供CanBuildFrom
。我的第一个直觉是在HashMap中完成的工作:
object DefaultingMap extends generic.MutableMapFactory[DefaultingMap] {
def empty[K, V]: DefaultingMap[K, V] = // Not possible!
implicit def canBuildFrom[K, V]:
generic.CanBuildFrom[Coll, (K, V), DefaultingMap[K, V]] =
new MapCanBuildFrom[K, V]
}
我相信这会让它编译,但这种方法不起作用,因为无法定义empty
方法 - 你需要知道defaultValue
应该是什么。如果我可以在类本身中定义CanBuildFrom
而不是伴随对象,我会没事的,因为那里有defaultValue
。
我怎样才能让它发挥作用?
答案 0 :(得分:5)
可变映射在Scala中是Builder
,因此默认情况下MapFactory
采用所讨论类型的空映射来获取构建器。
如果您有自定义地图构建规则,您可以做的一件事就是定义类似于collection.generic.MapFactory
的自定义工厂。您必须以与此类似的方式定义它,但同时使empty
方法和newBuilder
方法为defaultValue
采用其他参数。
有些内容(如果您在建议的其他链接中阅读有关Scala 2.8集合API的更多信息,您会发现您不必为地图实现通用伴随对象):
import collection._
class DefaultingMap[K, V](val defaultValue: V)
extends mutable.HashMap[K, V]
with mutable.MapLike[K, V, DefaultingMap[K, V]] {
override def empty = new DefaultingMap(defaultValue)
}
object DefaultingMap {
def newBuilder[K, V](d: V): DefaultingMap[K, V] = new DefaultingMap[K, V](d)
implicit def canBuildFrom[K, V] =
new generic.CanBuildFrom[DefaultingMap[K, V], (K, V), DefaultingMap[K, V]] {
def apply(from: DefaultingMap[K, V]) = newBuilder[K, V](from.defaultValue)
def apply() = error("unsupported default apply")
}
}
object Main {
def main(args: Array[String]) {
println((new DefaultingMap[Int, Int](5)).defaultValue)
println(((new DefaultingMap[Int, Int](5)).map(x => x)).defaultValue)
}
}
打印:
$ scalac defaulting.scala
$ scala Main
5
5
我承认,这仍然无法解决无参数apply
的问题。
答案 1 :(得分:2)
如果您在2.8或更高版本中使用collection.immutable.Map
,则可以使用withDefault
方法:
val m = collection.immutable.Map(1->"a", 2->"b", 3->"c")
val n = m withDefaultValue "default"
// n(7) will return "default"
<强>更新强>
如果要映射集合,请将withDefaultValue
移到处理链的末尾:
val o = (for ((k, v) <- m) yield (k, v)) withDefaultValue "default"
// o(0) will return "default"
答案 2 :(得分:1)
好的,这是一种非常接近我想要的方法:
class DefaultingMap[K, V](defaultValue: => V)
extends mutable.HashMap[K, V]
with mutable.MapLike[K, V, DefaultingMap[K, V]] {
override def empty = new DefaultingMap[K, V](defaultValue)
override def default(key: K): V = {
val result = this.defaultValue
this(key) = result
result
}
implicit def canBuildFrom[NK] =
new generic.CanBuildFrom[DefaultingMap[K, V], (NK, V), DefaultingMap[NK, V]] {
def apply(from: DefaultingMap[K, V]) =
new DefaultingMap[NK, V](from.newDefaultValue)
def apply() =
new DefaultingMap[NK, V](defaultValue)
}
def newDefaultValue = defaultValue
}
现在,如果我将CanBuildFrom
纳入范围,那么一切都像魅力一样:
scala> val counter = new DefaultingMap[Char, Int](0)
counter: DefaultingMap[Char,Int] = Map()
scala> for (c <- "ababcbbb") counter(c) += 1
scala> import counter._
import counter._
scala> counter.map{case (k, v) => (k, v * 2)}
res1: DefaultingMap[Char,Int] = Map((a,4), (c,2), (b,10))
scala> for ((k, v) <- counter; if v > 1) yield (k.toString, v * 2)
res2: DefaultingMap[java.lang.String,Int] = Map((a,4), (b,10))
但是,如果我放弃import counter._
,我会得到与以前相同的行为。如果我能弄清楚如何找到implicit def canBuildFrom
,我会被设置......
答案 3 :(得分:0)
Scala 2.8 Collections API是一个非常好的文档,我似乎记得它在讨论转换的这个方面,但我不记得确切的位置。我想这不是很有帮助......
答案 4 :(得分:0)
它对map
的返回类型没有帮助(但同样,通常不能使用map
转换来定义新集合的默认值)。但是给你另一种方法:
class DefaultHashMap[A, B](dflt: => B) extends scala.collection.mutable.Map[A, B] {
val underlying = new scala.collection.mutable.HashMap[A, B]()
def get(key: A) = underlying.get(key) orElse Some(default(key))
def iterator: Iterator[(A, B)] = underlying.iterator
def +=(kv: (A, B)) = { underlying += kv; this }
def -=(key: A) = { underlying -= key; this }
override def empty: DefaultHashMap[A, B] = new DefaultHashMap[A, B](dflt)
override def default(key: A) = dflt
}