2013-04-24 11 views
8

Mam java.lang.Iterable, który leniwie wylicza swoje wartości. Mam do niego dostęp od Scala. Czy istnieje kluczowa metoda API zwracania tylko różnych wartości? Na przykład, obrazowanie istniała metoda filtr, który również wszystkie wyniki zwracane do tej pory:Jak uzyskać różne rzeczy od Scala Iterable, zachowując lenistwo

val myLazyDistinctIterable = iterable.filter((previousReturnedItems, newItem) => previousReturnedItems.contains(newItem)) 

Chyba nie jest to bardzo ogólny przypadek, ponieważ wiąże się przechowywanie wcześniej wracać rzeczy, i że może być dlaczego nieprawdaż” t w rdzeniu API.

Wiem o List.distinct i Set s, ale chcę czegoś, co nie będzie obliczać jego elementów, dopóki nie zostanie zapytany.

+0

Myślę, że najprościej to zrobić, to zwrócić iterator z możliwym do zmiany ustawieniem 'Set' w jego zamknięciu, który śledzi to, co widzi, a następnie jest generowany przez' filter' zastosowany do jego wejścia, aktualizując widziane ustawia i zwraca wartość false, jeśli jest to właściwe. –

Odpowiedz

10

Można użyć metody distinct na Stream. Na przykład, jeśli masz ten Iterable:

val it = new java.lang.Iterable[Int] { 
    def iterator = new java.util.Iterator[Int] { 
    var i = 0 
    var first = true 

    def hasNext = true 
    def next = 
     if (first) { first = false; i } else { first = true; i += 1; i - 1 } 
    def remove() { throw new UnsupportedOperationException("Can't remove.") } 
    } 
} 

Można napisać:

scala> import scala.collection.JavaConverters._ 
import scala.collection.JavaConverters._ 

scala> val s = it.asScala.toStream 
s: scala.collection.immutable.Stream[Int] = Stream(0, ?) 

scala> s.take(10).toList 
res0: List[Int] = List(0, 0, 1, 1, 2, 2, 3, 3, 4, 4) 

scala> val s = it.asScala.toStream.distinct 
s: scala.collection.immutable.Stream[Int] = Stream(0, ?) 

scala> s.take(10).toList 
res1: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 

Możemy powiedzieć, że wszystko jest odpowiednio leniwy, ponieważ strumień jest nieskończona.

+0

D'oh, powinieneś sprawdzić Stream. Dzięki. Pamiętam, że Stream przechowuje swoje wcześniej zwrócone przedmioty. Myślałem, że to był początkowy szczegół implementacji, ale myślę, że jest to część jego semantyki. –

+0

Czy kiedykolwiek próbowałeś używać własnych Scalas? x.toList.distinct –

6

UPDATE Czytanie pytań uważnie jest dobre. Brak lenistwa w tym rozwiązaniu. Przepraszam.

toSet zrobi dokładnie to, co chcesz:

  1. Store powtórzyć elementy w kolekcji (nie to, co chcesz, ale wymagane)
  2. Spadek/Wymień duplikatów

Przykład

val it = Seq(1,2,3,4,2,4): Iterable[Int] 
it.toSet 
// Set(1,2,3,4) 

Jeśli masz ochotę, możesz to zmienić do iterable:

it.toSet.toIterable 

Lub, pimp się Iterable:

implicit class UniquableIterable[T](t: Iterable[T]) { 
    def unique = t.toSet.toIterable 
} 

a następnie zadzwonić

it.unique 
+0

Nie martw się, zgadnij, że po prostu nie jesteś leniwym człowiekiem;) –

+0

@DanGravell Yep ... Nie sądzę, że obejdziesz pisanie 'UniqueIterable' z naprawdę brzydkim stanem wewnętrznym. – gzm0

+0

To rozwiązuje mój problem. – David

-1

To powinno załatwić sprawę (ale nienawidzę):

class UniqueIterable[T](i: Iterable[T]) extends Iterable[T] { 
    import scala.collection.mutable.Set 
    def iterator = new Iterator[T] { 
    val it = i.iterator 
    var nextE: Option[T] = None 
    val seen: Set[T] = Set.empty 
    def hasNext = { 
     popNext() 
     nextE.isDefined 
    } 
    def next = { 
     popNext() 
     val res = nextE.get 
     nextE = None 
     res 
    } 

    @tailrec 
    private def popNext() { 
     if (nextE.isEmpty && it.hasNext) { 
     val n = it.next 
     if (seen contains n) popNext() 
     else { 
      seen += n 
      nextE = Some(n) 
     } 
     } 
    } 
    } 
} 
+0

Dlaczego upadek? – gzm0

1

Rozszerzenie na mój komentarz powyżej, ale nie mogę przetestować go teraz:

def unique[A](it: Iterator[A]): Iterator[A] = { 
    val seen = mutable.Set[A]() 
    it.filter { a => 
    if (seen(a)) 
     false 
    else { 
     seen += a 
     true 
    } 
    } 
} 

Masz pomysł, przynajmniej. Następnie zastosowałbyś to do iteratora, który uzyskasz ze swojej iteracji, i nie uzyskaj niepotrzebnego zachowania przechowywania Stream.

1

Oto kod, który dodaje metodę .disctinct do Iterator.

implicit class IteratorWrapper[T](it: Iterator[T]) { 
    def distinct = new Iterator[T] { 
     var seen = Set.empty[T] 
     var ahead = Option.empty[T] 

     def searchAhead { 
      while (ahead.isEmpty && it.hasNext) { 
       val v = it.next 
       if (!seen(v)) { 
        seen += v 
        ahead = Some(v) 
       } 
      } 
     } 

     def hasNext = { 
      searchAhead 
      ahead.nonEmpty 
     } 

     def next = { 
      searchAhead 
      val result = ahead.get 
      ahead = None 
      result 
     } 
    } 
} 

Należy pamiętać, że, jak to zwykle tak z iteratorów, oryginalny iterator nie jest ważna po wywołaniu .distinct na nim.

+0

To jest złe! Rozważ: 'Iterator (1, 1)'. Po pierwszym wywołaniu 'next',' hasNext' nadal będzie prawdziwe, ale wywołanie 'next' spowoduje rzucenie. – gzm0

+0

Zrobiłem dokładnie to i wszystko jest w porządku, tak jak powinno być. http://pastebin.com/NPAjq2aS –

+0

Cóż, tak ... ponieważ wywołujesz następny na oryginalnym iteratorze, a nie ten stworzony przez wywołanie "odrębny". – gzm0

Powiązane problemy