I'm trying to figure out how an iterator works with this example:
There is the function that produces stream generator for given iterable object (list, generator, etc) whose elements contain position and value and sorted by order of apperance. Stream generator is equal to initial stream (without position), gaps filled with zeroes.
from itertools import count
def gen_stream(total, sorted_iterable, extractor=lambda x: x):
sorted_iterator = iter(sorted_iterable)
iterable = count() if total is None else range(total)
try:
current_extracted_record = extractor(next(sorted_iterator))
except StopIteration:
current_extracted_record = None
for i in iterable:
if current_extracted_record:
if i == current_extracted_record[0]:
try:
yield current_extracted_record[1]
current_extracted_record = extractor(next(sorted_iterator))
except StopIteration:
current_extracted_record = None
else:
yield 0
else:
yield 0
For example:
gen = gen_stream(9,[(4,111),(7,12)])
list(gen)
[0, 0, 0, 0, 111, 0, 0, 12, 0] # first element has zero index, so 111 located on fifth position, 12 located on 8th position
This function also support custom position-value extractor for more advanced cases, e.g.
def day_extractor(x):
months = [31, 28, 31, 30, 31, 31, 30, 31, 30, 31, 30, 31]
acc = sum(months[:x[1] - 1]) + x[0] - 1
return acc, x[2]
precipitation_days = [(3,1,4),(5,2,6)]
list(gen_stream(59,precipitation_days,day_extractor)) #59: January and February to limit output
[0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
precipitation_days format is following: (d,m,mm), where d - day in month, m - month, mm - precipitation in millimeters So, in example:
(3,1,4) # January,3 precipitation: 4 mm
(5,2,6) # February,5 precipitation: 6 mm
Extractor passed as optional third parameter with default value - lambda function that handles (position, value) pairs like in first example.
This is where the questions begin
Question 1 Can I replace
try:
current_extracted_record = extractor(next(sorted_iterator))
except StopIteration:
current_extracted_record = None
with default value for the function next instead of catching the exception StopIteration by using one line of code
current_extracted_record = extractor(next((sorted_iterator), None))
will it always work correctly in other situations?
Question 2 How can this part be replaced by using default value for the method next() and loop while instead of the loop for. In theory, the code should be shorter.
for i in iterable:
if current_extracted_record:
if i == current_extracted_record[0]:
try:
yield current_extracted_record[1]
current_extracted_record = extractor(next(sorted_iterator))
except StopIteration:
current_extracted_record = None
else:
yield 0
else:
yield 0
Question 3 This may seem like an idiotic question, but as I understand it, the extractor does not have an index. Then what do the numbers in square brackets mean?
current_extracted_record[0]
current_extracted_record[1]
Thanks if you can help.
I apologize for the 3 questions in the thread, but it seems to me that they describe the same problem in different details.
Answer (Question1&Question2)
def gen_stream(total, sorted_iterable, extractor=lambda x: x):
elem_iter = iter(map(extractor, sorted_iterable))
pos, val = next(elem_iter, (None, None))
cnt = 0
while total is None or cnt < total:
if cnt == pos:
yield val
pos, val = next(elem_iter, (None, None))
else:
yield 0
cnt += 1