how to get a cycle without using a loop in python? - python

im teaching my self python and i came across this interesting question which says:
Implement a generator cycle such that if we assign
i = cycle()
then repeated calls to
next(i)
return the values
me
myself
i
me
myself
i
...
I can't use a for loop but only a generator or stream. I cant use libraries. I need to output it 20 times
what I've tried so far but i cant manage to get the cycle to work:
def cycle(i):
saved = []
for el in m:
yield el
saved.append(el)
while saved:
for el in saved:
yield element

This is probably what you want:
def cycle(n=0):
saved=['me','myself','i']
while True:
yield saved[n]
n = (n+1) % 3
i = cycle()
for _ in range(20):
print(next(i))

If you don't want loops at all, try this:
class cycle:
def __init__(self,lst,maxstep = 20):
self.lst = lst
self.n = 0
self.len = len(lst)
self.maxstep = maxstep
def __next__(self):
if self.n>=self.maxstep: # remove this line
raise StopIteration # and this line, if you want infinite stream
ret = self.lst[self.n % self.len]
self.n += 1
return ret
obj = cycle(['me','myself','i'])
If you want to be able to call list on it, that is make it iterable:
class cycle():
def __init__(self,lst,maxstep = 20):
self.lst = lst
self.n = 0
self.i = 0
self.len = len(lst)
self.maxstep = maxstep
def __iter__(self):
while self.i < self.maxstep:
yield self.lst[self.i % self.len]
self.i += 1
def __next__(self):
if self.n>=self.maxstep:
raise StopIteration
ret = self.lst[self.n % self.len]
self.n += 1
return ret
obj = cycle([1, 2, 3])
print(list(obj))
# [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2]
# or
# print(next(obj))
# 1

Related

Why can't my version of a sorted list in Python add a second number?

In the following code, I'm able to add the first number, but I can't add the second number. Inside the class, self correctly updates to [1, 3], but the instance stays at [1]. Why is that and how do I fix it?
from bisect import bisect
class SortedList(list):
def __init__(self):
self = []
def add(self, x):
index = bisect(self, x)
if not self:
self.append(x)
else:
self = self + [x] + self[index:] # after the second call self = [1, 3]
pass
t = 1
for _ in range(t):
n, a, b = 24, 3, 5
if b == 1:
print('yes')
else:
sl = SortedList() # sl = []
st = set()
sl.add(1) # sl = [1]
st.add(1)
i = 0
while sl[i] < n: # when i == 1, there's an error because sl = [1]
num1 = sl[i] * a # num1 = 3
num2 = sl[i] + b
if num1 > sl[i] and num1 not in st:
sl.add(num1) # after this, sl = [1] still
st.add(num1)
if num2 > 1 and num2 not in st:
sl.add(num2)
st.add(num2)
if n in st:
break
i += 1
print('yes' if n in st else 'no')
Don't modify self, when you assign the resulting list to self you change its reference in the local context of the function. But the remote reference keep unchanged.
Better explained in Is it safe to replace a self object by another object of the same type in a method?
By sub-classing list:
import bisect
class SortedList(list):
def append(self, x):
if not self:
super(SortedList, self).append(x)
else:
idx = bisect.bisect(self, x)
self.insert(idx, x)
And then:
>>> import random
>>> l = SortedList()
>>> for i in range(10):
... l.append(random.randint(0, 100))
>>> print(l)
[5, 31, 50, 58, 69, 69, 70, 78, 85, 99]
In order to keep the list sorted you should also override some magic methods such __add__, __iadd__ ...
By wrapping list:
class SortedList(object):
def __init__(self):
self._data = []
def append(self, x):
if not self:
self._data = [x]
else:
idx = bisect.bisect(self, x)
# Here you can change self._data
# Even `insert` is better, because it don't need to copy the list
# you can do
self._data = self._data[:idx] + [x] + self._data[idx:]
But it's very partial, in order to have SortedList look like a list you have to implement the sequence protocol.
self = []
...
self = self + [x] + self[index:]
This doesn't do what you think it does. Whenever you write a = ... (without indexing on the left hand side, which is special) you don't actually change what a is. Instead, you just take whatever is on the right hand side and give it the name a, ignoring whatever a was before.
Example:
>>> a = [1, 2, 3]
>>> b = a
>>> a is b
True
>>> id(a), id(b)
(140484089176136, 140484089176136)
>>> a.append(4) # Modification.
>>> b
[1, 2, 3, 4]
>>> a = a + [5] # Modification? No! We create a new object and label it 'a'.
>>> a
[1, 2, 3, 4, 5]
>>> b
[1, 2, 3, 4]
>>> a is b
False
>>> id(a), id(b)
(140484995173192, 140484089176136)
What you want is bisect_left and to use list.insert:
index = bisect_left(li, x)
li.insert(index, x)
Thanks, everyone. I was able to get the code to work as expected by changing the class as follows:
class SortedList(list):
def add(self, x):
index = bisect(self, x)
self.insert(index, x)

Replacing a for loop with tail recursion in a recursive function

I'm trying to make the following function completely tail recursive, e.g. get that pesky for loop out of there. Reason being is that I'm trying to easily convert the solution to an iterative one involving the use of an explicit stack. Please advise.
def permutations(A):
P = []
P2 = []
permutations_recursive(A, [], P)
permutations_tail_recursive(A, [], P2, 0)
print(P2)
return P
def permutations_recursive(first, last, perms):
if len(first) == 0:
perms.append(last)
else:
for i in range(len(first)):
permutations_recursive(
first[:i] + first[i+1:],
last + [first[i]],
perms)
Close iterative analog:
def permutations(A):
P = []
permutationsI(A, P)
print(P)
def permutationsI(A, perms):
stack = [(A, [])]
while len(stack):
first, last = stack.pop()
if len(first):
for i in range(len(first)):
stack.append((first[:i] + first[i+1:],last + [first[i]]))
else:
perms.append(last)
permutations([1,2,3])
>>[[3, 2, 1], [3, 1, 2], [2, 3, 1], [2, 1, 3], [1, 3, 2], [1, 2, 3]]
A fully recursive function should be:
def permutations_comp_recursive(first, last, perms, i):
if len(first) == 0:
perms.append(last)
elif i == len(first):
pass
else:
permutations_comp_recursive(first, last, perms, i+1)
if first:
permutations_comp_recursive(
first[:i]+first[i+1:],
last + [first[i]],
perms, 0)
For good performance i recommand numpy solutions.
Edit 1: Now the following should be tail-recursive, with the use of list comprehensions. This uses a workarount for tail-recursion in python (and the last 2 arguments were omitted - the result is passed as return value):
import itertools as it
class Recurse(Exception):
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
def recurse(*args, **kwargs):
raise Recurse(*args, **kwargs)
def tail_recursive(f):
def decorated(*args, **kwargs):
while True:
try:
return f(*args, **kwargs)
except Recurse as r:
args = r.args
kwargs = r.kwargs
continue
return decorated
#tail_recursive
def permutations_tail_recursive(first, last, direct=False):
if len(first) == 0 or not all(first):
return last
else:
l = len(first[0]) if direct else len(first)
if last:
return recurse([fi[:i]+fi[i+1:] for fi, i in it.product(first, range(l))],
[last[j] + first[j][i] for j, i in it.product(range(len(last)), range(l))], True)
else:
return recurse([first[:i]+first[i+1:] for i in range(l)],
[first[i] for i in range(l)], True)
This is not optimised and uses loops. Im not sure if this and the code without loop above can be combined - might look into it again.
itertools.permutations can be used for this application.

N Puzzle in Python

I'm trying to build a solution to the N-Puzzle problem using breadth first search in Python.
My solution is adept at finding an answer if all of the numbers bar the zero are in order. e.g.
initial_state = [1,2,3,4,0,5,6,7,8]
or
initial_state = [1,2,3,4,5,6,7,0,8]
but fails with
initial_state = [1,2,5,3,4,0,6,7,8]
Pleases find below my implementation. If someone could point out the flaw in my logic it'd much appreciated.
Thanks in advance!
def right(state):
items = list(state)
i = items.index(0)
n = int(math.sqrt(len(items)))
if (i+1) % n != 0:
del items[i]
items.insert(i+1, 0)
return tuple(items)
else:
pass
def left(state):
items = list(state)
i = items.index(0)
n = int(math.sqrt(len(items)))
if i % n != 0:
del items[i]
items.insert(i-1, 0)
return tuple(items)
else:
pass
def up(state):
items = list(state)
i = items.index(0)
n = int(math.sqrt(len(items)))
if n**2 < i <= (n**2 - n):
del items[i]
items.insert(i+n, 0)
return tuple(items)
else:
pass
def down(state):
items = list(state)
i = items.index(0)
n = int(math.sqrt(len(items)))
if i > n:
del items[i]
items.insert(i-n, 0)
return tuple(items)
else:
pass
class Problem(object):
def __init__(self, initial, goal=None):
self.initial = initial
self.goal = goal
self.n = len(initial)
self.size = int(math.sqrt(self.n))
self.blank = self.initial.index(0)
self.top_row = [i for i in range(self.n) if i < self.size]
self.bottom_row = [i for i in range(self.n) if self.n - (self.size) <= i < self.n]
self.left_column = [i for i in range(self.n) if i % self.size == 0]
self.right_column = [i for i in range(self.n) if (i + 1) % self.size == 0]
def actions(self):
result_list = ["UP","DOWN","LEFT","RIGHT"]
return result_list
def result(self, state, action):
if action == "RIGHT":
return right(state)
if action == "LEFT":
return left(state)
if action == "UP":
return up(state)
if action == "DOWN":
return down(state)
def goal_test(self, state):
return state == self.goal
def path_cost(self, c):
return c + 1
class Node:
def __init__(self, state, parent=None, action=None, path_cost=0):
self.state = state
self.parent = parent
self.action = action
self.path_cost = path_cost
self.depth = 0
if parent:
self.depth = parent.depth + 1
def __repr__(self):
return "<Node %s>" % (self.state,)
def __lt__(self, node):
return self.state < node.state
def expand(self, problem):
return [self.child_node(problem, action)
for action in problem.actions() if self.child_node(problem,action) is not None]
def child_node(self, problem, action):
next = problem.result(self.state, action)
if next:
return Node(next, self, action,
problem.path_cost(self.path_cost))
else:
pass
def solution(self):
return [node.action for node in self.path()[1:]]
def path(self):
node, path_back = self, []
while node:
path_back.append(node)
node = node.parent
return list(reversed(path_back))
def __eq__(self, other):
return isinstance(other, Node) and self.state == other.state
def __hash__(self):
return hash(self.state)
def bfs(problem):
node = Node(problem.initial)
frontier = deque([node])
explored = set()
while frontier:
node = frontier.pop()
explored.add(node.state)
if problem.goal_test(node.state):
return node
for child in node.expand(problem):
if child.state not in explored and child not in frontier:
frontier.append(child)
return [child for child in explored]
p = Problem((1,2,5,3,4,0,6,7,8), (0,1,2,3,4,5,6,7,8))
bfs(p)
#returns
"""[(1, 2, 5, 3, 4, 0, 6, 7, 8),
(1, 2, 0, 5, 3, 4, 6, 7, 8),
(0, 1, 2, 5, 3, 4, 6, 7, 8),
(1, 2, 5, 3, 0, 4, 6, 7, 8),
(1, 2, 5, 0, 3, 4, 6, 7, 8),
(1, 0, 2, 5, 3, 4, 6, 7, 8)]"""
If you process the neighbors (children) of a node (state) by moving the space in UP, DOWN, LEFT, RIGHT order, the solution of an 8-puzzle with bfs starting with the initial state 1,2,5,3,4,0,6,7,8 will be like the following (you can check out where it's differing with your solution):
path_to_goal: ['Up', 'Left', 'Left']
cost_of_path: 3
You may want to refer to this https://sandipanweb.wordpress.com/2017/03/16/using-uninformed-informed-search-algorithms-to-solve-8-puzzle-n-puzzle/?frame-nonce=9e97a821bc for more details.
This condition in up is never true: if n**2 < i <= (n**2 - n).
And this condition in down is off by one: if i > n.
Whether the rest of your code is correct or not is unclear, but you need to debug the fundamentals of your board representation and manipulation code first.
In your space-moving code, I personally would turn your index into an x and y coordinate:
x, y = i % n, i // n
Then you can test more naturally: x>0 for left, x<n-1 for right, y<n-1 for up and y>0 for down.

list with infinite elments

I need to operate on two separate infinite list of numbers, but could not find a way to generate, store and operate on it in python.
Can any one please suggest me a way to handle infinite Arithmetic Progession or any series and how to operate on them considering the fact the minimal use of memory and time.
Thanks every one for their suggestions in advance.
You are looking for a python generator instead:
def infinitenumbers():
count = 0
while True:
yield count
count += 1
The itertools package comes with a pre-built count generator.
>>> import itertools
>>> c = itertools.count()
>>> next(c)
0
>>> next(c)
1
>>> for i in itertools.islice(c, 5):
... print i
...
2
3
4
5
6
This is where the iterator comes in. You can't have an infinite list of numbers, but you can have an infinite iterator.
import itertools
arithmetic_progression = itertools.count(start,step) #from the python docs
The docs for Python2 can be found here
I have another python3 solution (read SICP chapter 3.5)
class Stream:
def __init__(self, head, tail):
self.head = head
self.tail = tail
self.memory = None
self.isDone = False
def car(self):
return self.head
def cdr(self):
if self.isDone:
return self.memory
self.memory = self.tail()
self.isDone = True
return self.memory
def __getitem__(self, pullFrom):
if pullFrom < 1 or self.memory == []:
return []
return [self.car()] + self.cdr()[pullFrom - 1]
def __repr__(self):
return "[" + repr(self.car()) + " x " + repr(self.tail) + "]"
def map(self, func):
if self.memory == []:
return []
return Stream(func(self.car()), lambda: Stream.map(self.cdr(), func))
def from_list(lst):
if lst == []:
return []
return Stream(lst[0], lambda:
Stream.from_list(lst[1:]))
def filter(self, pred):
if self.memory == []:
return []
elif pred(self.car()):
return Stream(self.car(), lambda: Stream.filter(self.cdr(), pred))
else:
return self.cdr().filter(pred)
def sieve(self):
return Stream(self.car(), lambda: self.cdr().filter(lambda n: n % self.car() > 0).sieve())
def foreach(self, action, pull = None):
if pull is None:
action(self.car())
self.cdr().foreach(action, pull)
elif pull <= 0:
return
else:
action(self.car())
self.cdr().foreach(action, pull-1)and run:
a = Stream(0, lambda: a.map((lambda x: x + 1)))
print(a[10])
which returns:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] .
But streams are lazily evaluated, so:
>>> a = Stream(0, lambda: a.map((lambda x: x + 1)))
>>> print(a)
prints:
[0 x [...]]
To create an object that acts like a "mutable" infinite list, you can overload the __getitem__ and __setitem__ methods in a class:
class infinite_list():
def __init__(self, func):
self.func = func
self.assigned_items = {}
def __getitem__(self, key):
if key in self.assigned_items:
return self.assigned_items[key]
else:
return self.func(key)
def __setitem__(self, key , value):
self.assigned_items[key] = value
Then, you can initialize the "infinite list" with a lambda expression and modify an item in the list:
infinite_thing = infinite_list(lambda a: a*2)
print(infinite_thing[1]) #prints "2"
infinite_thing[1] = infinite_thing[2]
print(infinite_thing[1]) #prints "4"
Similarly, it is possible to create an "infinite dictionary" that provides a default value for each missing key.
Perhaps the natural way to generate an infinite series is using a generator:
def arith(a, d):
while True:
yield a
a += d
This can be used like so:
print list(itertools.islice(arith(10, 2), 100))
My solution is:
from hofs import *
def cons_stream(head,tail):
return [head,tail,False,False]
def stream_cdr(strm):
if strm[2]:
return strm[3]
strm[3] = strm[1]()
strm[2] = True
return strm[3]
def show_stream(stream, num = 10):
if empty(stream):
return []
if num == 0:
return []
return adjoin(stream[0], show_stream(stream_cdr(stream), num - 1))
def add_streams(a , b):
if empty(a):
return b
if empty(b):
return a
return cons_stream(a[0] + b[0] , lambda : add_streams( stream_cdr(a), stream_cdr(b)))
def stream_filter( pred , stream ):
if empty(stream):
return []
if pred(stream[0]):
return cons_stream(stream[0], lambda : stream_filter(pred, stream_cdr(stream)))
else:
return stream_filter( pred , stream_cdr( stream ))
def sieve(stream):
return cons_stream(stream[0] , lambda : sieve(stream_filter(lambda x : x % stream[0] > 0 , stream_cdr(stream))))
ones = cons_stream(1, lambda : ones)
integers = cons_stream(1, lambda : add_streams(ones, integers))
primes = sieve(stream_cdr(integers))
print(show_stream(primes))
Copy the Python code above.
When I tried it, i got [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] which is 10 of an infinite list of primes.
You need hofs.py to be
def empty(data):
return data == []
def adjoin(value,data):
result = [value]
result.extend(data)
return result
def map(func, data):
if empty(data):
return []
else:
return adjoin(func(data[0]), map(func, data[1:]))
def keep(pred, data):
if empty(data):
return []
elif pred(data[0]):
return adjoin( data[0] , keep(pred, data[1:]))
else:
return keep(pred, data[1:])
I assume you want a list of infinite numbers within a range. I have a similar problem, and here is my solution:
c = 0
step = 0.0001 # the difference between the numbers
limit = 100 # The upper limit
myInfList = []
while c <= limit:
myInfList.append(c)
c = c + step
print(myInfList)

Making a python iterator go backwards?

Is there anyway to make a python list iterator to go backwards?
Basically i have this
class IterTest(object):
def __init__(self, data):
self.data = data
self.__iter = None
def all(self):
self.__iter = iter(self.data)
for each in self.__iter:
mtd = getattr(self, type(each).__name__)
mtd(each)
def str(self, item):
print item
next = self.__iter.next()
while isinstance(next, int):
print next
next = self.__iter.next()
def int(self, item):
print "Crap i skipped C"
if __name__ == '__main__':
test = IterTest(['a', 1, 2,3,'c', 17])
test.all()
Running this code results in the output:
a
1
2
3
Crap i skipped C
I know why it gives me the output, however is there a way i can step backwards in the str() method, by one step?
EDIT
Okay maybe to make this more clear. I don't want to do a full reverse, basically what i want to know if there is an easy way to do the equivalent of a bidirectional iterator in python?
No, in general you cannot make a Python iterator go backwards. However, if you only want to step back once, you can try something like this:
def str(self, item):
print item
prev, current = None, self.__iter.next()
while isinstance(current, int):
print current
prev, current = current, self.__iter.next()
You can then access the previous element any time in prev.
If you really need a bidirectional iterator, you can implement one yourself, but it's likely to introduce even more overhead than the solution above:
class bidirectional_iterator(object):
def __init__(self, collection):
self.collection = collection
self.index = 0
def next(self):
try:
result = self.collection[self.index]
self.index += 1
except IndexError:
raise StopIteration
return result
def prev(self):
self.index -= 1
if self.index < 0:
raise StopIteration
return self.collection[self.index]
def __iter__(self):
return self
Am I missing something or couldn't you use the technique described in the Iterator section in the Python tutorial?
>>> class reverse_iterator:
... def __init__(self, collection):
... self.data = collection
... self.index = len(self.data)
... def __iter__(self):
... return self
... def next(self):
... if self.index == 0:
... raise StopIteration
... self.index = self.index - 1
... return self.data[self.index]
...
>>> for each in reverse_iterator(['a', 1, 2, 3, 'c', 17]):
... print each
...
17
c
3
2
1
a
I know that this doesn't walk the iterator backwards, but I'm pretty sure that there is no way to do that in general. Instead, write an iterator that walks a discrete collection in reverse order.
Edit you can also use the reversed() function to get a reversed iterator for any collection so that you don't have to write your own:
>>> it = reversed(['a', 1, 2, 3, 'c', 17])
>>> type(it)
<type 'listreverseiterator'>
>>> for each in it:
... print each
...
17
c
3
2
1
a
An iterator is by definition an object with the next() method -- no mention of prev() whatsoever. Thus, you either have to cache your results so you can revisit them or reimplement your iterator so it returns results in the sequence you want them to be.
Based on your question, it sounds like you want something like this:
class buffered:
def __init__(self,it):
self.it = iter(it)
self.buf = []
def __iter__(self): return self
def __next__(self):
if self.buf:
return self.buf.pop()
return next(self.it)
def push(self,item): self.buf.append(item)
if __name__=="__main__":
b = buffered([0,1,2,3,4,5,6,7])
print(next(b)) # 0
print(next(b)) # 1
b.push(42)
print(next(b)) # 42
print(next(b)) # 2
You can enable an iterator to move backwards by following code.
class EnableBackwardIterator:
def __init__(self, iterator):
self.iterator = iterator
self.history = [None, ]
self.i = 0
def next(self):
self.i += 1
if self.i < len(self.history):
return self.history[self.i]
else:
elem = next(self.iterator)
self.history.append(elem)
return elem
def prev(self):
self.i -= 1
if self.i == 0:
raise StopIteration
else:
return self.history[self.i]
Usage:
>>> prev = lambda obj: obj.prev() # A syntactic sugar.
>>>
>>> a = EnableBackwardIterator(iter([1,2,3,4,5,6]))
>>>
>>> next(a)
1
>>> next(a)
2
>>> a.next() # The same as `next(a)`.
3
>>> prev(a)
2
>>> a.prev() # The same as `prev(a)`.
1
>>> next(a)
2
>>> next(a)
3
>>> next(a)
4
>>> next(a)
5
>>> next(a)
6
>>> prev(a)
5
>>> prev(a)
4
>>> next(a)
5
>>> next(a)
6
>>> next(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
You can wrap your iterator in an iterator helper to enable it to go backward. It will store the iterated values in a collection and reuse them when going backwards.
class MemoryIterator:
def __init__(self, iterator : Iterator):
self._iterator : Iterator = iterator
self._array = []
self._isComplete = False
self._pointer = 0
def __next__(self):
if self._isComplete or self._pointer < len(self._array):
if self._isComplete and self._pointer >= len(self._array):
raise StopIteration
value = self._array[self._pointer]
self._pointer = self._pointer + 1
return value
try:
value = next(self._iterator)
self._pointer = self._pointer + 1
self._array.append(value)
return value
except StopIteration:
self._isComplete = True
def prev(self):
if self._pointer - 2 < 0:
raise StopIteration
self._pointer = self._pointer - 1
return self._array[self._pointer - 1]
The usage can be similar to this one:
my_iter = iter(my_iterable_source)
memory_iterator = MemoryIterator(my_iter)
try:
if forward:
print(next(memory_iterator))
else:
print(memory_iterator.prev())
except StopIteration:
pass
I came here looking for a bi-directional iterator. Not sure if this is what the OP was looking for but it is one way to make a bi-directional iterator—by giving it an attribute to indicate which direction to go in next:
class BidirectionalCounter:
"""An iterator that can count in two directions (up
and down).
"""
def __init__(self, start):
self.forward = True
# Code to initialize the sequence
self.x = start
def __iter__(self):
return self
def __next__(self):
if self.forward:
return self.next()
else:
return self.prev()
def reverse(self):
self.forward = not self.forward
def next(self):
"""Compute and return next value in sequence.
"""
# Code to go forward
self.x += 1
return self.x
def prev(self):
"""Compute and return previous value in sequence.
"""
# Code to go backward
self.x -= 1
return self.x
Demo:
my_counter = BidirectionalCounter(10)
print(next(my_counter))
print(next(my_counter))
my_counter.reverse()
print(next(my_counter))
print(next(my_counter))
Output:
11
12
11
10
i think thi will help you to solve your problem
class TestIterator():
def __init__(self):`
self.data = ["MyData", "is", "here","done"]
self.index = -1
#self.index=len(self.data)-1
def __iter__(self):
return self
def next(self):
self.index += 1
if self.index >= len(self.data):
raise StopIteration
return self.data[self.index]
def __reversed__(self):
self.index = -1
if self.index >= len(self.data):
raise StopIteration
return self.data[self.index]
r = TestIterator()
itr=iter(r)
print (next(itr))
print (reversed(itr))
ls = [' a', 5, ' d', 7, 'bc',9, ' c', 17, '43', 55, 'ab',22, 'ac']
direct = -1
l = ls[::direct]
for el in l:
print el
Where direct is -1 for reverse or 1 for ordinary.
Python you can use a list and indexing to simulate an iterator:
a = [1,2,3]
current = 1
def get_next(a):
current = a[a.index(current)+1%len(a)]
return current
def get_last(a):
current = a[a.index(current)-1]
return current # a[-1] >>> 3 (negative safe)
if your list contains duplicates then you would have to track your index separately:
a =[1,2,3]
index = 0
def get_next(a):
index = index+1 % len(a)
current = a[index]
return current
def get_last(a):
index = index-1 % len(a)
current = a[index-1]
return current # a[-1] >>> 3 (negative safe)
An iterator that visits the elements of a list in reverse order:
class ReverseIterator:
def __init__(self,ls):
self.ls=ls
self.index=len(ls)-1
def __iter__(self):
return self
def __next__(self):
if self.index<0:
raise StopIteration
result = self.ls[self.index]
self.index -= 1
return result
I edited the python code from dilshad (thank you) and used the following Python 3 based code to step between list item's back and forth or let say bidirectional:
# bidirectional class
class bidirectional_iterator:
def __init__(self):
self.data = ["MyData", "is", "here", "done"]
self.index = -1
def __iter__(self):
return self
def __next__(self):
self.index += 1
if self.index >= len(self.data):
raise StopIteration
return self.data[self.index]
def __reversed__(self):
self.index -= 1
if self.index == -1:
raise StopIteration
return self.data[self.index]
Example:
>>> r = bidirectional_iterator()
>>> itr=iter(r)
>>> print (next(itr))
MyData
>>> print (next(itr))
is
>>> print (next(itr))
here
>>> print (reversed(itr))
is
>>> print (reversed(itr))
MyData
>>> print (next(itr))
is
This is a common situation when we need to make an iterator go back one step. Because we should get the item and then check if we should break the loop. When breaking the loop, the last item may be requied in later usage.
Except of implementing an iteration class, here is a handy way make use of builtin itertools.chain :
from itertools import chain
>>> iterator = iter(range(10))
>>> for i in iterator:
... if i <= 5:
... print(i)
... else:
... iterator = chain([i], iterator) # push last value back
... break
...
0
1
2
3
4
5
>>> for i in iterator:
... print(i)
...
6
7
8
9
please see this function made by Morten Piibeleht. It yields a (previous, current, next) tuple for every element of an iterable.
https://gist.github.com/mortenpi/9604377

Categories