Using return instead of yield - python

Is return better than yield? From what ive read it can be. In this case I am having trouble getting iteration from the if statement. Basically what the program does is take two points, a begin and end. If the two points are at least ten miles apart, it takes a random sample. The final if statement shown works for the first 20 miles from the begin point, begMi. nCounter.length = 10 and is a class member. So the question is, how can I adapt the code to where a return statement would work instead of a yield? Or is a yield statement fine in this instance?
def yielderOut(self):
import math
import random as r
for col in self.fileData:
corridor = str(col['CORRIDOR_CODE'])
begMi = float(col['BEGIN_MI'])
endMi = float(col['END_MI'])
roughDiff = abs(begMi - endMi)
# if the plain distance between two points is greater than length = 10
if roughDiff > nCounter.length:
diff = ((int(math.ceil(roughDiff/10.0))*10)-10)
if diff > 0 and (diff % 2 == 0 or diff % 3 == 0 or diff % 5 == 0)\
and ((diff % roughDiff) >= diff):
if (nCounter.length+begMi) < endMi:
vars1 = round(r.uniform(begMi,\
(begMi+nCounter.length)),nCounter.rounder)
yield corridor,begMi,endMi,'Output 1',vars1
if ((2*nCounter.length)+begMi) < endMi:
vars2 = round(r.uniform((begMi+nCounter.length),\
(begMi+ (nCounter.length*2))),nCounter.rounder)
yield corridor,begMi,endMi,'Output 2',vars1,vars2
So roughdiff equals the difference between two points and is rounded down to the nearest ten. Ten is then subtracted so the sample is taken from a full ten mile section; and that becomes diff. So lets say a roughDiff of 24 is rounded to 20, 20 - 10, diff + begin point = sample is taken from between mi 60 and 70 instead of between 70 and 80.
The program works, but I think it would be better if I used return instead of yield. Not a programmer.

return is not better, it's different. return says "I am done. Here is the result". yield says "here is the next value in a series of values"
Use the one that best expresses your intent.

Using yield makes your function a generator function, which means it will produce a series of values each time its (automatically created) next() method is called.
This is useful when you want to process things iteratively because it means you don't have to save all the results in a container and then process them. In addition, any preliminary work that is required before values can generated only has to be done once, because the generator created will resume execution of your code following the that last yield encountered — i.e. it effectively turns it into what is called a coroutine.
Generator functions quit when they return a value rather than yield one. This usually happens when execution "falls off the end" when it will return None by default.
From the looks of your code, I'd say using yield would be advantageous, especially if you can process the results incrementally. The alternative would be to have it store all the values in a container like a list and return that when it was finished.

I use yield in situations where I want to continue iteration on some object. However, if I wanted to make that function recursive, I'd use return.

Related

How to run 2 functions simultaneously , and make them stop when one of the them returns something

example:
random_list = ['foo','foo','foo','foo',56,'foo','foo','foo','foo','foo']
# in random_list there are always 10 elements, one of which is 56, but its position is unknown
def find_index_of_56_in_list_1():
for index in range(0,5):
if 56 == random_list[index]:
return index
def find_index_of_56_in_list_2():
for index in range(6,10):
if 56 == random_list[index]:
return index
I would like to split the computational power of iterating in the list in 2 Thread one for the first half of the list 'find_index_of_56_in_list_1' and the other for the second half of the list 'find_index_of_56_in_list_2'.
and if one process has terminated then the other must terminate as well and nothing or return 'none'.
The problem I am trying to solve is not this one but a bigger one, this example is an analogy. I've tried with threading library, but i can't get one process to terminate when the other has terminated or vice versa.
Can try using thread, I like to use python-worker (link)
from worker import worker
random_list = ['foo','foo','foo','foo',56,'foo','foo','foo','foo','foo']
# in random_list there are always 10 elements, one of which is 56, but its position is unknown
#worker
def find_index_of_56_in_list_1():
for index in range(0,5):
if 56 == random_list[index]:
return index
#worker
def find_index_of_56_in_list_2():
for index in range(6,10):
if 56 == random_list[index]:
return index
def main():
worker1 = find_index_of_56_in_list_1()
worker2 = find_index_of_56_in_list_2()
result = None
while worker1.is_alive and worker2.is_alive:
if worker1.finished:
print("worker 1 finished first")
worker2.abort()
result = worker1.ret
if worker2.finished:
print("worker 2 finished first")
worker1.abort()
result = worker2.ret
return result
I feel like you could find some answers in recursion. The question is: do you want to?
Jokes aside, most search algorithms use some form of recursion, and they perform really well. Your example makes me think of binary search -> you take your list, go to the middle point. If it's not the value you're searching, you go to the middle value of the left list or the right list. Keep doing this until you find the value, then return it

Issues with recursive functions when function is called the second time around

I'm trying to create a function that is similar to a shopping list where the function asks the users for 2 inputs, the first one being of the item and the second one being the number of said item the user wants, the only caveat being that the total number of items needs to be exactly 10.
def manualInput():
items = []
noitems = []
x = 1
while sum(noitems) != 10 and sum(noitems) < 10:
try:
i = str(input(f'Item {x}: '))
noi = int(input(f'No. of {i}: '))
items.append(i)
noitems.append(noi)
x = x+1
except ValueError:
print('Check inputs and try again')
items.clear()
noitems.clear()
manualInput()
if sum(noitems) > 10:
print('Number of items need to be exactly 10. Try again')
items.clear()
noitems.clear()
manualInput()
return items, noitems
Now this function works perfectly fine if the user does it perfectly the first time around. However if a ValueError is thrown or the sum of the number of items is greater than 10, the lists always return as empty lists despite the function asking the users for their inputs again.
For example if the user wants to type in Oranges and Apples and wants 6 and 4 of each respectively and manages to input all the information correctly, the first time around, the function returns (['Oranges', 'Apples'], [6, 4]) which is what I want.
In the event of an error however, lets say the user accidently inputs Oranges Apples 6 5 , the function does print out Number of items need to be exactly 10. Try again but when the user inputs all the information again, correctly this time, the function does stop but returns ([], []).
I've tried taking out the item.clear() and noitems.clear() commands but this just results in the function returning (['Oranges', 'Apples'], [6, 5])
So to sum it up I don't know why my lists aren't being appended the second time around when I call the function again in the event of an error. It was my understanding that when u call a function within a function it sort of acts like a loop where the function is run again from the top.
It seems you have not understood recursion and the advantage of it.
Your function creates everytime it gets called a new empty items and noitems list. That's why it is impossible to "remember" the last state.
You have to pass them as function arguments - so that the different rounds of calls can share the state of these two lists.
Second, the while loop is an imperative programming construct - recursion replaces the loop - or should replace it. So you have to get rid of it.
Third, naming in programming is one of the most critical and hardest thing.
noitems tells a reader: "there are no items" - but what you mean are 'Number of items" - call it num_items avoid misinterpretable names.
Fourth, programming is communication. Communication requires that you follow conventions of the peer group with/to which you communicate. Writing Python code means you address your code - besides to the machines especially - to the community of people writing Python code (and who have to maintain the machines and your code). - Be it your successor at work, your colleagues, or your instructor in the course. This means: Always follow the Python code conventions and guidelines (style guide https://peps.python.org/pep-0008/ - in other languages - usually the google style guide for that language).
The style guide says: You don't name in python in camelCase, but in snake_case.https://peps.python.org/pep-0008/
So call it manual_input() - not manualInput() (camelCase is JavaScript and Java convention).
def manual_nput(items = [], num_items = []):
if sum(num_items) == 10: # this is the recursion exit condition
return items, num_items # culminating in returning the result
elif sum(num_items) < 10: # these are the different cases
item = str(input(f'Item {len(items)}: '))
num_item = int(input(f'No. of {item}: '))
return manual_input(items + [item], num_items + [num_item])
# they usually end in a recursive call of the function
# with updated arguments
else: # the number of items is > 10
# in this case remove the last item and call the manual_input again
print('Number of items need to be exactly 10. Try again')
return manual_input(items[:-1], num_items[:-1])
# this [:-1] removes the last input and puts the state of
# items and num_items back to the beginning of the previous call

Nothing is output in python recursive function

List item
I'm working on a code that calculates the 'distance' between two configurations of a Flip Cube, The distance between two configurations x and y is the minimum number of steps required to go from x to y, or conversely.
To make that I've created a simpler version that makes something different, this code takes two integer numbers ci and cf. with ci returns an iterable called main_level through the generator called multi, then, it iterates through it searching for the parameter cf, whenever cf is not in main_level the variable steps is increased by 1 and for each element in main_level we repeat the same process done for ci. Finally, when cii==cf the program ends and returns the steps variable, which counts the number of "levels" that we have to go down to find the given parameter cf. This code doesn't have any practical purpose is just a base for the problem I mentioned above.
If I call the distance(ci, cf) function with ci=5, the first two levels are:
{0,3,6,9,12} <-- first level (steps is initialized with 1)
if cf is any of the numbers in the set, the program should end and return steps=1,
if cf is not in that set, the programs form the second level:
{15,18,21,24,27,30,33} and search cf, if cf is there, the program ends and should return steps=2, if not, it forms the third level, and so on. But there is a problem, actually, when I call the distance function with ci=5 and cf= any natural number, and print its value, anything is output, only for cf=0, it outputs step=1. I don't really know what's going on. I would appreciate your help.
Here is the code:
#Base solution to FlipCube problem
def multi(par):
for i in range(par):
yield i*3
steps=1
def distance(ci,cf):
main_level =set(multi(ci))
global steps
def check_main_level(cf):
global steps
nonlocal main_level
def lower_level(config_list):
sett=set()
for i in config_list:
sett.update(q for q in multi(i) if q not in config_list)
nonlocal main_level
main_level=sett
check_main_level(cf)
for i in main_level:
if i==cf:
break
else:
steps+=1
lower_level(main_level)
check_main_level(cf)
return steps
#testing
e= distance(5,0)
print(e)# prints 1, very good
e2= distance(5,9)
print(e2)# should print 1, but doesn't print anything :(
e3= distance(5,27)
print(e3)# should print 2, but doesn't print anything :(
The program does not terminate recursion under all circumstances. The culprit seems to be the for loop in check_main_level. Change the code after your definition of lower_level to:
# code portion of check_main_level
if cf > max(main_level):
steps+=1
lower_level(main_level)
# end of code portion check_main_level (replacing for-loop)
you have an infinity loop, that's why nothing is printed.
You can see it easyly by adding a print :
for i in config_list:
print(i)
sett=set()
sett.update(q for q in list(multi(i)) if q not in config_list)

Algorithm to return all possible paths in this program to a nested list

So I have a game with a function findViableMoves(base). If i call this function at the start with the parameter base, I get an output [move1, move2 ... moven] which denotes all of the n viable moves that the user can perform give the state base. (there are in fact 2 root moves)
Upon performing a move, say move2 - base gets changed depending on the move, the function gets called again and now we have an output for findViableMoves(base) of [move21,move22 .... move2n].
Depth-first-tree
If you look at this diagram, it's a very similar situation - there's no looping back, it's just a normal tree. I need a program that performs a depth-first search (i think?) on all the possible moves given a starting state of base, and then returns then in a list as such:
[[move1,move11,move111],[move1,move11,move112],....[moven,moven1,moven11],...]
There will be more elements in these lists (14 at most), but I was just wondering if someone could provide any hints over how I can build an algorithm to do this? Efficiency doesn't really matter to me as there isn't too many paths, I just want it done for now.
I'm not 100% clear on what you're after, but if you have a list or similar iterable that is changing while the loop is happening you could try something like the below.
This example allows the list and the loop condition to both remain dynamic during the loop execution.
import random
import sys
import time
changing_list = ['A', 27, 0.12]
def perform_operation(changing_list, counter):
sometimes_add_another_element_threshold = 0.6
if random.random() > sometimes_add_another_element_threshold:
changing_list.append(random.random())
print(changing_list[counter])
def main(z=0):
safety_limit = 100
counter = 0
condition = True
while condition and counter < safety_limit:
perform_operation(changing_list, counter)
counter += 1
condition = counter<len(changing_list)
print("loop finished")
if __name__ == '__main__':
start_time = time.time()
main(int(sys.argv[1])) if len(sys.argv)>1 else main()
print(time.time() - start_time)
which provides output of varying length that looks something like:
A
27
0.12
0.21045788812161237
0.20230442292518247
loop finished
0.0058634281158447266

Curious about effect of recursion of a method

I have written an instance method which uses recursion to find a certain solution. It works perfectly fine except the time when I'm exiting the if-elif block. I call the function itself inside IF block. Also, I have only one return statement. The output from the method is weird for me to understand. Here is the code and the output:
def create_schedule(self):
"""
Creates the day scedule for the crew based on the crew_dict passed.
"""
sched_output = ScheduleOutput()
assigned_assignements = []
for i in self.crew_list:
assigned_assignements.extend(i.list_of_patients)
rest_of_items = []
for item in self.job.list_of_patients:
if item not in assigned_assignements:
rest_of_items.append(item)
print("Rest of the items are:", len(rest_of_items))
if len(rest_of_items) != 0:
assignment = sorted(rest_of_items, key=lambda x: x.window_open)[0]
# print("\nNext assignment to be taken ", assignment)
output = self.next_task_eligibility(assignment, self.crew_list)
if len(output) != 0:
output_sorted = sorted(output, key=itemgetter(2))
crew_to_assign = output_sorted[0][1]
assignment.eta = output_sorted[0][4]
assignment.etd = int(assignment.eta) + int(assignment.care_duration)
crew = next((x for x in self.crew_list if x.crew_number == crew_to_assign), None)
self.crew_list.remove(crew)
crew.list_of_patients.append(assignment)
crew.time_spent = assignment.etd
self.crew_list.append(crew)
self.create_schedule()
else:
print("*" * 80, "\n", "*" * 80, "\nWe were not able to assign a task so stopped.\n", "*" * 80, "\n", "*" * 80)
sched_output.crew_output = self.crew_list
sched_output.patients_left = len(rest_of_items)
elif not rest_of_items:
print("Fully solved.")
sched_output.crew_output = self.crew_list
sched_output.patients_left = 0
print("After completely solving coming here.")
return sched_output
This was the output:
Rest of the items are: 10
Rest of the items are: 9
Rest of the items are: 8
Rest of the items are: 7
Rest of the items are: 6
Rest of the items are: 5
Rest of the items are: 4
Rest of the items are: 3
Rest of the items are: 2
Rest of the items are: 1
Rest of the items are: 0
Fully solved.
After completely solving coming here.
After completely solving coming here.
After completely solving coming here.
After completely solving coming here.
After completely solving coming here.
After completely solving coming here.
After completely solving coming here.
After completely solving coming here.
After completely solving coming here.
After completely solving coming here.
After completely solving coming here.
What I don't understand is that as soon as my list rest_of_items is empty, I assign data to sched_output and return it. However, print statement is being executed for the same number of time as recursion was done. How can I avoid this?
My output is perfectly fine. All I want to do is understand the cause of this behaviour and how to avoid it.
The reason it's printing out 11 times is that you always call print at the end of the function, and you're calling the function 11 times. (It's really the same reason you get Rest of the items are: … 11 times, which should be a lot more obvious.)
Often, the best solution is to redesign things so instead of doing "side effects" like print inside the function, you just return a value, and the caller can then do whatever side effects it wants with the result. In that case, it doesn't matter that you're calling print 11 times; the print will only happen once, in the caller.
If that isn't possible, you can change this so that you only print something when you're at the top of the stack. But in many recursive functions, there's no obvious way to figure that out without passing down more information:
def create_schedule(self, depth=0):
# etc.
self.create_schedule(depth+1)
# etc.
if not depth:
print('After completely solving come here.')
returns sched_output
The last resort is to just wrap the recursive function, like this:
def _create_schedule(self):
# etc.
self._create_schedule()
# etc.
# don't call print
return sched_output
def create_schedule(self):
result = self._create_schedule()
print('After completely solving come here.')
return result
That's usually only necessary when you need to do some one-time setup for the recursive process, but here you want to do some one-time post-processing instead, which is basically the same problem, so it can be solved the same way.
(Of course this is really just the first solution in disguise, but it's hidden inside the implementation of create_schedule, so you don't need to change the interface that the callers see.)
As you call your create_schedule function within itself before the function finishes, once it has gotten to the end and doesn't need to call itself again, each function ends, and hits the "After completely solving coming here.", at the end of the function.
This means that each function, after calling itself, is still running - just stuck at the line where it calls itself - until they have all completed, which is when the paused functions can finish their task, printing out your statement.
You have print("After completely solving coming here.") at the end of your recursive function. That line will be executed once for each recursion.
Consider this simple example, which recreates your issue:
def foo(x):
print("x = {x}".format(x=x))
if x > 1:
foo(x-1)
print("Done.")
Now call the function:
>>> foo(5)
x = 5
x = 4
x = 3
x = 2
x = 1
Done.
Done.
Done.
Done.
Done.
As you can see, on the final call to foo(x=0), it will print "Done.". At that point, the function will return to the previous call, which will also print "Done." and so on.

Categories

Resources