1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Pipelined filters and operations on iterators.
17 # This module enhance `Collection`s with some methods that enable a
18 # pipeline-like programing that offers the manupulation of
19 # collections trough connected filters with reasonable memory constraints.
21 # The filters methods return a view bounded on the original collection.
22 # Filters can be chained to build complex virtual collections.
24 # FIXME: Fix vocabulary (lazy/view/filter)
25 # FIXME: Maybe the name of the method should indicate that the method is 'lazy' (or is a filter).
28 redef interface Collection[E
]
29 # Filter: sort with `default_comparator`.
30 # SEE: `sort_with` for details
31 # REQUIRE: self isa Iterator[Comparable]
33 # assert [1,3,2].sort_filter.to_a == [1,2,3]
34 fun sort_filter
: Collection[E
]
36 assert self isa Collection[Comparable]
38 default_comparator
.sort
(a
)
42 # Filter: sort with a given `comparator`.
43 # Important: require O(n) memory.
44 fun sort_with
(comparator
: Comparator[E
]): Collection[E
]
51 # Filter: reject duplicates.
52 # Elements already seen are rejected.
54 # Important: rely on `==` and `hash`
55 # Important: require O(m) in memory, where m is the total number of uniq items.
57 # assert [1,2,1,1,1,3,2].uniq.to_a == [1,2,3]
59 # REQUIRE: self isa Iterator[Object]
60 fun uniq
: Collection[E
]
62 assert self isa Collection[Object]
63 return new PipeUniq[E
](self)
66 # Filter: reject continuous sequences of duplicates
68 # Important: rely on `==`.
70 # assert [1,2,1,1,1,3,2].seq_uniq.to_a == [1,2,1,3,2]
71 fun seq_uniq
: Collection[E
]
73 return new PipeSeqUniq[E
](self)
76 # The view of two combined collections.
78 # assert ([1..20[ + [20..40[).to_a == ([1..40[).to_a
79 fun +(other
: Collection[E
]): Collection[E
]
81 return new PipeJoin[E
](self, other
)
84 # Alternate each item with `e`.
86 # assert [1,2,3].alternate(0).to_a == [1,0,2,0,3]
87 fun alternate
(e
: E
): Collection[E
]
89 return new PipeAlternate[E
](self, e
)
92 # Filter: reject a given `item`.
94 # assert [1,1,2,1,3].skip(1).to_a == [2,3]
95 fun skip
(item
: E
): Collection[E
]
97 return new PipeSkip[E
](self, item
)
100 # Filter: keep only the first `length` items.
102 # assert [1,2,3,4,5].head(2).to_a == [1,2]
103 fun head
(length
: Int): Collection[E
]
105 return new PipeHead[E
](self, length
)
108 # Filter: reject the first `length` items.
110 # assert [1,2,3,4,5].skip_head(2).to_a == [3,4,5]
112 # ENSURE: self == return
113 fun skip_head
(length
: Int): Collection[E
]
115 return new PipeSkipHead[E
](self, length
)
118 # Filter: keep only the last `length` items.
120 # assert [1,2,3,4,5].tail(2).to_a == [4,5]
122 # Important: require O(length) in memory
123 fun tail
(length
: Int): Collection[E
]
125 return new PipeTail[E
](self, length
)
128 # Filter: reject the last `length` items.
130 # assert [1,2,3,4,5].skip_tail(2).to_a == [1,2,3]
132 # Important: require O(length) in memory
133 fun skip_tail
(length
: Int): Collection[E
]
135 return new PipeSkipTail[E
](self, length
)
139 ### Specific private collection and iterator classes
141 private class PipeUniq[E
]
143 var source
: Collection[E
]
144 redef fun iterator
do return new PipeUniqIterator[E
](source
.iterator
)
147 private class PipeUniqIterator[E
]
150 var source
: Iterator[E
]
152 var seen
= new HashSet[Object] # FIXME HashSet[E]
154 redef fun is_ok
do return source
.is_ok
156 redef fun item
do return source
.item
160 self.seen
.add
(self.item
.as(Object))
162 while source
.is_ok
and self.seen
.has
(source
.item
.as(Object)) do
168 private class PipeSeqUniq[E
]
170 var source
: Collection[E
]
171 redef fun iterator
do return new PipeSeqUniqIterator[E
](source
.iterator
)
174 private class PipeSeqUniqIterator[E
]
177 var source
: Iterator[E
]
179 redef fun is_ok
do return source
.is_ok
181 redef fun item
do return source
.item
187 while source
.is_ok
and seen
== source
.item
do
193 private class PipeJoin[E
]
195 var source1
: Collection[E
]
196 var source2
: Collection[E
]
197 redef fun iterator
do return new PipeJoinIterator[E
](source1
.iterator
, source2
.iterator
)
200 private class PipeJoinIterator[E
]
202 var source1
: Iterator[E
]
203 var source2
: Iterator[E
]
207 return source1
.is_ok
or source2
.is_ok
212 if source1
.is_ok
then return source1
.item
else return source2
.item
217 if source1
.is_ok
then source1
.next
else source2
.next
221 private class PipeAlternate[E
]
223 var source
: Collection[E
]
225 redef fun iterator
do return new PipeAlternateIterator[E
](source
.iterator
, odd_item
)
228 private class PipeAlternateIterator[E
]
231 var source
: Iterator[E
]
235 redef fun is_ok
do return source
.is_ok
255 private class PipeSkip[E
]
257 var source
: Collection[E
]
259 redef fun iterator
do return new PipeSkipIterator[E
](source
.iterator
, skip_item
)
262 private class PipeSkipIterator[E
]
266 var source
: Iterator[E
]
269 init(source
: Iterator[E
], skip_item
: E
)
272 self.skip_item
= skip_item
279 while source
.is_ok
and source
.item
== skip_item
do source
.next
282 redef fun is_ok
do return source
.is_ok
284 redef fun item
do return source
.item
293 private class PipeHead[E
]
295 var source
: Collection[E
]
297 redef fun iterator
do return new PipeHeadIterator[E
](source
.iterator
, pipe_length
)
300 private class PipeHeadIterator[E
]
303 var source
: Iterator[E
]
307 redef fun is_ok
do return length
> 0 and source
.is_ok
309 redef fun item
do return source
.item
318 private class PipeSkipHead[E
]
320 var source
: Collection[E
]
324 var res
= source
.iterator
325 var length
= pipe_length
326 while length
> 0 and res
.is_ok
do
334 private class PipeTail[E
]
336 var source
: Collection[E
]
340 var lasts
= new List[E
]
341 var res
= source
.iterator
342 var length
= pipe_length
344 while lasts
.length
>= length
do lasts
.shift
348 return lasts
.iterator
352 private class PipeSkipTail[E
]
354 var source
: Collection[E
]
356 redef fun iterator
do return new PipeSkipTailIterator[E
](source
.iterator
, pipe_length
)
359 private class PipeSkipTailIterator[E
]
362 var source
: Iterator[E
]
366 var lasts
= new List[E
]
368 init(source
: Iterator[E
], length
: Int)
372 var lasts
= self.lasts
373 while source
.is_ok
and lasts
.length
< length
do
374 lasts
.push
(source
.item
)
379 redef fun is_ok
do return source
.is_ok
381 redef fun item
do return lasts
.first
386 lasts
.push
(source
.item
)