2012-03-15 38 views
5

Tôi muốn viết phương thức mergeKeys nhóm các giá trị trong một số Iterable[(K, V)] bằng các phím. Ví dụ, tôi có thể viết:Giá trị nhóm bằng một khóa với bất kỳ 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))) 
    } 
    } 

Tuy nhiên, tôi muốn để có thể sử dụng bất kỳ Monoid thay vì viết một phương pháp để List. Ví dụ, các giá trị có thể là số nguyên và tôi muốn tổng hợp chúng thay vì gắn chúng vào một danh sách. Hoặc họ có thể là tuples (String, Int) nơi tôi muốn tích lũy các chuỗi trong một tập hợp nhưng thêm các số nguyên. Làm thế nào tôi có thể viết một phương pháp như vậy? Hoặc là có cái gì khác tôi có thể sử dụng trong scalaz để có được điều này được thực hiện?

Cập nhật: Tôi không ở xa như tôi nghĩ. Tôi đã gần hơn một chút, nhưng tôi vẫn không biết làm thế nào để làm cho nó hoạt động nếu các giá trị là tuple. Tôi có cần phải viết một chuyển đổi tiềm ẩn khác không? Nghĩa là, một chuyển đổi tiềm ẩn cho từng số tham số kiểu?

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 
    } 

Trả lời

6

Thứ nhất, trong khi nó không liên quan đến câu hỏi của bạn, bạn đang hạn chế tính tổng quát của mã của bạn bằng cách đề cập rõ kiểu dữ liệu constructor F[_]. Nó hoạt động tốt mà không làm vậy:

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)) 
     } 
    } 
} 

[...] 

Bây giờ, đối với câu hỏi thực tế của bạn, không có cần phải thay đổi mergeKeys để xử lý loại hài hước kết hợp; chỉ cần viết Monoid để xử lý bất kỳ loại nào của kết hợp bạn muốn làm. Giả sử bạn muốn làm Strings Ints + của bạn ví dụ:

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 
} 

cho

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

Perfect, cảm ơn! – schmmd

Các vấn đề liên quan