Every now and then I fall on this pattern when looping through an array:
for a in b:
if a==y:
#do somethinng
return 1
there is no else statement because I need to check the full array for a==y (let's say a is name in a dir list) before returning anything.
It would also be a good idea to return something only if no element in the array fulfills the condition a==y
How should I re organize the code in this sense?
How do I tell the caller if the loop doesn't 'succeed' in finding 'y'?
If you are expecting to encounter y only once or only take into account its first occurrence (given that you have a return statement within the condition), then here's another method:
if any(a == y for a in b):
#do something
return 1
else:
return 0
It would probably be a good idea to return something only if no element in the array fulfills the condition x==b but I don't know how to re organize the code in this sense.
The usual thing is to put the return reflecting the fact that nothing matched after the loop:
for a in b:
if a==y:
#do somethinng
return 1
return 0 # or whatever
In some cases, it may be appropriate to throw an exception after the loop instead, if the fact that nothing matched within the loop is an exceptional condition, not a normal case.
Please be sure to check out navneethc's answer using any as well.
Related
Q1 of this problem set is the following:
Write a Python procedure fix_machine to take 2 string inputs and
returns the 2nd input string as the output if all of its characters
can be found in the 1st input string and "Give me something that's not
useless next time." if it's impossible. Letters that are present in
the 1st input string may be used as many times as necessary to create
the 2nd string (you don't need to keep track of repeat usage).
For example:
print fix_machine('UdaciousUdacitee', 'Udacity') should output "Give me something that's not useless next time."
and:
print fix_machine('buy me dat Unicorn', 'Udacity') should output 'Udacity'
This is the solution I came up with by myself:
def fix_machine(debris, product):
i = -1
while True:
i = i +1
if debris.find(product[i]) == -1:
return "Give me something that's not useless next time."
break
word = product[0:i+1]
if word == product:
break
return word
I found this other sample solution online:
def fix_machine(debris, product):
x = 0
while x < len(product):
if debris.find(product[x]) == -1:
return "Give me something that's not useless next time."
x += 1
return product
My code executes correctly, but I was wondering if it makes sense the way I'm using the break function. Any recommendation about how to better understand while-loops and optimize them?
The two are roughly equivalent in terms of what they do. You are looping until you break, whereas the second solution always loops to what it knows to be the maximum number of loops needed (unless it finds the failure case) - the length of the product argument. Both will run in the same O(n) amount of time, so while you have some awkwardness in your code it's not practically different.
There are other approaches to go about this that are clearer to understand. For instance, since you don't care about repeat variables, it might be easier to take your product as input to a set:
required_characters = {c for c in product} # puts all characters into a set, dropping repeats (because sets can only have a given value once)
for c in required_characters:
if c not in debris:
return "Give me something that's not useless next time."
return product
The functional difference here is that you are looping through product once, and can stop looping through debris the moment you find all the characters. But this also runs in O(n) - which is as fast as you can actually do this. The real question is what is clearest to you, the programmer, and clearest to whomever reads your code.
In the comments, #alani notes that you can also do something like this:
if set(product) <= set(debris):
return product
return "Give me..."
This does something similar, using set comparison. The slight downside here is that you're putting both inputs into sets (an O(n) operation), which means you're guaranteed the maximum runtime - nothing will quit early if it knows it is a condition that can't be satisfied. Still, it is even more concise and therefore clear. For reasonable inputs a totally valid approach.
while loops can be used for any type of looping but for situations where you're iterating over a collection (like the letters in a word) a for loop is much more appropriate. Generally a while loop is more useful when you don't know in advance how many times the loop will execute.
Here's a sample of iterating through both words looking for a letter in product that doesn't appear in debris:
NOT_FOUND_MSG = "Give me something that's not useless next time."
def fix_machine(debris, product):
for p_char in product:
found = False
for d_char in debris:
if p_char == d_char:
found = True
break
if not found:
return NOT_FOUND_MSG
return product
We can clarify (and maybe optimize) by using in to check if p_char is in debris without explicitly iterating over it:
def fix_machine(debris, product):
for p_char in product:
if p_char not in debris:
return NOT_FOUND_MSG
return product
Let's trim it down some more by using an alternate form of the for loop:
def fix_machine(debris, product):
if any(p_char not in debris for p_char in product):
return NOT_FOUND_MSG
return product
Finally an alternative which eliminates explicit loops altogether by taking advantage of the properties of a set:
def fix_machine(debris, product):
return product if set(product).issubset(debris) else NOT_FOUND_MSG
I'm going to answer this under the assumption this is part of an exercise on while loops.
Your solution was a good attempt, but there are a few things that you can still improve. I'm going to walk through your code and point them out.
def fix_machine(debris, product):
i = -1 # THING 1
while True:
i = i +1 # ALSO THING 1
if debris.find(product[i]) == -1:
return "Give me something that's not useless next time."
break # THING 2
word = product[0:i+1] # THING 3
if word == product:
break
return word
Thing 1: Initializing your index variable like this may work, but it isn't very clean. Instead you should do what the example you provided did and initialize to zero then increment at the end of your loop. This is easier to read and better practice overall.
Thing 2: This break statement will never be reached. The return statement will end your function (and by extension, the loop).
Thing 3: This is a clever idea, but a better idea would be to break once you've iterated over the entirety of the product. That way you don't have to check for equality every iteration.
If you fix all of these you'll notice you end up with the second example:
def fix_machine(debris, product):
x = 0 #THING 1 (Initialize to zero)
while x < len(product): #THING 3 (stop at end of product)
if debris.find(product[x]) == -1:
return "Give me something that's not useless next time." # THING 2 (no break)
x += 1 # THING 1(INCREMENT AT END)
return product
I am a beginner in programming and I am trying to implement if...else statement but being failed. I have an array list and I have to check it does not contain a specific value. But the else statement does not work.
#For an example:
a=[10,12.0,13.0]
b=[12.0]
if a <= b:
print("something")
else:
print("others") #else statement doesn't work
I think what you're looking for is an iteration. Currently you're asking if your list a is less than or equal to your list b, but semantically this doesn't have a lot of meaning. In fact, Python seems to somewhat unintuitively select the first element of each list in order to perform the comparison. Thus, in the example code you give it will always evaluate as True since 10 < 12.0.
However, if you add an iterator like so:
a=[10,12.0,13.0]
b=[12.0]
for ai in a:
for bi in b:
if ai <= bi:
print(f"a={ai} b={bi} and a is strictly smaller.")
else:
print(f"a={ai} b={bi} and a is equal or greater.")
In this manner you can compare every item in a with every item in b.
However, if you simply want to ensure that 12.0 is not in a, Python provides a much simpler way:
a=[10,12.0,13.0]
b=[12.0]
if b[0] in a:
print("The list contains the selected value.")
I'm new to Python (I've only used C) and I've discovered new loops such as for/else... So I wonder if I'm ignoring a cleaner way to handle this loop:
flag = 0
for i in range (n):
if not flag and condition:
statement_1
flag = 1
if flag and condition:
statement_2
I need to keep the for counting, because I know that at least one element will satisfy the condition, so when I find it I'll do statement_1. Then if another element will satisfy the condition as well, I'll do statement_2.
flag = False # I prefer booleans
for i in range(n):
if condition(i): # We always need it to be True
if not flag:
statement(i)
flag = True
else:
statement2(i)
So far this would work, but since you said there is at least one that satisfies the condition
foo = range(n) # any iterable
iterfoo = iter(foo)
initial_value = next(i for i in iterfoo if condition(i))
statement(initial_value)
for i in iterfoo:
if condition(i):
statement2(i)
Now these both, (if I'm not missing something) should do the same thing, just in different ways, so it is your choice, although it also saves you 2 lines of code since you wont be doing the first line in your actual code, so I vote for the second snippet :D
I have this question and I want your expert answers about it, because I want to get better in programming.
"""
The parameter s_str is a string. The parameter n is an int > 0.
The function x() should return the last n characters of s_str if
s_str has a length >= n, or the empty string if s_str has a length < n
Example:
x('abcdef', 3) == 'def'
"""
So, I could build the exact code with or without the for statement and it would give me (print) the same values, but I don't know what is the more common way to do it. If I'd go for a for statement, I'd do this:
for i in s_str:
if len(s_str) >= n:
return a_str[-n:]
elif len(s_str) < n:
return ''
Is the idea of using a for statement wrong if you know in advance that you are not going to use i, in this case? I could easily remove the for statement and still get the right answer, so is that enough reason not to use it?
There are cases in which a for loop is justified even if you do not intend to use the loop index (e.g when you want to preform a certain task n times). Having said that, this problem can be solved in a more elegant way, as you have shown.
Also please note that your code iterates over the string len(str) times, except it returns in the first iteration, so the for loop in this case is redundant.
"so is that enough reason not to use it?"
Yes. Simple is better than complex.
You dont actually need a for loop
if len(a_str) >= n:
return a_str[-n:]
it is better and simple too.
I am trying to make word chains, but cant get around recursive searching.
I want to return a list of the words reuired to get to the target word
get_words_quicker returns a list of words that can be made by just changing one letter.
def dig(InWord, OutWord, Depth):
if Depth == 0:
return False
else:
d = Depth - 1;
wordC = 0;
wordS = [];
for q in get_words_quicker(InWord):
wordC+=1
if(OutWord == q):
return q
wordS.append(q)
for i in range(0,wordC):
return dig(wordS[i],OutWord,d)
Any help/questions would be much appreciated.
ANALYSIS
There is nowhere in your code that you form a list to return. The one place where you make a list is appending to wordS, but you never return this list, and your recursive call passes only one element (a single word) from that list.
As jasonharper already pointed out, your final loop can iterate once and return whatever the recursion gives it, or it can fall off the end and return None (rather than "nothing").
You have two other returns in the code: one returns False, and the other will return q, but only if q has the same value as OutWord.
Since there is no code where you use the result or alter the return value in any way, the only possibilities for your code's return value are None, False, and OutWord.
REPAIR
I'm afraid that I'm not sure how to fix this routine your way, since you haven't really described how you intended this code to carry out the high-level tasks you describe. The abbreviated variable names hide their purposes. wordC is a counter whose only function is to hold the length of the list returned from get_words_quicker -- which could be done much more easily.
If you can clean up the code, improve the data flow and/or documentation to something that shows only one or two disruptions in logic, perhaps we can fix the remaining problems. As it stands, I hesitate to try -- you'd have my solution, not yours.