5

我正在尝试实现一个默认值 map,我希望过滤器、映射等在 a上也尽可能DefaultingMap产生 a 。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那里是可用的。

我怎样才能让它工作?

4

5 回答 5

5

可变映射Builder在 Scala 中是 s,因此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

于 2010-11-19T13:12:51.223 回答
2

如果您collection.immutable.Map在 2.8 或更高版本中使用,则该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"
于 2010-11-19T11:33:14.153 回答
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找到它,我会被设置...

于 2010-11-19T11:34:57.867 回答
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
}
于 2010-11-19T12:23:19.630 回答
0

Scala 2.8 Collections API是一个非常好的文档,我似乎记得它讨论了转换的这一方面,尽管我不记得确切的位置。我想这不是很有帮助...

于 2010-11-19T11:26:55.417 回答