假设我有一个元素列表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的函数?
答案 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的解决方案基本类似,但使用模式匹配而不是isEmpty
和head
。
(名称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)))
您现在可以看到我们已经创建了一个非常灵活且通用的解决方案,处理任何类型的项目列表,并允许您使用任何其他测试功能来安排它们。此外,我们大量使用复杂算法的划分来解决这个问题。