Return pieces of an eternal

Asked

Viewed 77 times

1

Implement a generator function, chunker, which takes an iterable and returns a specific piece of size at a time. Resorting to the function like this:

for chunk in chunker(range(25), 4):
    print(list(chunk))

should result in the exit:

[0, 1, 2, 3]
[4, 5, 6, 7]
[8, 9, 10, 11]
[12, 13, 14, 15]
[16, 17, 18, 19]
[20, 21, 22, 23]
[24]

I’m struggling once again on this subject about generators in Python. I can run the code, but not with the desired output. I’m not able to create the columns as he asks in the question, I can only count the elements with range().

This was my code :

def chunker(iterable, size):
    num = size
    for item in iterable:
       yield item,size


for chunk in chunker(range(25), 4):
    print(list(chunk))

That generated this output:

[0, 4]
[1, 4]
[2, 4]
[3, 4]
[4, 4]
[5, 4]
[6, 4]
[7, 4]
[8, 4]
[9, 4]
[10, 4]
[11, 4]
[12, 4]
[13, 4]
[14, 4]
[15, 4]
[16, 4]
[17, 4]
[18, 4]
[19, 4]
[20, 4]
[21, 4]
[22, 4]
[23, 4]
[24, 4]

2 answers

4


Miguel’s solution works, but it has limitations. The first is that it is necessary to convert the generator to list when passing as parameter, which already hurts the requested in the statement, which specifies that the parameter is eternal (and not only a list). Just call chunker(range(25), 4) already produces the expected return, but it is a particularity of the type range (it implements the methods magicians in order to generate some results without consuming the generator, such as __len__). For other generators the solution may not work:

def gerador():
    nome = "Stack Overflow"
    for char in nome:
        yield char

def chunker(iterable, size):
    for i in range(0, len(iterable), size):
        yield iterable[i:i+size]

for chunk in chunker(gerador(), 4):
    print(list(chunk))

Resulting in:

Traceback (most recent call last):
  File "python", line 10, in <module>
  File "python", line 7, in chunker
TypeError: object of type 'generator' has no len()

The best solution would be to use the function iter together with the function itertools.islice:

def chunker(iterable, size):
    it = iter(iterable)
    return iter(lambda: list(islice(it, size)), [])

See working on Repl.it | Ideone

So the exit would be:

['S', 't', 'a', 'c']
['k', ' ', 'O', 'v']
['e', 'r', 'f', 'l']
['o', 'w']

Or, for the example of the question:

[0, 1, 2, 3]
[4, 5, 6, 7]
[8, 9, 10, 11]
[12, 13, 14, 15]
[16, 17, 18, 19]
[20, 21, 22, 23]
[24]
  • I was now working on a solution so I wouldn’t have to turn the entry into a list. Good solution

  • 1

    That’s why we’re here :D

  • 1

    Anderson notes that I put list(range(25)) on purpose (I know I don’t need it with range), but this way I would implement yours gerador(), chunker(list(gerador()), 4) e.g. Your solution is better regardless of that, my biggest problem is actually having to turn into a list

3

Here’s what you can do:

def chunker(iterable, size):
    for i in range(0, len(iterable), size): # percorremos o nosso range com um step de 4 neste caso, [0, 4, 8, 12, 16, 20, 24]
        yield iterable[i:i+size] # ficamos com os valores do nosso iteravel contidos no intervalo entre i e i+size, na primeira volta entre 0 e 0+4 ([0,1,2,3]) na segunda entre 4 e 4+4 ([4, 5, 6, 7]), etc...

c = chunker(list(range(25)), 4)
print(list(c)) # [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15], [16, 17, 18, 19], [20, 21, 22, 23], [24]]

You loop over this generator like this:

for i in chunker(list(range(25)), 4):
    print(i)

DEMONSTRATION

  • 1

    thanks , only with this code has already worked :

  • def chunker(iterable, size): for i in range(0, Len(iterable), size): Yield iterable[i:i+size]

  • You’re welcome @Cleomirsantos

Browser other questions tagged

You are not signed in. Login or sign up in order to post.