99import itertools
1010import builtins
1111
12+ from python_toolbox import nifty_collections
1213
1314infinity = float ('inf' )
1415
1516
16- def iterate_overlapping_subsequences (iterable , length = 2 , wrap_around = False ):
17+ def iterate_overlapping_subsequences (iterable , length = 2 , wrap_around = False ,
18+ lazy_tuple = False ):
1719 '''
1820 Iterate over overlapping subsequences from the iterable.
1921
@@ -26,7 +28,21 @@ def iterate_overlapping_subsequences(iterable, length=2, wrap_around=False):
2628
2729 If `wrap_around=True`, the result would be `[(0, 1, 2), (1,
2830 2, 3), (2, 3, 0), (3, 0, 1)]`.
31+
32+ If `lazy_tuple=True`, returns a `LazyTuple` rather than an iterator.
2933 '''
34+ iterator = _iterate_overlapping_subsequences (
35+ length = length , wrap_around = wrap_around
36+ )
37+
38+ if lazy_tuple :
39+ return nifty_collections .LazyTuple (iterator )
40+ else :
41+ return iterator
42+
43+
44+ def _iterate_overlapping_subsequences (iterable , length , wrap_around ):
45+
3046 if length == 1 :
3147 yield from iterable
3248
@@ -65,15 +81,26 @@ def iterate_overlapping_subsequences(iterable, length=2, wrap_around=False):
6581 yield tuple (deque )
6682
6783
68- def shorten (iterable , n ):
84+ def shorten (iterable , n , lazy_tuple = False ):
6985 '''
7086 Shorten an iterable to length `n`.
7187
7288 Iterate over the given iterable, but stop after `n` iterations (Or when the
7389 iterable stops iteration by itself.)
7490
7591 `n` may be infinite.
92+
93+ If `lazy_tuple=True`, returns a `LazyTuple` rather than an iterator.
7694 '''
95+ iterator = _shorten (iterable = iterable , n = n )
96+
97+ if lazy_tuple :
98+ return nifty_collections .LazyTuple (iterator )
99+ else :
100+ return iterator
101+
102+
103+ def _shorten (iterable , n ):
77104
78105 if n == infinity :
79106 yield from iterable
@@ -98,7 +125,18 @@ def enumerate(reversible, reverse_index=False):
98125 reverse index, by specifying `reverse_index=True`. This causes `i` to count
99126 down to zero instead of up from zero, so the `i` of the last member will be
100127 zero.
128+
129+ If `lazy_tuple=True`, returns a `LazyTuple` rather than an iterator.
101130 '''
131+ iterator = _enumerate (reversible = reversible , reverse_index = reverse_index )
132+
133+ if lazy_tuple :
134+ return nifty_collections .LazyTuple (iterator )
135+ else :
136+ return iterator
137+
138+
139+ def _enumerate (reversible , reverse_index ):
102140 if reverse_index is False :
103141 return builtins .enumerate (reversible )
104142 else :
@@ -132,8 +170,21 @@ def get_length(iterable):
132170 return i
133171
134172
135- def iter_with (iterable , context_manager ):
136- '''Iterate on `iterable`, `with`ing the context manager on every `next`.'''
173+ def iter_with (iterable , context_manager , lazy_tuple = False ):
174+ '''
175+ Iterate on `iterable`, `with`ing the context manager on every `next`.
176+
177+ If `lazy_tuple=True`, returns a `LazyTuple` rather than an iterator.
178+ '''
179+ iterator = _iter_with (iterable = iterable , context_manager = context_manager )
180+
181+ if lazy_tuple :
182+ return nifty_collections .LazyTuple (iterator )
183+ else :
184+ return iterator
185+
186+
187+ def _iter_with (iterable , context_manager ):
137188
138189 iterator = iter (iterable )
139190
@@ -163,13 +214,15 @@ def get_items(iterable, n, container_type=tuple):
163214 return container_type (shorten (iterable , n ))
164215
165216
166- def double_filter (filter_function , iterable ):
217+ def double_filter (filter_function , iterable , lazy_tuple = False ):
167218 '''
168219 Filter an `iterable` into two lists according to a `filter_function`.
169220
170221 This is similar to the builtin `filter`, except it returns a tuple of two
171222 iterators, the first iterating on items that passed the filter function,
172223 and the second iterating on items that didn't.
224+
225+ If `lazy_tuple=True`, returns a `LazyTuple` rather than an iterator.
173226 '''
174227 iterator = iter (iterable )
175228
@@ -198,7 +251,12 @@ def make_false_iterator():
198251 else :
199252 yield value
200253
201- return (make_true_iterator (), make_false_iterator ())
254+ iterators = (make_true_iterator (), make_false_iterator ())
255+
256+ if lazy_tuple :
257+ return tuple (map (nifty_collections .LazyTuple , iterators ))
258+ else :
259+ return iterators
202260
203261
204262
@@ -217,7 +275,7 @@ def get_ratio(filter_function, iterable):
217275
218276
219277def fill (iterable , fill_value = None , fill_value_maker = None , length = infinity ,
220- sequence_type = None ):
278+ sequence_type = None , lazy_tuple = False ):
221279 '''
222280 Iterate on `iterable`, and after it's exhaused, yield fill values.
223281
@@ -228,19 +286,25 @@ def fill(iterable, fill_value=None, fill_value_maker=None, length=infinity,
228286 If `length` is given, shortens the iterator to that length.
229287
230288 If `sequence_type` is given, instead of returning an iterator, this
231- function will return a sequence of that type.
289+ function will return a sequence of that type. If `lazy_tuple=True`, uses a
290+ `LazyTuple`. (Can't use both options together.)
232291 '''
292+ # Validating user input:
293+ assert (sequence_type is None ) or (lazy_tuple is False )
294+
233295 iterator = _fill (iterable , fill_value = fill_value ,
234296 fill_value_maker = fill_value_maker ,
235297 length = length )
236298
237- if sequence_type is None :
299+ if lazy_tuple :
300+ return nifty_collections .LazyTuple (iterator )
301+ elif sequence_type is None :
238302 return iterator
239303 else :
240304 return sequence_type (iterator )
241305
242306
243- def _fill (iterable , fill_value = None , fill_value_maker = None , length = infinity ):
307+ def _fill (iterable , fill_value , fill_value_maker , length ):
244308 if fill_value_maker is not None :
245309 assert fill_value is None
246310 else :
0 commit comments