2013-04-24 4 views
8

Ich habe eine java.lang.Iterable, die ihre Werte träge berechnet. Ich rufe von Scala aus darauf zu. Gibt es eine Kern-API-Möglichkeit, nur bestimmte Werte zurückzugeben? Zum Beispiel Bildgebung gab es eine Filtermethode, die auch alle Ergebnisse bereitgestellt lieferte bisher:So erhalten Sie bestimmte Artikel von einem Scala Iterable, Aufrechterhaltung der Faulheit

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

Ich denke, dies ist nicht ein sehr allgemeiner Fall ist, weil es vorher zurück Speicher von Elementen beinhaltet, und das auch sein mag, warum es isn‘ t in der Kern-API.

Ich weiß über List.distinct und Set s, aber ich möchte etwas, das seine Elemente nicht berechnen wird, bis gefragt.

+0

Ich denke, der einfachste Weg, dies zu tun wäre, einen Iterator mit einem veränderlichen 'Set' in seiner Schließung zurück, die verfolgt, was es gesehen wird, und dann durch' Filter' auf seine Eingabe angewendet wird, aktualisiert die gesehen setze und gib gegebenenfalls false zurück. –

Antwort

10

Sie die distinct Methode auf Stream verwenden nennen. Zum Beispiel, wenn Sie diese 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.") } 
    } 
} 

Sie schreiben:

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) 

Wir können sagen, dass alles, da der Strom unendlich ist in geeigneter Weise faul ist.

+0

D'oh, sollte Stream überprüft haben. Vielen Dank. Ich erinnere mich jetzt, dass Stream seine zuvor zurückgegebenen Elemente speichert. Ich dachte, das wäre ursprünglich ein Implementierungsdetail, aber ich nehme an, dass es auch Teil seiner Semantik ist. –

+0

hast du schon mal versucht, Scalas eigene distinct zu verwenden? x.toList.distinct –

6

UPDATE Fragen sorgfältig zu lesen ist gut. Keine Faulheit in dieser Lösung. Es tut uns leid.

toSet wird genau das tun, was Sie wollen:

  1. Shop iteriert Elemente in einer Sammlung (nicht das, was Sie wollen, aber erforderlich)
  2. Tropfen/Duplikate ersetzen

Beispiel

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

Wenn Sie Lust haben, können Sie das zurück konvertieren zu einem iterable:

it.toSet.toIterable 

Oder pimp die Iterable:

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

Und dann kann

it.unique 
+0

Keine Sorge, du bist einfach keine faule Person;) –

+0

@DanGravell Ja ... Ich glaube nicht, dass du es schaffen wirst, ein "UniqueIterable" mit wirklich hässlichem internem Zustand zu schreiben. – gzm0

+0

Das löst mein Problem. – David

-1

Dies sollte die Arbeit machen (aber ich hasse):

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

Warum der Downvote? – gzm0

1

oben auf meinem Kommentar Aufweiten, aber ich kann es jetzt nicht testen:

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

Sie auf die Idee kommen, mindestens. Sie würden dies dann auf den Iterator anwenden, den Sie von Ihrem iterablen Element erhalten, und nicht das unnötige Speicherverhalten von Stream erhalten.

1

Hier ist der Code, der die .disctinct Methode zu Iterator hinzufügt.

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 
     } 
    } 
} 

Seien Sie sich bewusst, dass, wie es in der Regel mit Iteratoren so die ursprüngliche Iterator nach dem Aufruf .distinct auf sie nicht gültig ist.

+0

Das ist falsch! Betrachte: 'Iterator (1, 1)'. Nachdem 'next' zum ersten Mal aufgerufen wurde, ist 'hasNext' immer noch wahr, aber der Aufruf' next' wird tatsächlich geworfen. – gzm0

+0

Ich habe genau das getan und alles ist in Ordnung, wie es sein sollte. http://pastebin.com/NPAjq2aS –

+0

Nun ja ... weil Sie als nächstes auf dem ursprünglichen Iterator aufrufen, nicht die eine durch den Aufruf von "distinct" erstellt. – gzm0

Verwandte Themen