当元素以Scala惯用方式重复时,如何按元素分组将列表转换为列表列表

时间:2018-10-26 21:13:17

标签: scala list functional-programming

我想通过每当元素重复如下所示时就中断,将元素列表转换为列表列表

输入:

List(1, 2, 3, 1, 2, 1, 3, 1, 2, 3)

输出:

List[List[Integer]] = List(List(1, 2, 3), List(1, 2), List(1, 3), List(1, 2, 3))

这是我尝试过的:

val tokens = List(1,2,3,1,2,1,3,1,2,3)

val group = new ListBuffer[List[Integer]]()
val nextGroup = new ListBuffer[Integer]()
val nextTokens = new ListBuffer[Integer]()

for (t <- tokens) {
  if (nextTokens.contains(t)) {
    group += nextGroup.toList
    nextGroup.clear()
    nextTokens.clear()
  }
  nextGroup += t
  nextTokens += t
}

group += nextGroup.toList

group.toList

我正在寻找一种更好的方法来使用map / foldLeft ...函数而不使用ListBuffer。

谢谢。

3 个答案:

答案 0 :(得分:0)

这是使用foldLeft

的版本
tokens
  .drop(1)
  .foldLeft(List(tokens.take(1))) { case (res, token) =>
    if (res.head.contains(token)) {
      List(token) +: res
    } else {
      (res.head :+ token) +: res.tail
    }
  }
  .reverse

使用droptake可以确保它在空白列表上有效。反向生成结果意味着最新的List总是首当其冲,并且对于长列表来说也是有效的。

为了完整起见,这是一个执行相同功能的递归函数:

def groupDistinct[T](tokens: List[T]): List[List[T]] = {
  @tailrec
  def loop(token: List[T], cur: List[T], res: List[List[T]]): List[List[T]] =
    token match {
      case Nil =>
        res :+ cur
      case head :: tail =>
        if (cur.contains(head)) {
          loop(tail, List(head), res :+ cur)
        } else {
          loop(tail, cur :+ head, res)
        }
    }

  loop(tokens, Nil, Nil)
}

答案 1 :(得分:0)

使用下面提供的解决方案,我提出了以下解决方案

https://stackoverflow.com/a/52976957/1696418

mySeq.foldLeft(List.empty[List[Int]]) {
  case (acc, i) if acc.isEmpty => List(List(i))
  case (acc, i) if acc.last.contains(i) => acc :+ List(i)
  case (acc, i) => acc.init :+ (acc.last :+ i)
}

答案 2 :(得分:0)

这是一个非常直接的解决方案,它在跟踪两个累加器数组的同时使用q:一个用于最终结果,一个用于当前考虑的令牌子列表。最后,我们将结果数组与最后一个子列表结合起来。

int main(void) {
   int m=44;
   int *p = &m;    
   int* q = p-1;
   printf("%p\n",(void*)q); /* this is valid */
   //printf("%d \n",*q); /* this cause seg fault */
   return 0;
}

请注意,这并不是计算效率最高的解决方案,因为对于每次迭代,我们正在使用foldLeft来检查正在考虑的整个子列表。如果您希望提高效率,则可以(取决于您的数据)考虑一种使用哈希映射或类似数据结构的方法来代替快速重复检查。