如何在Scala中编写这个递归的groupBy函数

时间:2015-04-28 13:05:03

标签: scala recursion types

最近我遇到了一个非常有用的 groupBy 函数,Groovy在Iterable上提供了这个函数:

public static Map groupBy(Iterable self, List<Closure> closures)

您可以使用它在列表甚至地图see example by mrhaki here上执行递归 groupBy

我想编写一个在Scala中执行相同操作的函数。但刚刚开始我的Scala之旅,我对于如何定义和实现此方法感到很遗憾。特别是函数的泛型方面以及此方法签名的返回类型超出了我的水平。

我需要更多有经验的Scala开发人员来帮助我。

以下签名是完全错误还是我在球场?

def groupBy[A, K[_]](src: List[A], fs: Seq[(A) ⇒ K[_]]): Map[K[_], List[A]]

另外,如何使用正确的类型实现递归?

2 个答案:

答案 0 :(得分:5)

这是一个简单的多组实现:

uistack

如果你真的需要一些递归类型,你需要一个包装器:

implicit class GroupOps[A](coll: Seq[A]) {
  def groupByKeys[B](fs: (A => B)*): Map[Seq[B], Seq[A]] =
    coll.groupBy(elem => fs map (_(elem)))
}

val a = 1 to 20

a.groupByKeys(_ % 3, _ % 2) foreach println

将定义更改为:

sealed trait RecMap[K, V]

case class MapUnit[K, V](elem: V) extends RecMap[K, V] {
  override def toString = elem.toString()
}
case class MapLayer[K, V](map: Map[K, RecMap[K, V]]) extends RecMap[K, V] {
  override def toString = map.toString()
}

implicit class GroupOps[A](coll: Seq[A]) { def groupByKeys[B](fs: (A => B)*): Map[Seq[B], Seq[A]] = coll.groupBy(elem => fs map (_(elem))) def groupRecursive[B](fs: (A => B)*): RecMap[B, Seq[A]] = fs match { case Seq() => MapUnit(coll) case f +: fs => MapLayer(coll groupBy f mapValues {_.groupRecursive(fs: _*)}) } } 会产生与问题更相关的内容

最后我从引用文章重建域名定义:

a.groupRecursive(_ % 3, _ % 2)

现在我们可以写

case class User(name: String, city: String, birthDate: Date) {
  override def toString = name
}

implicit val date = new SimpleDateFormat("yyyy-MM-dd").parse(_: String)
val month = new SimpleDateFormat("MMM").format (_:Date)

val users = List(
  User(name = "mrhaki", city = "Tilburg"  , birthDate = "1973-9-7"),
  User(name = "bob"   , city = "New York" , birthDate = "1963-3-30"),
  User(name = "britt" , city = "Amsterdam", birthDate = "1980-5-12"),
  User(name = "kim"   , city = "Amsterdam", birthDate = "1983-3-30"),
  User(name = "liam"  , city = "Tilburg"  , birthDate = "2009-3-6")
)

并获取

  

地图(蒂尔堡 - &gt;地图(Mar - &gt; List(liam),Sep - &gt; List(mrhaki)),纽约    - &GT;地图(Mar - &gt; List(bob)),阿姆斯特丹 - &gt;地图(Mar - &gt; List(kim),May - &gt; List(britt)))

答案 1 :(得分:1)

由于方法完全不同,我决定添加另一个答案。

你可以,实际上得到非包装正确类型的地图与巨大的变通方法。我不是很擅长这个,所以有机会可以简化。

技巧 - 创建类型化函数序列,最近使用type classestype path方法生成多级地图。

所以这是解决方案

sealed trait KeySeq[-V] {
  type values
}

case class KeyNil[V]() extends KeySeq[V] {
  type values = Seq[V]
}

case class KeyCons[K, V, Next <: KeySeq[V]](f: V => K, next: Next)
                                           (implicit ev: RecGroup[V, Next]) extends KeySeq[V] {
  type values = Map[K, Next#values]

  def #:[K1](f: V => K1) = new KeyCons[K1, V, KeyCons[K, V, Next]](f, this)
}

trait RecGroup[V, KS <: KeySeq[V]] {
  def group(seq: Seq[V], ks: KS): KS#values
}

implicit def groupNil[V]: RecGroup[V, KeyNil[V]] = new RecGroup[V, KeyNil[V]] {
  def group(seq: Seq[V], ks: KeyNil[V]) = seq
}

implicit def groupCons[K, V, Next <: KeySeq[V]](implicit ev: RecGroup[V, Next]): RecGroup[V, KeyCons[K, V, Next]] =
  new RecGroup[V, KeyCons[K, V, Next]] {
    def group(seq: Seq[V], ks: KeyCons[K, V, Next]) = seq.groupBy(ks.f) mapValues (_ groupRecursive ks.next)
  }



implicit def funcAsKey[K, V](f: V => K): KeyCons[K, V, KeyNil[V]] =
  new KeyCons[K, V, KeyNil[V]](f, KeyNil[V]())

implicit class GroupOps[V](coll: Seq[V]) {
  def groupRecursive[KS <: KeySeq[V]](ks: KS)(implicit g: RecGroup[V, KS]) =
    g.group(coll, ks)
}

关键函数由#:右关联运算符

组成

所以如果我们定义

def mod(m:Int) = (x:Int) => x % m
def even(x:Int) = x % 2 == 0

然后

1 to 30 groupRecursive (even _ #: mod(3)   #: mod(5) )

会产生正确的Map[Boolean,Map[Int,Map[Int,Int]]] !!!

如果我们想从之前的问题

users.groupRecursive(((u:User)=> u.city(0)) #: ((u:User) => month(u.birthDate)))

我们正在构建Map[Char,Map[String,User]]