创建函数的通用版本,该函数对列表中满足特定条件的元素进行分组

时间:2014-09-14 05:57:38

标签: scala list

我的功能是List Person case class Person(first:String,last:String,age:Int) @annotation.tailrec def groupPersons(persons: List[Person], results: List[List[Person]] = Nil): List[List[Person]] = persons match { case Nil => results.reverse case persons => groupPersons(persons.dropWhile(_.first == persons.head.first), persons.takeWhile(_.first == persons.head.first) :: results) } 的{​​{1}},如果它们的名字相同,并且它们在列表中彼此相邻。

dropWhile

我想创建此函数的通用版本,该函数也适用于其他类型,并接受传递给takeWhile def group(p: List[P], results: List[List[P]] = Nil, f: (P,P) => Boolean): List[List[P]] = { p match { case Nil => results.reverse case p => group(p.dropWhile(f), p.takeWhile(f) :: results, f) } } 的函数。

类似的东西:

{{1}}

无法使上述工作?任何想法如何解决这一问题?

2 个答案:

答案 0 :(得分:2)

你大部分都在那里。向group添加类型参数可以解决一个问题。您的另一个问题是dropWhiletakeWhile是谓词(P=> Boolean),但您想使用带有两个参数的f。我们可以通过调整f并部分应用来解决这个问题。最后,span的效率会比takeWhile / dropWhile

略高一些
  def group[P](p: List[P], 
               results: List[List[P]] = Nil, 
               f: (P,P) => Boolean): List[List[P]] = {
   p match {
     case Nil => results.reverse

     case p => {
       val g = f.curried(p.head)
       val (matching, rest) = p.span(g)
       group(rest, matching :: results, f)
     }
   }
} 

val xs = List(Person("John", "Doe", 42), Person("Jane", "Smith", 50), 
              Person("Jane", "Piper", 52), Person("Mary", "Morse", 52))

def matchFirstName(a:Person,b:Person) = a.first == b.first

group(xs, Nil, matchFirstName) 
//> res0: List[List[exprs.exprs.Person]] = List(List(Person(John,Doe,42)), List(
//| Person(Jane,Smith,50), Person(Jane,Piper,52)), List(Person(Mary,Morse,52)))

答案 1 :(得分:1)

我不确定这是否需要你,但还有更好的方法:

case class Person(first:String,last:String,age:Int)
val xs = List(Person("John", "Doe", 42), Person("Jane", "Smith", 50), Person("Jane", "Piper", 52), Person("Mary", "Morse", 52))

有一个函数groupBy

scala> xs.groupBy(_.first)
res0: Map[String,List[Person]] = 
  Map(Jane -> List(Person(Jane,Smith,50), Person(Jane,Piper,52)), 
      Mary -> List(Person(Mary,Morse,52)), 
      John -> List(Person(John,Doe,42)))

您可以转换为列表:xs.groupBy(_.first).toList,然后继续处理您的数据。

感谢@Paul,我没有注意到“彼此相邻”的要求。它在我看来就像一个简单的排序应用程序,即sortWith函数,它接受函数lt:(A, A) => Boolean,就像你的f参数一样。