2012-02-17 11 views
10

Sto cercando di estendere l'iteratore per creare un nuovo metodo takeWhileInclusive, che funzionerà come takeWhile ma includerà l'ultimo elemento.Scala, estensione dell'iteratore

Il mio problema è la procedura migliore per estendere l'iteratore per restituire un nuovo iteratore che vorrei essere pigro valutato. Provenendo da uno sfondo C# io uso normalmente IEnumerable e uso la parola chiave yield, ma tale opzione non sembra esistere in Scala.

per esempio ho potuto

List(0,1,2,3,4,5,6,7).iterator.map(complex time consuming algorithm).takeWhileInclusive(_ < 6) 

quindi in questo caso l'takeWhileInclusive avrebbe solo risolvere il predicato sui valori fino a ottenere il risultato maggiore di 6, e comprenderà questo primo risultato

finora ho:

object ImplicitIterator { 
    implicit def extendIterator(i : Iterator[Any]) = new IteratorExtension(i) 
} 

class IteratorExtension[T <: Any](i : Iterator[T]) { 
    def takeWhileInclusive(predicate:(T) => Boolean) = ? 
} 
+0

Avevate avuto uno sguardo al flusso? –

+0

Un flusso potrebbe essere decisamente più appropriato qui nel caso di esempio, tuttavia ho ancora lo stesso problema sul modo migliore per creare il metodo di estensione –

+2

Oh, 'takeWhileInclusive'. Il mio vecchio ['takeTo'] (https://issues.scala-lang.org/browse/SI-2963) .... –

risposta

7

Questo è un caso in cui trovo la soluzione mutabile superiore:

class InclusiveIterator[A](ia: Iterator[A]) { 
    def takeWhileInclusive(p: A => Boolean) = { 
    var done = false 
    val p2 = (a: A) => !done && { if (!p(a)) done=true; true } 
    ia.takeWhile(p2) 
    } 
} 
implicit def iterator_can_include[A](ia: Iterator[A]) = new InclusiveIterator(ia) 
+0

Questa è sicuramente una soluzione elegante al mio problema, evviva! –

+0

Prenderò la versione funzionale senza né 'var's né' val's, grazie! –

+0

@oxbow_lakes - Se non ti dispiace il sovraccarico, è un'ottima alternativa. (Normalmente non userei un val per la funzione, stavo solo cercando di separare le cose per chiarezza qui.) –

0
scala> List(0,1,2,3,4,5,6,7).toStream.filter (_ < 6).take(2) 
res8: scala.collection.immutable.Stream[Int] = Stream(0, ?) 

scala> res8.toList 
res9: List[Int] = List(0, 1) 

Dopo l'aggiornamento:

scala> def timeConsumeDummy (n: Int): Int = { 
    | println ("Time flies like an arrow ...") 
    | n } 
timeConsumeDummy: (n: Int)Int 

scala> List(0,1,2,3,4,5,6,7).toStream.filter (x => timeConsumeDummy (x) < 6) 
Time flies like an arrow ... 
res14: scala.collection.immutable.Stream[Int] = Stream(0, ?) 

scala> res14.take (4).toList 
Time flies like an arrow ... 
Time flies like an arrow ... 
Time flies like an arrow ... 
res15: List[Int] = List(0, 1, 2, 3) 

timeConsumeDummy viene chiamato 4 volte. Mi sto perdendo qualcosa?

+0

Spiacente, l'esempio non è il caso specifico che sto cercando di risolvere, includerò un esempio più approfondito per illustrare cosa sono dopo –

+0

@JPullar: Il tuo take (2) è scomparso e cambiato posto con (_ <6), mentre il metodo timeConsumingMethod è a sinistra di (_ <6) ora. Così fa (timeConsumingMethod) produce un risultato Int come risultato, che viene paragonato a (_ <6) ora, o è l'elemento List iniziale, che deve essere inferiore a 6? –

+0

Ciò che la tua presentazione è corretta e ciò che sono dopo nella valutazione pigra.Il mio problema, tuttavia, è quello di emulare il modo in cui la funzione filtro sta valutando pigramente in un metodo di estensione personalizzata –

10

È possibile utilizzare il metodo di Iteratorspan per fare questo abbastanza pulito:

class IteratorExtension[A](i : Iterator[A]) { 
    def takeWhileInclusive(p: A => Boolean) = { 
    val (a, b) = i.span(p) 
    a ++ (if (b.hasNext) Some(b.next) else None) 
    } 
} 

object ImplicitIterator { 
    implicit def extendIterator[A](i : Iterator[A]) = new IteratorExtension(i) 
} 

import ImplicitIterator._ 

Ora (0 until 10).toIterator.takeWhileInclusive(_ < 4).toListList(0, 1, 2, 3, 4), per esempio.

+1

L'ultima riga del tuo metodo può essere scritta in modo più succinto come 'a ++ (b prendi 1)' –

2
class IteratorExtension[T](i : Iterator[T]) { 
    def takeWhileInclusive(predicate:(T) => Boolean) = new Iterator[T] { 
    val it = i 
    var isLastRead = false 

    def hasNext = it.hasNext && !isLastRead 
    def next = { 
     val res = it.next 
     isLastRead = !predicate(res) 
     res 
    } 
    } 
} 

E c'è un errore nel tuo implicito. Qui è fissato:

object ImplicitIterator { 
    implicit def extendIterator[T](i : Iterator[T]) = new IteratorExtension(i) 
} 
+0

Wow, c'erano alcuni errori gravi nella mia versione precedente. –

+0

Questo era il punto in cui stava andando il mio pensiero, grazie per esserci arrivato per me! Offre un buon approccio generalizzato. Vorrei che ci fosse una soluzione generale più elegante che doveva costruire un nuovo iteratore. –

3

Quanto segue richiede scalaz per ottenere fold su una tupla (A, B)

scala> implicit def Iterator_Is_TWI[A](itr: Iterator[A]) = new { 
    | def takeWhileIncl(p: A => Boolean) 
    | = itr span p fold (_ ++ _.toStream.headOption) 
    | } 
Iterator_Is_TWI: [A](itr: Iterator[A])java.lang.Object{def takeWhileIncl(p: A => Boolean): Iterator[A]} 

qui è al lavoro:

scala> List(1, 2, 3, 4, 5).iterator takeWhileIncl (_ < 4) 
res0: Iterator[Int] = non-empty iterator 

scala> res0.toList 
res1: List[Int] = List(1, 2, 3, 4) 

Si può rotolare il proprio volte nel corso di un paio di simile:

scala> implicit def Pair_Is_Foldable[A, B](pair: (A, B)) = new { 
    | def fold[C](f: (A, B) => C): C = f.tupled(pair) 
    | } 
Pair_Is_Foldable: [A, B](pair: (A, B))java.lang.Object{def fold[C](f: (A, B) => C): C}