将列表填入列表列表

时间:2013-06-01 07:44:52

标签: list scala immutability

假设我有一个元素列表myl和一个函数f。我想将myl列入列表清单,其中每个新的“子列表”包含myl的连续范围,f的值不变。

例如,如果myl = List( (1,2), (3,2), (4,1), (6,2) )def f(x: (Int, Int)) = x._2。然后结果应为List( List((1,2), (3,2)), List((4, 1)), List((6,2)) )

是否有一种优雅的方式来编写这样一个没有任何var s的函数?

4 个答案:

答案 0 :(得分:4)

def groupBy[A](as: List[A])(p: (A, A) => Boolean): List[List[A]] =
  as.foldRight(List.empty[List[A]]) {
    case (x, (ys @ y :: _) :: zs) if p(x, y) => (x :: ys) :: zs
    case (x, ys) => List(x) :: ys
  }

scala> groupBy(myl)(_._2 == _._2)
res0: List[List[(Int, Int)]] = List(List((1,2), (3,2)), List((4,1)), List((6,2)))

编辑:我还使用span编写了此版本:

def groupBy[A](as: List[A])(p: (A, A) => Boolean): List[List[A]] =
  as match {
    case x :: xs =>
      val (ys, zs) = xs.span(p(x, _))
      (x :: ys) :: groupBy(zs)(p)
    case _ => Nil
  }

这与ghik的解决方案基本类似,但使用模式匹配而不是isEmptyhead

(名称groupBy的解释:Haskell库中有一个相同名称的函数,它具有完全相同的行为。)

答案 1 :(得分:2)

更一般的解决方案:

def partitionBy[A](list: List[A])(groupingFunc: A => Any): List[List[A]] =
  if (list.nonEmpty) {
    val firstGroupingValue = groupingFunc(list.head)
    val (group, rest) = list.span(groupingFunc(_) == firstGroupingValue)
    group :: partitionBy(rest)(groupingFunc)
  } else Nil

使用示例:

scala> partitionBy(List((1,2),(3,2),(5,2),(1,1),(2,1),(3,2),(5,2)))(_._2)
res14: List[List[(Int, Int)]] = List(List((1,2), (3,2), (5,2)), List((1,1), (2,1)), List((3,2), (5,2)))

答案 2 :(得分:1)

您可以尝试foldRight

val result2 = l.foldRight(List[List[(Int, Int)]]()) {
  (x, acc) =>
    if (acc.isEmpty) {
      List(x) :: Nil
    } else if (acc.head.head._2 == x._2) {
      (x :: acc.head) :: acc.tail
    } else {
      List(x) :: acc
    }
}

答案 3 :(得分:1)

你的问题太具体了,不存在解决它的一般函数,所以我们必须写一个自己的函数。

实现功能算法的标准策略是Divide and conquer,这基本上意味着提取问题的最小部分,然后逐步建立算法。

实施

好的,我们需要的最小的东西是测试两个连续项:

def testContiguity( a : (Int, Int), b : (Int, Int) ) : Boolean 
  = a._2 == b._2

然后我们需要一些函数使用两项比较函数来安排列表。如果标准库拥有它会很好,但它没有,所以我们定义自己的:

def arrange
  [ A ]
  ( list : List[ A ] )
  ( f : (A, A) => Boolean ) 
  : List[ List[ A ] ]
  = list match {
      case a :: b :: tail => 
        if( f(a, b) ) putInFirstGroup( a, arrange( b :: tail )( f ) )
        else putInNewGroup( a, arrange( b :: tail )( f ) )
      case a :: Nil =>
        putInNewGroup( a, Nil )
      case Nil =>
        Nil
    }

好的,你可以看到上面的实现还依赖于另外两个函数,让我们定义它们:

def putInFirstGroup
  [ A ]
  ( item : A, groups : List[ List[ A ] ] ) 
  : List[ List[ A ] ]
  = groups match {
      case group :: tail =>
        (item :: group) :: tail
      case Nil => 
        (item :: Nil) :: Nil
    }

def putInNewGroup
  [ A ]
  ( item : A, groups : List[ List[ A ] ] ) 
  : List[ List[ A ] ]
  = (item :: Nil) :: groups

就是这样!

用法

scala> arrange( List( (1,2), (3,2), (4, 1), (6,2) ) )( testContiguity )
res2: List[List[(Int, Int)]] = List(List((1,2), (3,2)), List((4,1)), List((6,2)))

您现在可以看到我们已经创建了一个非常灵活且通用的解决方案,处理任何类型的项目列表,并允许您使用任何其他测试功能来安排它们。此外,我们大量使用复杂算法的划分来解决这个问题。