How to reset Yield and stop iteration - python

I am facing some problems regarding a generator. I have a list of 1000 elements. I want to read single item one by one and do some operation. The operation is something like comparing with some specific value. If I able to find that value from the list I want to stop iteration and reset the yield again.
I looking for the funtionality how to reset __next__ pointer in generator. Also I have to make 100 object in runtime FN_SOVLS.
class FN_SOV1S:
def __init__(self,elementlist,idxNo):
self._elementlist = elementlist
self._idxNo =idxNo
setup()
process()
def setup(self):
try:
self.df = pd.read_excel(r'D:\OPCUA\Working_VF1.xls', sheet_name='Valve1S')
for tag,col in self.readcmd():
if col==4:
self.cmd = tag
if col == 5:
self.openFB = tag
if col == 6:
self.clsFB = tag
if col == 7:
self.delaytime = tag
except Exception as e:
log_exception(e)
def process(self):
for tagname,tagvalue in self.searchValueBytag():
if tagname == self.cmd:
if tagvalue == 1:
sleep(self.delaytime)
gen.writegeneral.writenodevalue(self.openFB,1)
gen.writegeneral.writenodevalue(self.clsFB,0)
else:
gen.writegeneral.writenodevalue(self.openFB, 0)
gen.writegeneral.writenodevalue(self.clsFB, 1)
def searchValueBytag(self):
n = 0
while n < len(self._elementlist):
tagname, tagvalue = self._elementlist[n]
yield tagname, tagvalue
n =+ 1
The condition is to reset Generator function is:
for tagname,tagvalue in self.searchValueBytag():
if tagname == self.cmd:

You cannot "reset" a running generator.
What you can do is break out of the for-loop that uses the generator.
Later you can then create the generator anew by calling searchValueBytag again.

I don't completely understand your question, but hopefully, this can help. This uses a flag that will continue restarting the generator until the self.cmd value is no longer found.
Obviously, this is not identical to your code and more is needed for it to work perfectly, but you can easily use this flag to reset the generator
def generator():
# arbitrary length
length = 100
n = 0
while n < length:
yield n
n += 1
# create a complete flag that is only true when the end of the iteration is reached
complete = False
# keep trying until complete is true
while not complete:
# restarts the generator by making a new one
g = generator()
# keeps going until 'break'
while True:
# try/catch because next returns error when the end of the generator is reached
# when the end is reached we know that to turn complete to true
try:
# get the next val in the iterator
value = next(g)
# if value is the reset flag, then break out of while loop and restart generator
if value == RESET_FLAG:
break
except:
# StopIteration exeption received, job finished
complete = True
break
def generator():
length = 100
n = 0
while n < length:
yield n
n += 1

Related

Generators function

MAX_STUDENTS = 50
def get_student_ids():
student_id = 1
while student_id <= MAX_STUDENTS:
# Write your code below
n = yield student_id
if n != None:
student_id = n
continue
student_id += 1
student_id_generator = get_student_ids()
for i in student_id_generator:
# Write your code below
if i == 1:
i = student_id_generator.send(25)
print(i)
Im quite confused, when i run the code below, i understand the the send function gives 25 as the yield value and assigns it to n, however when entering the if statement, checking if n is not None, wouldnt this create an infinite loop, since n is not none, would take us to the continue statement, which takes us back to the next iteration of the while loop, completely skipping the incrementing of student id
The first thing one can notice is that n will always be of type none except when you send 25 into the generator.
Let's look at the flow of the program
this is the driver code at the start of the program
student_id_generator = get_student_ids() #creates a generator class
for i in student_id_generator: #interates through the generator class
if i == 1: # note that the first i == 1, so the first time the code hits
#this condition, it will be true
i = student_id_generator.send(25) #sets n to be 25
print(i) #for the first iteration, will return 1, as the first yield is 1
now, since n=25 (you just sent 25 into the generator code), let's look at the generator
n = yield student_id # n=25
if n != None: #since n==25, n is not none
student_id = n #student_id = 25
continue #go back to the while loop
student_id += 1 # will not be run
Now that student_id = 25, for the next iteration in the driver code, it will yield 25, so 25 will be printed. But n will be None as nothing is sent to the generator, so student_id += 1 will be run. From there, the while loop kicks in and it will continue until 'student_id == 50', where the loop breaks.
Will there be an infinite loop? No. Because the condition 'n != None' only occurs once.
I hope this helps. If you are still confused, my suggestion is to take out a pen and paper and work out what happens to the code step by step. I did that to help myself understand it.

looping over a nested list with multiple rows and columns

I wanted to replace the items from listoflist = [[None, None, None]] with the output from
def _execute():
user_input = input("type in: ")
return user_input
without creating a new list
def insertdata(data): is doing that. its start with the first value and continues till the last value
def insertdata(data):
data_added = False
n = len(listoflist[0])
index = 0
while not data_added and index != n:
if listoflist[0][index] is None:
listoflist[0][index] = data
data_added = True
else:
index += 1
if index == n:
print("\n The list is full, No more elements will be added \n")
while True:
insertdata(_execute())
print(listoflist)
I want to extend the function to loop over a nested list with multiple rows.
what condition should the if statment have so it starts at the first row and execute the inner loop `?
def double_loop(data):
data_added_outer = False
index_outer = 0
n_out = len(listoflist)
# outer loop
while not data_added_outer and index_outer < n_out:
if: # what condition ?
#### inner loop ###############
data_added_inner = False
n_inner = len(listoflist[index_outer])
index_inner = 0
while not data_added_inner and index_inner != n_inner:
if listoflist[index_outer][index_inner] is None:
listoflist[index_outer][index_inner] = data
data_added = True
else:
index_inner += 1
if index_inner == n_inner:
print("\n The list is full, No more elements will be added \n")
####### inner ############################################################
else:
index_outer += 1
if index_outer == n_out:
print("\n The list is full, No more elements will be added \n")
Instead of replacing the data, the elegant approach in Python is to create new data, using comprehensions:
def with_nones_replaced(grid, replacement):
return [
[
replacement if value is None else value
for value in row
]
for row in grid
]
As far as I have understood, the job of your function is simply to replace the None objects in a 2D-List with the data parameter passed to the function. I find a for-loop approach much more cleaner, and pythonic in this case.
def changedata(arr, data):
for item in arr:
for x in range(len(item)):
if item[x] is None:
item[x] = data
You can also use the map function to simplify the code:
def changedata(arr, data):
for i in range(len(arr)):
arr[i] = list(map(lambda x: data if x is None else x, arr[i]))
If the data is coming from a function, you can still use this paradigm by calling
_execute() once, storing it in a variable, and then passing it to changedata()
data = _execute()
changedata(listoflist, data)
print(listoflist)

Python Wrong Input when working with Lists

I am working with this problem (https://open.kattis.com/problems/whowantstoliveforever). As the problem states, my program has to determine if the universe lives or dies based on the input 0s and 1s.
to determine the next value of the i-th bit, look at the current value of the bits at positions i−1 and i+1 (if they exist; otherwise assume them to be 0). If you see exactly one 1, then the next value of the i-th bit is 1, otherwise it is 0. All the bits change at once, so the new values in the next state depend only on the values in the previous state. We consider the universe dead if it contains only zeros.
My current solution works on the example input file, however it fails when submitting it to Kattis. (Wrong Answer)
Below is my code.
import sys
def get_bit(bits, i):
if 0 <= i < len(bits):
return int(bits[i])
else:
return 0
def get_new_state(old_state):
new_state = []
for index in range(len(old_state)):
if (get_bit(old_state, index-1) == 0 and get_bit(old_state, index+1) == 0) or (get_bit(old_state, index-1) == 1 and get_bit(old_state, index+1) == 1):
new_state.append(0)
elif(get_bit(old_state, index-1) == 0 and get_bit(old_state, index+1) == 1) or (get_bit(old_state, index-1) == 1 and get_bit(old_state, index+1) == 0):
new_state.append(1)
return new_state
def is_dead(state):
if len(set(state)) == 1:
return True
else:
return False
def foresee_fate(state):
seen = []
while True:
if is_dead(state):
return False
if state in seen:
return True
seen.append(state)
state = get_new_state(state)
def print_result(boolean):
print("LIVES" if boolean else "DIES")
num_cases = int(sys.stdin.readline().strip())
for i in range(num_cases):
cur_state = []
case = sys.stdin.readline().strip()
for char in case:
cur_state.append(char)
print_result(foresee_fate(cur_state))
Please let me know what I can do to improve this program.
As suggested, I have added the following to my code and IT works now. However, I ran into a new problem. now I'm getting "Time Limit Exceeded" > 3.00 s.
def is_dead(state):
if set(state).pop() == 1:
return False
elif len(set(state)) == 1:
return True
else:
return False
Please let me know if there is any suggestion on getting around this problem

Loop to Store Test Results in Python

In short, I need to test some functions by creating 100 random integer lists of each specified length (500, 1000, 10000) and store the results. Eventually, I need to be able to calculate an average execution time for each test but I've yet to get that far with the code.
I assumed the following was the best way to approach this:
Create a dictionary to store the required list length values.
For each value in that dictionary, generate a new list of random integers (list_tests).
Create another dictionary for storing the results of each function test (test_results).
Use a while loop to create 100 lists of each length.
Run the tests by calling each function in the while loop and storing each result in the results dictionary.
The program appears to run but I have a couple problems:
It never reaches the other list_tests values; never proceeds beyond 500.
It's overwriting the values for the test_results dictionary
I don't quite understand where I've gone wrong with the loop in main(). Is my process to test these functions feasible? If so, I'm lost as to how I can fix this looping problem. Thank you in advance for any assistance you can offer!
Here is my program:
import time
import random
def sequential_search(a_list, item):
start = time.time()
pos = 0
found = False
while pos < len(a_list) and not found:
if a_list[pos] == item:
found = True
else:
pos = pos+1
end = time.time()
return found, end-start
def ordered_sequential_search(a_list, item):
start = time.time()
pos = 0
found = False
stop = False
while pos < len(a_list) and not found and not stop:
if a_list[pos] == item:
found == True
else:
if a_list[pos] > item:
stop = True
else:
pos = pos+1
end = time.time()
return found, end-start
def num_gen(value):
myrandom = random.sample(xrange(0, value), value)
return myrandom
def main():
#new_list = num_gen(10000)
#print(sequential_search(new_list, -1))
list_tests = {'t500': 500, 't1000': 1000, 't10000': 10000}
for i in list_tests.values():
new_list = num_gen(i)
count = 0
test_results = {'seq': 0, 'ordseq': 0}
while count < 100:
test_results['seq'] += sequential_search(new_list, -1)[1]
test_results['ordseq'] += ordered_sequential_search(new_list, -1)[1]
count += 1
if __name__ == '__main__':
main()
I think you meant
found = True
Instead of
found == True
On line 47
Also a for loop is much cleaner try this, it should be what your looking for:
def ordered_sequential_search(a_list, item):
start = time.time()
found = False
stop = False
for i in range(len(a_list)):
if a_list[i] == item:
found = True
else:
if a_list[i] > item:
stop = True
if found: break
if stop: break
end = time.time()
return found, end-start
It is overwriting values in the dictionary because you have specified the key to overwrite values. You are not appending to the dictionary which you should have done.
Your while loop might not be breaking, that why your for loop cannot iterate to another value.

Python iterator behaviour

Given an arbitrary input string I'm meant to find the sum of all numbers in that string.
This obviously requires that i know the NEXT element in string while iterating through it...and make the decision whether its an integer. if the previous element was an integer also, the two elements form a new integer, all other characters are ignored and so on.
For instance an input string
ab123r.t5689yhu8
should result in the sum of 123 + 5689 + 8 = 5820.
All this is to be done without using regular expressions.
I have implemented an iterator in python, whose (next()) method i think returns the next element, but passing the input string
acdre2345ty
I'm getting the following output
a
c
d
r
e
2
4
t
y
Some numbers 3 and 5 are missing...why is this? I need that the next() to work for me to be able to sift through an input string and do the calculations correctly
Better still, how should i implement the next method so that it yields the element to the immediate right during a given iteration?
Here is my code
class Inputiterator(object):
'''
a simple iterator to yield all elements from a given
string successively from a given input string
'''
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def next(self):
"""
check whether we've reached the end of the input
string, if not continue returning the current value
"""
if self.index == len(self.data)-1:
raise StopIteration
self.index = self.index + 1
return self.data[self.index]
# Create a method to get the input from the user
# simply return a string
def get_input_as_string():
input=raw_input("Please enter an arbitrary string of numbers")
return input
def sort_by_type():
maininput= Inputiterator(get_input_as_string())
list=[]
s=""
for char in maininput:
if str(char).isalpha():
print ""+ str(char)
elif str(char).isdigit() and str(maininput.next()).isdigit():
print ""+ str(char)
sort_by_type()
Python strings are already iterable, no need to create you own iterator.
What you want is thus simply achieved without iterators:
s = "acdre2345ty2390"
total = 0
num = 0
for c in s:
if c.isdigit():
num = num * 10 + int(c)
else:
total += num
num = 0
total += num
Which results in:
>>> print total
4735
This can be done with itertools.groupby:
from itertools import groupby
s = 'ab123r#t5689yhu8'
tot = 0
for k, g in groupby(s, str.isdigit):
if k:
tot += int(''.join(g))
Or in one line (as suggested in the comments down below):
tot = sum((int(''.join(g)) for k, g in groupby(s, str.isdigit) if k)
Edit: I first deleted this answer as there are much better solutions for your problem in this thread, but as you are directly asking how to use the next method to get your code working I have recovered it, in case you find it useful.
Try this (I mocked the iterator for convenience):
def sort_by_type():
maininput = iter("acdre2345ty")
for char in maininput:
if char.isalpha():
print char
elif char.isdigit():
number = char
while True:
# try/except could take care of the StopIteration exception
# when a digit is last in the string
#
# try:
# char = maininput.next()
# except StopIteration:
# char = ""
#
# however using next(iterator, default) is much better:
#
char = next(maininput, "")
if char.isdigit():
number += char
else:
break
print number
print char
if produces:
a
c
d
r
e
2345
t
y
For entertainment purposes only (I couldn't resist, 49 chars):
eval(''.join([['+0+',x][x.isdigit()]for x in s]))

Categories

Resources