2010-07-06 32 views
8

Tôi thấy mình sử dụng rất nhiều bản đồ lồng nhau, ví dụ Map [Int, Map [String, Set [String]]] và tôi muốn có Maps, Sets mới, vv được tạo tự động khi tôi truy cập một khóa mới. Ví dụ. giống như sau:Thiết kế bản đồ có giá trị mặc định thuận tiện trong Scala

val m = ... 
m(1992)("foo") += "bar" 

Lưu ý rằng tôi không muốn sử dụng getOrElseUpdate đây nếu tôi không phải vì nó được khá dài dòng khi bạn đã lồng bản đồ và che khuất những gì đang thực sự xảy ra trong các mã:

m.getOrElseUpdate(1992, Map[String, Set[String]]()).getOrElseUpdate("foo", Set[String]()) ++= "bar" 

Vì vậy, tôi sẽ ghi đè phương pháp "mặc định" của HashMap. Tôi đã thử hai cách để làm điều này, nhưng không phải là hoàn toàn thỏa đáng. giải pháp đầu tiên của tôi là viết một phương pháp mà tạo ra bản đồ, nhưng có vẻ như tôi vẫn phải xác định đầy đủ lồng Loại bản đồ khi tôi khai báo biến hoặc những thứ không làm việc:

scala> def defaultingMap[K, V](defaultValue: => V): Map[K, V] = new HashMap[K, V] {      | override def default(key: K) = { 
|  val result = defaultValue 
|  this(key) = result 
|  result 
| } 
| } 
defaultingMap: [K,V](defaultValue: => V)scala.collection.mutable.Map[K,V] 

scala> val m: Map[Int, Map[String, Set[String]]] = defaultingMap(defaultingMap(Set[String]())) 
m: scala.collection.mutable.Map[Int,scala.collection.mutable.Map[String,scala.collection.mutable.Set[String]]] = Map() 

scala> m(1992)("foo") += "bar"; println(m)              
Map(1992 -> Map(foo -> Set(bar))) 

scala> val m = defaultingMap(defaultingMap(Set[String]())) 
m: scala.collection.mutable.Map[Nothing,scala.collection.mutable.Map[Nothing,scala.collection.mutable.Set[String]]] = Map() 

scala> m(1992)("foo") += "bar"; println(m) 
<console>:11: error: type mismatch; 
found : Int(1992) 
required: Nothing 
     m(1992)("foo") += "bar"; println(m) 
     ^

giải pháp thứ hai của tôi là để viết một lớp nhà máy với một phương pháp, và theo cách đó tôi chỉ phải khai báo từng loại một lần. Nhưng sau đó mỗi khi tôi muốn có một mặc định bản đồ mới có giá trị, tôi phải cả hai nhanh chóng lớp nhà máy và sau đó gọi phương thức, mà vẫn có vẻ hơi dài dòng:

scala> class Factory[K] {          
| def create[V](defaultValue: => V) = new HashMap[K, V] { 
|  override def default(key: K) = {      
|  val result = defaultValue       
|  this(key) = result         
|  result            
|  }              
| }              
| }               
defined class Factory 

scala> val m = new Factory[Int].create(new Factory[String].create(Set[String]())) 
m: scala.collection.mutable.HashMap[Int,scala.collection.mutable.HashMap[String,scala.collection.mutable.Set[String]]] = Map() 

scala> m(1992)("foo") += "bar"; println(m) 
Map(1992 -> Map(foo -> Set(bar))) 

Tôi thực sự muốn có một cái gì đó đơn giản như thế này:

val m = defaultingMap[Int](defaultingMap[String](Set[String]())) 

Bất kỳ ai cũng có cách để làm điều đó?

Trả lời

5

Với Scala 2.8:

object DefaultingMap { 
    import collection.mutable 
    class defaultingMap[K] { 
    def apply[V](v: V): mutable.Map[K,V] = new mutable.HashMap[K,V] { 
     override def default(k: K): V = { 
     this(k) = v 
     v 
     } 
    } 
    } 
    object defaultingMap { 
    def apply[K] = new defaultingMap[K] 
    } 

    def main(args: Array[String]) { 
    val d4 = defaultingMap[Int](4) 
    assert(d4(3) == 4) 
    val m = defaultingMap[Int](defaultingMap[String](Set[String]())) 
    m(1992)("foo") += "bar" 
    println(m) 
    } 
} 

Bạn không thể cà ri thông số gõ vào Scala, do đó lừa với lớp để nắm bắt những loại quan trọng là cần thiết.

Nhân tiện: Tôi không nghĩ rằng API kết quả là rất rõ ràng. Tôi đặc biệt không thích truy cập bản đồ tác dụng phụ.

+0

Tôi thấy, lừa là sử dụng một lớp đối tượng đi kèm ở trên cùng của lớp nhà máy. Cảm ơn, rất tuyệt! Ngoài sự tò mò, bạn sẽ sử dụng API nào? Tôi đang cố gắng tránh sự tàn bạo getOrElseUpdate lồng nhau tôi đã cho thấy trong khối mã thứ hai ở trên. Tôi chắc chắn sẽ mở ra những cách khác để làm điều đó. – Steve

3

Hóa ra tôi cần phải mở rộng MapLike, hoặc khi tôi gọi bộ lọc, bản đồ, vv, bản đồ có giá trị mặc định của tôi sẽ được chuyển trở lại Bản đồ thông thường mà không có ngữ nghĩa mặc định. Dưới đây là một biến thể của giải pháp mkneissl rằng làm điều đúng đắn cho bộ lọc, bản đồ vv

import scala.collection.mutable.{MapLike,Map,HashMap} 

class DefaultingMap[K, V](defaultValue: => V) extends HashMap[K, V] 
with 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 
    } 
} 

object DefaultingMap { 
    def apply[K] = new Factory[K] 
    class Factory[K] { 
    def apply[V](defaultValue: => V) = new DefaultingMap[K, V](defaultValue) 
    } 
} 

Và đây có nghĩa là, trong hành động, làm đúng với bộ lọc:

scala> val m = DefaultingMap[String](0) 
m: DefaultingMap[String,Int] = Map() 

scala> for (s <- "the big black bug bit the big black bear".split(" ")) m(s) += 1 

scala> val m2 = m.filter{case (_, count) => count > 1} 
m2: DefaultingMap[String,Int] = Map((the,2), (big,2), (black,2)) 
Các vấn đề liên quan