2011-01-21 2 views
15

Учитывая, например:функция Scala Список для группировки последовательных одинаковых элементов

List(5, 2, 3, 3, 3, 5, 5, 3, 3, 2, 2, 2) 

Я хотел бы, чтобы добраться до:

List(List(5), List(2), List(3, 3, 3), List(5, 5), List(3, 3), List(2, 2, 2)) 

Я бы предположить, что существует простая функция списка, который делает это, но я не могу его найти.

+2

99 scala problems? –

ответ

15

Это трюк, который я обычно использую:

def split[T](list: List[T]) : List[List[T]] = list match { 
    case Nil => Nil 
    case h::t => val segment = list takeWhile {h ==} 
    segment :: split(list drop segment.length) 
} 

На самом деле ... Это не так, я обычно абстрактные над типом сбора и оптимизации с хвостовой рекурсией, а также, но хотел держать ответ простой.

+2

Ницца. Но разве это не было бы достаточно распространено, чтобы гарантировать собственную библиотечную функцию? –

+0

Никаких аргументов нет, это наверняка возникает в вопросах более одного раза ... –

+0

@KevinWright «оптимизируется с хвостовой рекурсией» -> Как бы вы это сделали? –

8
val xs = List(5, 2, 3, 3, 3, 5, 5, 3, 3, 2, 2, 2) 

Вот еще один способ.

(List(xs.take(1)) /: xs.tail)((l,r) => 
    if (l.head.head==r) (r :: l.head) :: l.tail else List(r) :: l 
).reverseMap(_.reverse) 
6
list.foldRight(List[List[Int]]()){ 
    (e, l) => l match { 
    case (`e` :: xs) :: fs => (e :: e :: xs) :: fs 
    case _ => List(e) :: l 
    } 
} 

Или

list.zip(false :: list.sliding(2).collect{case List(a,b) => a == b}.toList) 
.foldLeft(List[List[Int]]())((l,e) => if(e._2) (e._1 :: l.head) :: l.tail 
             else List(e._1) :: l).reverse 

[Редактировать]

//find the hidden way 
//the beauty must be somewhere 
//when we talk scala 

def split(l: List[Int]): List[List[Int]] = 
    l.headOption.map{x => val (h,t)=l.span{x==}; h::split(t)}.getOrElse(Nil) 
+0

Будет ли какой-либо из подходов работать со значениями/NaN? Я пытаюсь выяснить, вижу ли я n последовательных значений Double.NaN в векторе.Глядя на решения, размещенные здесь, кажется, я ничего не придумаю. – TomTom101

+0

Значения NaN потребуют особого обращения. Возможно, вы могли бы обернуть 'Int' в 'Option [Int]' и преобразовать 'NaN' в 'None'. Тогда будут работать обычные решения на основе равенства. Или вы можете написать свою собственную функцию равенства. – Landei

8

Черт Рекс Керр, для написания ответа я идти. Поскольку существуют незначительные стилистические различия, вот мое взятие:

list.tail.foldLeft(List(list take 1)) { 
    case (acc @ (lst @ hd :: _) :: tl, el) => 
     if (el == hd) (el :: lst) :: tl 
     else (el :: Nil) :: acc 
} 

Поскольку элементы идентичны, я не стал реверсирования подсписки.

+0

Не могли бы вы объяснить (или указать мне какую-нибудь ссылку) case (acc @ (lst @ hd :: _). Я никогда не видел такой синтаксис раньше. Спасибо заранее. – gashu

+1

@gashu В соответствии с шаблоном 'x @ y' означает, что 'x' будет присвоено значение _matched_ by' y'. Так, например, 'x @ _ :: _' присваивает xa непустой список (то есть список с головой и хвостом , в соответствии с '_ :: _'). Таким образом,' acc', выше, является полным списком, а 'lst' является главой списка. –

+0

@ daniel-c-sobral, поэтому такие выражения используются только в шаблоне сопоставление или в другом контексте, они означают что-то другое? – gashu

5

У меня есть эти реализации, лежащие вокруг от работы над методами коллекций. В конце я проверил в более простых реализациях inits и хвостов и оставил кластер. Каждый новый метод, независимо от того, насколько просто заканчивается сбор большого налога, который трудно увидеть извне. Но вот реализация, которую я не использовал.

import generic._ 
import scala.reflect.ClassManifest 
import mutable.ListBuffer 
import annotation.tailrec 
import annotation.unchecked.{ uncheckedVariance => uV } 

def inits: List[Repr] = repSequence(x => (x, x.init), Nil) 
def tails: List[Repr] = repSequence(x => (x, x.tail), Nil) 
def cluster[A1 >: A : Equiv]: List[Repr] = 
    repSequence(x => x.span(y => implicitly[Equiv[A1]].equiv(y, x.head))) 

private def repSequence(
    f: Traversable[A @uV] => (Traversable[A @uV], Traversable[A @uV]), 
    extras: Traversable[A @uV]*): List[Repr] = { 

    def mkRepr(xs: Traversable[A @uV]): Repr = newBuilder ++= xs result 
    val bb = new ListBuffer[Repr] 

    @tailrec def loop(xs: Repr): List[Repr] = { 
    val seq = toCollection(xs) 
    if (seq.isEmpty) 
     return (bb ++= (extras map mkRepr)).result 

    val (hd, tl) = f(seq) 
    bb += mkRepr(hd) 
    loop(mkRepr(tl)) 
    } 

    loop(self.repr) 
} 

[Изменить: Я забыл, что другие люди не будут знать внутренности. Этот код написан изнутри TraversableLike, так что это не будет работать из коробки]

0

это может быть проще:.

val input = List(5, 2, 3, 3, 3, 5, 5, 3, 3, 2, 2, 2) 
input groupBy identity values 
+9

Это будет группировать все одинаковые элементы, а не только последовательные. –

2

Вот хвост рекурсивной решение вдохновлен @Kevin Райт и @ Landei:

@tailrec 
def sliceEqual[A](s: Seq[A], acc: Seq[Seq[A]] = Seq()): Seq[Seq[A]] = { 
    s match { 
    case fst :: rest => 
     val (l, r) = s.span(fst==) 
     sliceEqual(r, acc :+ l) 
    case Nil => acc 
    } 
} 
Смежные вопросы