I have created an itertools
cycle for the English alphabet using the code below,
lowercase_letters_cycle = itertools.cycle(string.ascii_lowercase)
If I run a for
loop on this iterator object, the first iteration would give me "a" as the output because the cycle starts from "a". How can I make it so that cycle starts from any letter of my choice?
One way that works is,
def start_cycle(letter):
lowercase_letters_cycle = itertools.cycle(lowercase_letters)
letter_index = lowercase_letters.index(letter)
index = 0
while True:
if index == letter_index:
break
letter = next(lowercase_letters_cycle)
index = 1
return lowercase_letters_cycle
But is there any shorter method?
CodePudding user response:
The itertools
documentation supplies a recipe for consuming a number of items from an iterator.
from itertools import islice
import collections
def consume(iterator, n=None):
"Advance the iterator n-steps ahead. If n is None, consume entirely."
# Use functions that consume iterators at C speed.
if n is None:
# feed the entire iterator into a zero-length deque
collections.deque(iterator, maxlen=0)
else:
# advance to the empty slice starting at position n
next(islice(iterator, n, n), None)
So you create the cycle, then just consume a bit of it before proceeding.
lowercase_letters_cycle = itertools.cycle(string.ascii_lowercase)
consume(lowercase_letters_cycle, ord('n') - ord('a'))
assert next(lowercase_letters_cycle) == 'n')
The same consume
is also available from the third-party more-itertools
package.
CodePudding user response:
You can combine islice
and cycle
from the itertools
module as follows:
import string
import itertools
my_it = itertools.islice(itertools.cycle(string.ascii_lowercase), 3, None)
It will yield d
(the character that comes after the first 3), then e
, ...., then z
, then a
, then b
, and so on. You can change the number in the second argument to islice
to start from a different letter.
CodePudding user response:
You could just slice the input list to start at the index of your choice.
offset = 10
lowercase_letters = list(string.ascii_lowercase)
# offset_index to end start to offset_index-1
offset_letters = lowercase_letters[offset:] lowercase_letters[:offset]
offset_letters_cycle = itertools.cycle(offset_letters)
Then,
for i in range(10):
print(next(offset_letters_cycle), end=" ")
prints:
k l m n o p q r s t
CodePudding user response:
import string
from itertools import cycle, dropwhile
def start_cycle(letter):
return dropwhile(lambda x: x!= letter, cycle(string.ascii_lowercase))
itertools.dropwhile
will feed the iterable results to its sentinel function - in this case lambda x: x != letter
, and will "swallow" the results until the function returns False
for the first time. At that point
the function is no longer called, and the iterable proceeds, yielding any
further values.
Also, note there is no need to convert ascii_lowercase
to a list,
as strings are already iterables.
Keep in mind that this will run into an infinite loop with 100% CPU if a character not in ascii_lowercase is passed. It is better to guard it with a check:
def start_cycle(letter):
if letter not in string.ascii_lowercase:
raise ValueError()
return dropwhile(lambda x: x!= letter, cycle(string.ascii_lowercase))