For loop menu in python? - python

Good day everyone, I'm fairly new to python and I had some questions regarding creating menus...
So I recently made a menu using the while loop, and it works like it's supposed to. My question, however, is whether or not it's possible to create a menu using a for loop. I know a for loop is used when you want something to cycle for an x amount of times.
I tried searching for the answer already, but I couldn't really find anything related. If anyone can point me in the right direction, I'd appreciate it.
Here is my menu using "while":
def mainMenu():
print("SELECT A FUNCTION")
print("")
print("e1- Calculate The Area Of A Sphere")
print("e2- Calculate The Volume Of A Cube")
print("e3- Multiplication Table")
print("e4- Converting Negatives To Positives")
print("e5- Average Student Grades")
print("e6- Car Sticker Color")
print("e7- Exit")
print("")
while True:
selection = input("Enter a choice: ")
if(selection == "e1"):
e1()
break
elif(selection == "e2"):
e2()
break
elif(selection == "e3"):
e3()
break
elif(selection == "e4"):
e4()
break
elif(selection == "e5"):
e5()
break
elif(selection == "e6"):
e6()
break
elif(selection == "e7"):
print("Goodbye")
break
else:
print("Invalid choice. Enter 'e1'-'e7'")
print("")
mainMenu()

My guess is that your instructor wanted you to use a generator. Based on your question, I'm going to assume that you are not familiar with generators. If you already know about them, skip to the second section, otherwise it might be worth your time to learn about them first.
Generators
In short, a generator acts like a function but instead of using a return statement it uses a yield statement. Calling next on a generator will run it until it reaches a yield statement and then suspend the process (and save the state of the generator, as opposed to normal functions, which typically don't save state between calls) until next is called on the generator again, at which point the control flow will be resumed where the generator left off.
The following code should give you an idea of how generators work:
# Define a generator function. This function will return our generator iterator.
def test_gen():
numCalls = 0
print("Start. This code is only executed once.")
while numCalls < 10:
print("Entering while loop.")
yield("Yielding. numCalls = %d." %(numCalls))
numCalls += 1
print("Resuming control flow inside generator.")
print("End. This code is only executed once.")
yield("Last yield.")
# End test_gen.
# Test the behavior of our generator function.
def run_test_gen():
# Create generator iterator by calling the generator function.
gen = test_gen()
# Continuously get the next value from the gen generator.
while True:
# If the generator has not been exhausted (run out of values to yield), then print the next value.
try:
print(next(gen))
# Break out of the loop when the generator has been exhausted.
except StopIteration:
print("Generator has been exhausted.")
break
# End run_test_gen.
run_test_gen()
If you want to know more about generators I'd check out the Python docs and the explanation Jeff Knupp has here.
Making a Menu With a Generator and a for Loop
Now that we know about generators, we can use them to create a menu based on a for loop. By placing a while True statement inside of a generator we can create a generator that yields an infinite number of values. Since generators are iterable, we can iterate over the yielded values in a for loop. See the below code for an example.
def infinite_gen():
"""
First, create a generator function whose corresponding generator iterator
will yield values ad infinitum. The actual yielded values are irrelevant, so
we use the default of None.
"""
while True:
yield
# End infinite_gen.
def print_str_fun(string):
"""
This function simply returns another function that will print out the value
of string when called. This is only here to make it easier to create some
mock functions with the same names that you used.
"""
return(lambda : print("Calling function %s." %(string)))
# End print_str.
# Create a tuple of the function names that you used.
fun_name_tuple = ('e%d' %(ii) for ii in range(1, 7))
# Create a dictionary mapping the function names as strings to the functions themselves.
fun_dict = {fun_name: print_str_fun(fun_name) for fun_name in fun_name_tuple}
# Add the e7 function separately because it has distinct behavior.
fun_dict['e7'] = lambda : print("Goodbye.")
def mainMenu():
"""
Example mainMenu function that uses for loop iterating over a generator
instead of a while loop.
"""
# Create our infinite generator.
gen = infinite_gen()
# Iterate over the values yielded by the generator. The code in the for loop
# will be executed once for every value yielded by the generator.
for _ in gen:
# Get function name from user input.
selection = input("Enter a choice: ")
# Try to call the function selected by the user.
try:
fun_dict[selection]()
break
# Print a note to user and then restart mainMenu if they supplied an
# unsupported function name.
except KeyError:
print("Invalid choice. Enter 'e1' - 'e7'.")
print()
mainMenu()
# End mainMenu.
# Allows one to run the script from the command line.
if __name__ == '__main__':
mainMenu()

Related

Is it possible to a "continue" statement inside a function that gets called within a "for loop"?

I have a for loop that I'm needing to break if there is an error.
I want to be able to continue a for loop if a boolean is true. But don't want to have to write an "if" statement over and over again. Is it possible to call "continue" outside of a "loop"?
The following code results in an error. But is my thinking of this would work.
_Range = 6
_RangeEnd = 0
def function_to_call():
print("x")
if _Continue is True:
continue
for x in range(_Range):
_RangeEnd = _RangeEnd + 1
function_to_call()
if _RangeEnd == 5:
_Continue = True
If this isn't possible. What would be an efficient way to do this? I'm reusing this function in a good number of different for loops.
You can't call continue outside of a loop, e.g. from a function called from inside a loop.
One option is to return a meaningful value from your function that tells the caller to continue, i.e. some truthy value or falsy value:
def function_that_decides_to_continue():
print("x")
return your_condition_here
for x in range(_Range):
if function_that_decides_to_continue():
continue # skip extra stuff
# do extra stuff
Or a sentinel object:
CONTINUE = object() # sentinel object, whose sole purpose is to be uniquely identifiable
def function_that_decides_to_continue():
print("x")
if your_condition_here:
return CONTINUE
for x in range(_Range):
if function_that_decides_to_continue() is CONTINUE:
continue
Another option in an error-handling use-case is directly handling those exceptions with try/except:
def function_that_may_fail(divisor):
return 10 / divisor
for x in [2, 5, 0, 1]:
try:
result = function_that_may_fail(x)
except ZeroDivisionError:
continue
do_stuff_with(result)
Though I admit I might be misinterpreting what you actually want to do, because perhaps the most straightforward "want to be able to continue a for loop if a boolean is true" is just a plain while:
while function_that_decides_to_continue():
# do stuff
You'll have to provide actual examples of the "different for loops" you wanted your "function that continues" will be used in.
While I am in doubt this is a good idea for the flow control of the program, something like this can simulate what you need:
_Range = 6
_RangeEnd = 0
class Continue(Exception):
pass
def function_to_call():
print("x")
if _Continue is True:
raise Continue
for x in ...:
try:
function_to_call()
something_else()
except Continue:
continue
And no, continue can't be outside the loop.
The continue needs to be inside a loop, I think the best solution would be to put a (if boolVar == True: continue) inside the for.

Range value changing when indexing a list backwards

I'm creating a chess game inside of Python (pygame) and in my validating moves function, I access a list of all possible moves.
However, as I'm removing from that list, I index it backwards. However, when I implement that change, the amount of repetitions undergone by the for loop encompassing the index changes from 20 to 1.
Here's the full function code:
def valid_move_2():
global white_to_move
possible_moves = generate_possible_moves()
print(range(len(possible_moves)))
for i in range(len(possible_moves)-1, -1, -1):
print("possible moves range")
print(str(possible_moves[i][0]))
move(possible_moves[i][0], possible_moves[i][1], possible_moves[i][2], possible_moves[i][3])
white_to_move = not white_to_move
check_check()
if check_check():
possible_moves.remove(possible_moves[i])
white_to_move = not white_to_move
undo_move()
if len(possible_moves) == 0:
if check_check():
checkmate = True
else:
stalement = True
else:
checkmate = False
stalemate = False
return possible_moves
To be specific:
print(range(len(possible_moves)))
This line returns 20.
However this line:
print("possible moves range")
only returns once, meaning the for loop repeats only once.
Where have I gone wrong?
THE ISSUE
The issue lies with this function, where my program gets stuck:
def square_under_attack(posx, posy):
print("SQUARE UNDER ATTACK FUNCTION CALLED")
global white_to_move
white_to_move = not white_to_move
enemy_moves = generate_possible_moves()
white_to_move = not white_to_move
x=0
for opponent_move in enemy_moves:
print(x)
if opponent_move[3] == posx and opponent_move[4] == posy: # if the opponent can move to the square being tested
print("returned true")
return True
x+=1
print("returned false")
return False
In this function, it gets stuck in the for loop. The x value was for troubleshooting to find out how many times the loop is iterated before not returning anything.
x is printed to have a value of 2 without either of the other 2 print functions being called at all.
What's wrong?
print(range(len(possible_moves)))
This shouldn't return 20.
It should return something like
range(0,20)
If so then it's problem of your for loop not range.
possible_moves.remove(possible_moves[i])
This line in particular is problematic.
If you are trying to remove i th element in possible_moves, use del
del possible_moves[i]
Also there are some dangerous bits in your code, one is global variable. Try to find another way to do that without using a global variable.
Another is your loop is iterated by initial length of possible_moves, and in the loop you are possibly removing something from possible_moves. This may cause index error. Easy fix would be to create a new list to keep track of what is deleted and what is not deleted.
If this doesn't solve error then there must be some issues in called method/functions
Try refactoring it with recursion since I think your iteration is very unorderly and possibly has repeated codes all over your code base.
If possible, use list comprehension instead of iteration to make things more concise.
If you want to do something like "Do until list L is empty" then use following pattern
while L:
do_something()
For your problem, chess, I would design it like this:
First define a function for each chess pieces that returns a set of grids they can go on next turn.
For check and checkmate I would look for a union of all the sets which represents all their possible next moves.
I think you are trying to implement your program by trying out every single enemy moves and then undoing it which is incredibly inefficient approach
You should make all the methods very concise and atomic. You shouldn't make them convoluted and do many implicit things under the hood. Keep them simple and logically concise as much as possible

generators, python, infinite loop

I am trying to produce a list of odd numbers using a generator (just to get a better insight into generators). I wrote the following code but, it doesn't stop running! While I expect the code stops when the condition i>n meets.
Any help is appreciated.
import sys
def odd(n):
i=0
while True:
if i%2==0:
continue
yield i
i+=1
if i>n:
return
# Here we build a generator
g = odd(10)
while True:
try:
print(next(g),end=' ')
except StopIteration:
sys.exit()
When i is even, you don't increment it, so it stays even for every subsequent iteration of the loop and never gets larger than n.
You want to increment i whether or not it is even.
def odd(n):
i=0
while True:
if i%2 != 0: # yield i only if it is odd
yield i
i+=1 # Increment i in either case
if i>n:
return
In my opinion, you have two style issues in your code that make it hard to see the problem:
The use of continue. A simple if statement would make it easier to see which code might not execute and which code will definitely execute. continue is mainly useful when you have nested if statements making things complicated.
You don't utilize the while condition. This assumes that the while loop will have to execute at least once. When writing a loop, you should generally consider what happens if the loop needs to execute 0 times. What if someone passes an argument of -1? What if you change the initial value of i to 1 to save an iteration?
def odd(n):
i = 0
while i <= n:
if i % 2:
yield i
i += 1
# Automatically return and throw StopIteration.

Return inside if condition

def hi (n):
if n<=5:
print("we are inside hi")
n+=1
return n
n=1
hi(n)
1) In the above code i have declared a function hi() which takes an input n
2)I want to iterate inside the if condition until n is less than 5,totally execute the print statement 4 times
3)But it is not working after execution of one time inside the condition
4)I am thinking i have given return statement for if condition but the function is totally being exit
5)(i am thinking i am returning the n value to the if condition and it checks the condition and it will iterate ) if wrong correct me
Not sure exactly what you want to achieve, but based on the info you have provided:
def hi (n):
while (n < 5):
print("we are inside hi")
n -= 1
Briefly speaking, using return inside a function means to return the value that is followed or return None if there is no value. In addition, the execution of the function is terminated just after the return statement is executed.
You can use the return statement, but if you want to iterate it is not correct because your function will terminate its execution. Also remember that once you execute the iteration of the loop, there won't be more statements to execute inside your function which means an implicit return statement will be executed which returns None, and again function ends execution.
You need a loop for this. Try this instead
for _ in range(4):
print("we are inside hi")
Of course, you need a loop to make iteration. If you just want to print the statement 4 times, simply make a range of loop.
def hi ():
for n in range(4):
print(n+1," we are inside hi")
hi()
you can use this:
def hi (n):
while n <= 5:
print("we are inside hi")
n+=1
return n
n=1
hi(n)
you need a loop to iterate and return statement will exit from the function call.

python: recursive lambda expression

I'm following a book "functional python programming" by David Mertz, i have come across a following piece of code.
def echo_IMP():
while 1:
x = raw_input("IMP -- ")
if x == 'quit':
break
else:
print(x)
The functional version of above code seems like this:
def identity_print(x):
print(x)
return x
echo_FP = lambda: identity_print(raw_input("IMP -- "))=='quit' or echo_FP()
echo_FP()
How this identity_print() is being called again ?
what does this or expression actually doing ?
echo_FP = lambda: identity_print(raw_input("IMP -- "))=='quit' or echo_FP()
The order of execution is:
raw_input("IMP -- ") returns users input (let's call in input)
result of raw input goes to identity_print(input) - which prints input and returns it
the remainder of the function is input=='quit' or echo_FP(). The way A or B works is:
if A:
return A
else:
return B
So, if input=='quit' (that is A), it returns True and ends there, otherwise the whole lambda is executed again (echo_FP()).
You are creating a function with some conditions inside it. in other words, you are saying "call identity_print, and if the result is 'quit': the condition is validated, if not: call me again"
The echo_FP function:
echo_FP = lambda: identity_print(raw_input("IMP -- "))=='quit' or echo_FP()
has this kind of behavior:
def echo_FP():
var = identity_print(input("IMP -- "))
if var == 'quit':
pass
else:
echo_FP()
The recursion comes when identity_print doesn't returns quit because the function executes the or condition which is a call to echo_FP
Here we are defining one lambda function with no actual input but the statement is actually a function call to identity_print() ..whose input is entered by user. That is why we are calling it like this echo_FP() with no input.
As per the functionaliy of 'or' operator it will search for first right match. so in this case if user enters anything other than 'quit' then the first match fails (identity_print(raw_input("IMP -- "))=='quit will result False) so it goes and executes echo_FP(). so till the point user keeps on entering anything other than 'quit' , it will keep on calling echo_FP(). The moment you enter 'quit' it will return true and comes out of it.

Categories

Resources