2012-03-15 23 views
5

我想編寫一個方法mergeKeys,該方法通過密鑰對Iterable[(K, V)]中的值進行分組。例如,我可以寫:通過任意Monoid的密鑰對組值進行編碼

def mergeKeysList[K, V](iter: Iterable[(K, V)]) = { 
    iter.foldLeft(Map[K, List[V]]().withDefaultValue(List.empty[V])) { 
     case (map, (k, v)) => 
      map + (k -> (v :: map(k))) 
    } 
    } 

不過,我想能夠使用任何Monoid而不是寫一個方法List的。例如,這些值可能是整數,我想總結它們而不是將它們附加到列表中。或者他們可能是元組(String, Int),我想在一個集合中累積字符串,但添加整數。我怎樣才能寫出這樣的方法?還是有什麼我可以用scalaz來完成這件事?

更新:我沒有我想象的那麼遙遠。我有點接近,但如果值是元組,我仍然不知道如何使它工作。我是否需要編寫另一個隱式轉換?也就是說,每種類型參數都有一個隱式轉換?

sealed trait SuperTraversable[T, U, F[_]] 
extends scalaz.PimpedType[TraversableOnce[(T, F[U])]] { 
    def mergeKeys(implicit mon: Monoid[F[U]]): Map[T, F[U]] = { 
    value.foldLeft(Map[T, F[U]]().withDefaultValue(mon.zero)) { 
     case (map, (k, v)) => 
     map + (k -> (map(k) |+| v)) 
    } 
    } 
} 

implicit def superTraversable[T, U, F[_]](
    as: TraversableOnce[(T, F[U])] 
): SuperTraversable[T, U, F] = 
    new SuperTraversable[T, U, F] { 
     val value = as 
    } 

回答

6

首先,雖然這是不相關的問題,你被明確提及的類型構造F[_]限制你的代碼的 普遍性。它工作正常,無 這樣做:

sealed trait SuperTraversable[K, V] 
extends scalaz.PimpedType[TraversableOnce[(K, V)]] { 
    def mergeKeys(implicit mon: Monoid[V]): Map[K, V] = { 
     value.foldLeft(Map[K, V]().withDefaultValue(mon.zero)) { 
      case (map, (k, v)) => 
       map + (k -> (map(k) |+| v)) 
     } 
    } 
} 

[...] 

現在,您的實際問題,就沒有必要改變mergeKeys處理 搞笑種組合;只需寫一個Monoid來處理任何類型的 結合你想要做的。假設你想要做你的字符串+ INTS例如:

implicit def monoidStringInt = new Monoid[(String, Int)] { 
    val zero = ("", 0) 
    def append(a: (String, Int), b: => (String, Int)) = (a, b) match { 
     case ((a1, a2), (b1, b2)) => (a1 + b1, a2 + b2) 
    } 
} 

println { 
    List(
     "a" -> ("Hello, ", 20), 
     "b" -> ("Goodbye, ", 30), 
     "a" -> ("World", 12) 
    ).mergeKeys 
} 

Map(a -> (Hello, World,32), b -> (Goodbye, ,30)) 
+0

完美,謝謝! – schmmd 2012-03-15 05:05:40

相關問題