X-Git-Url: http://nitlanguage.org diff --git a/lib/pipeline.nit b/lib/pipeline.nit index 055823f..54be22b 100644 --- a/lib/pipeline.nit +++ b/lib/pipeline.nit @@ -14,9 +14,8 @@ # Pipelined filters and operations on iterators. # -# This module enhance `Iterator`s with some methods that enable a -# pipeline-like programing that offers the manupulation of -# collections trough connected filters with reasonable memory constraints. +# This module enhances `Iterator` with some methods that enable a pipeline-like programing. +# The processing of elements in a pipeline is done trough connected filters that are implemented with reasonable memory constraints. module pipeline redef interface Iterator[E] @@ -35,7 +34,9 @@ redef interface Iterator[E] # Filter: sort with a given `comparator`. # Important: require O(n) memory. - fun sort_with(comparator: Comparator[E]): Iterator[E] + # + # assert ["a", "c", "b"].iterator.sort_with(alpha_comparator).to_a == ["a", "b", "c"] + fun sort_with(comparator: Comparator): Iterator[E] do var a = self.to_a comparator.sort(a) @@ -72,6 +73,8 @@ redef interface Iterator[E] # When the first iterator is terminated, the second is started. # # assert ([1..20[.iterator + [20..40[.iterator).to_a == ([1..40[).to_a + # + # SEE: `Iterator2` fun +(other: Iterator[E]): Iterator[E] do return new PipeJoin[E](self, other) @@ -79,7 +82,7 @@ redef interface Iterator[E] # Alternate each item with `e`. # - # assert [1,2,3].iterator.alternate(0).to_a == [1,0,2,0,3] + # assert [1,2,3].iterator.alternate(0).to_a == [1,0,2,0,3] fun alternate(e: E): Iterator[E] do return new PipeAlternate[E](self, e) @@ -87,7 +90,7 @@ redef interface Iterator[E] # Filter: reject a given `item`. # - # assert [1,1,2,1,3].iterator.skip(1).to_a == [2,3] + # assert [1,1,2,1,3].iterator.skip(1).to_a == [2,3] fun skip(item: E): Iterator[E] do return new PipeSkip[E](self, item) @@ -99,7 +102,7 @@ redef interface Iterator[E] # # var i = [1,2,3,4,5].iterator # assert i.head(2).to_a == [1,2] - # i.to_a == [3,4,5] + # assert i.to_a == [3,4,5] fun head(length: Int): Iterator[E] do return new PipeHead[E](self, length) @@ -158,6 +161,134 @@ redef interface Iterator[E] end end +# Concatenates a sequence of iterators. +# +# Wraps an iterator of sub-iterators and iterates over the elements of the +# sub-iterators. +# +# ~~~nit +# var i: Iterator[Int] +# var empty = new Array[Int] +# +# i = new Iterator2[Int]([ +# [1, 2, 3].iterator, +# empty.iterator, +# [4, 5].iterator +# ].iterator) +# assert i.to_a == [1, 2, 3, 4, 5] +# +# i = new Iterator2[Int]([ +# empty.iterator, +# [42].iterator, +# empty.iterator +# ].iterator) +# assert i.to_a == [42] +# ~~~ +# +# SEE: `Iterator::+` +class Iterator2[E] + super Iterator[E] + + # The inner iterator over sub-iterators. + var inner: Iterator[Iterator[E]] + + redef fun finish + do + var i = current_iterator + if i != null then i.finish + end + + redef fun is_ok + do + var i = current_iterator + if i == null then return false + return i.is_ok + end + + redef fun item + do + var i = current_iterator + assert i != null + return i.item + end + + redef fun next + do + var i = current_iterator + assert i != null + i.next + end + + redef fun start + do + var i = current_iterator + if i != null then i.start + end + + private var previous_iterator: nullable Iterator[E] = null + + private fun current_iterator: nullable Iterator[E] + do + if previous_iterator == null then + # Get the first sub-iterator. + if inner.is_ok then + previous_iterator = inner.item + previous_iterator.start + inner.next + else + return null + end + end + # Get the first sub-iterator that has a current item. + while inner.is_ok and not previous_iterator.is_ok do + previous_iterator.finish + previous_iterator = inner.item + previous_iterator.start + inner.next + end + return previous_iterator + end +end + +# Wraps an iterator to skip nulls. +# +# ~~~nit +# var i: Iterator[Int] +# +# i = new NullSkipper[Int]([null, 1, null, 2, null: nullable Int].iterator) +# assert i.to_a == [1, 2] +# +# i = new NullSkipper[Int]([1, null, 2, 3: nullable Int].iterator) +# assert i.to_a == [1, 2, 3] +# ~~~ +class NullSkipper[E: Object] + super Iterator[E] + + # The inner iterator. + var inner: Iterator[nullable E] + + redef fun finish do inner.finish + + redef fun is_ok do + skip_nulls + return inner.is_ok + end + + redef fun item do + skip_nulls + return inner.item.as(E) + end + + redef fun next do + inner.next + skip_nulls + end + + private fun skip_nulls do + while inner.is_ok and inner.item == null do inner.next + end +end + # Interface that reify a function. # Concrete subclasses must implements the `apply` method. # @@ -294,13 +425,7 @@ private class PipeSkip[E] var source: Iterator[E] var skip_item: E - init(source: Iterator[E], skip_item: E) - do - self.source = source - self.skip_item = skip_item - - do_skip - end + init do do_skip fun do_skip do @@ -345,10 +470,8 @@ private class PipeSkipTail[E] var lasts = new List[E] - init(source: Iterator[E], length: Int) + init do - self.source = source - self.length = length var lasts = self.lasts while source.is_ok and lasts.length < length do lasts.push(source.item) @@ -375,13 +498,7 @@ private class PipeSelect[E] var predicate: Function[E, Bool] - init(source: Iterator[E], predicate: Function[E, Bool]) - do - self.source = source - self.predicate = predicate - - do_skip - end + init do do_skip fun do_skip do