Given a string with many words, I want to reverse the order of the words. If the input is Thanks for all the fish
the output should be fish the all for Thanks
.
I'm trying to solve this using generators, to avoid creating a new list. I came up with the following solution:
from itertools import islice
def foo(s, begin, end):
for elem in islice(s, begin, end):
yield elem
def baz(s):
i = len(s)
j = len(s)
while True:
i -= 1
if i == -1:
foo(s, i+1, j)
break
if s[i] == ' ':
foo(s, i+1, j)
yield ' '
j = i
s = "Thanks for all the fish"
g = baz(s)
for x in g: print(x, end='')
However, the output of this code is " "
(a string with only the spaces).
On the other hand, if I directly print
the elements instead of yielding them, it works:
from itertools import islice
def foo(s, begin, end):
for elem in islice(s, begin, end):
print(elem, end='')
def baz(s):
i = len(s)
j = len(s)
while True:
i -= 1
if i == -1:
foo(s, i+1, j)
break
if s[i] == ' ':
foo(s, i+1, j)
print(' ', end='')
j = i
s = "Thanks for all the fish"
g = baz(s)
This works and have as output fish the all for Thanks
. But I do not want to just be able to print it, I want to have a correct generator.
Finally, I discovered that if avoid the calls to foo
it also works:
from itertools import islice
def baz(s):
i = len(s)
j = len(s)
while True:
i -= 1
if i == -1:
for elem in islice(s, i+1, j):
yield elem
break
if s[i] == ' ':
for elem in islice(s, i+1, j):
yield elem
yield ' '
j = i
s = "Thanks for all the fish"
g = baz(s)
for x in g: print(x, end='')
The output of this is fish the all for Thanks
.
This version gives me what I want (a correct generator), but I'm repeating code inside its function.
I red the stack exchange thread about what yield does, but I might have understood it wrong. What I understood is that a generator will run until it finds the next yield statement (or the function ends). This problem makes me think this is not exactly how it works, but apart from that I'm clueless.
How can I solve this using helper functions like in the first code snippet?
You can use yield from foo(...)
(see also PEP-380 on "Syntax for Delegating to a Subgenerator") or for whatever in foo(...): yield whatever
to successivly yield
the elements from a generator:
def baz(s):
i = len(s)
j = len(s)
while True:
i -= 1
if i == -1:
yield from foo(s, i+1, j) # yield from syntax
break
if s[i] == ' ':
for item in foo(s, i+1, j): # for-loop over generator
yield item
yield ' '
j = i
But, as @trentcl noted in the comments, in your case you don't need the foo
-helper function because it basically does what islice
does too. So you could simply replace foo
with islice
:
def baz(s):
i = len(s)
j = len(s)
while True:
i -= 1
if i == -1:
yield from islice(s, i+1, j) # yield from syntax
break
if s[i] == ' ':
for item in islice(s, i+1, j): # for-loop over generator
yield item
yield ' '
j = i