2012-03-15 24 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

相关问题