Execute statement between 2 elif blocks - python

Is there any way to execute statements between the (false) evaluation of 1 elif block and the evaluation of the next in Python 3.x?
I wish to optimise my program by only running the function "word_in_special_list" should the first 2 statements of an if block evaluate as false.
Ideally the program would look something like this:
for word in lis:
#Finds word in list
if word_in_first_list(word):
score += 1
elif word_in_second_list(word):
score -= 1
#Since the first 2 evaluations return false, the following statement is now run
a, b = word_in_special_list(word)
#This returns a Boolean value, and an associated score if it's in the special list
#It is executed only if the word isn't in the other 2 lists,
#and executed before the next elif
elif a:
score += b #Add b to the running score
else:
...other things...
#end if
#end for
Of course, putting a tuple in a elif evaluation returns an error. I also can't restructure my if statement because it is more likely that the word will be in the first or second list, and so this structure saves time. So is there any way to run a code block in between 2 elif evaluations?

You would have to make an else case then nest within that
for word in lis:
if word_in_first_list(word):
score += 1
elif word_in_second_list(word):
score -= 1
else:
a, b = word_in_special_list(word)
if a:
score += b #Add b to the running score
else:
...other things...

You could put in continue statements.
for word in lis:
#Finds word in list
if word_in_first_list(word):
score += 1
continue
if word_in_second_list(word):
score -= 1
continue
a, b = word_in_special_list(word)
if a:
score += b #Add b to the running score
else:
...other things...
I think this has the effect you're trying to achieve. Incidentally, I don't see why you need the function word_in_first_list. What's wrong with if word in first_list?

Related

Can't assign to literal with "for" loop

I am creating a Yahtzee program in Python. This function is meant to carry out the action that the user chooses (the user inputs a number, and it chooses the appropriate list item). I just got to the section about adding the total of one number set (the top part of a Yahtzee card with the ones, twos, etc.). I made a loop that adds one to the score for every 1 found in list dicevalues (a random list of "rolled dice" numbers; declared earlier in program).
I am getting the error on the for 1 in dicevalues: line. It says SyntaxError: cannot assign to literal. I looked up this error, but I'm not making sense of it. What I interpret here is that the program would run the code in the for block for every value 1 in dicevalues, but I'm not quite sure if you can use the for loop in that way.
def choiceAction():
if options[choice] == "Chance (score total of dice).":
global score
score += (a + b + c + d + e)
if options[choice] == "YAHTZEE!":
score += 50
if options[choice] == "Large straight":
score += 40
if options[choice] == "Small straight.":
score += 30
if options[choice] == "Four of a kind (total dice score).":
score += (a + b + c + d + e)
if options[choice] == "Three of a kind (total dice score).":
score += (a + b + c + d + e)
if options[choice] == "Full house.":
score += 25
if options[choice] == "Add all ones.":
for 1 in dicevalues: # <-- SyntaxError: can't assign to literal
score += 1
Is it possible that for some reason 1 cannot be in the for declaration?
If you don't want to use the items in dicevalues you can use a placeholder
for _ in dicevalues:
The error
When you write for x in dicevalues: you iterate over dicevalues and put each element in the variable x, so x can not be replaced with 1. This is why you get the error SyntaxError: can't assign to literal.
The solution(s)
Here are several solutions to perform what you want:
dicevalues = [2, 1, 3, 6, 4 ,1, 2, 1, 6]
# 1. Classic 'for' loop to iterate over dicevalues and check if element is equal to 1
score = 0
for i in dicevalues:
if i == 1:
score += 1
print(score) # 3
# 2. Comprehension to get only the elements equal to 1 in dicevalues, and sum them
score = 0
score += sum(i for i in dicevalues if i == 1)
print(score) # 3
# 3. The 'count()' method to count the number of elements equal to 1 in dicevalues
score = 0
score += dicevalues.count(1)
print(score) # 3

Storing variable inside of recursive function (python)

I wrote a recursive function to find (0,0) tuples inside of a list.
def find_tuple(l, score = 0):
for i, item in enumerate(l):
try:
if item == 0 and l[i+1] == 0:
score = score + 1
print("Number of tuples: {}".format(score))
l = l[i+2:]
find_tuple(l, score)
except:
break
return score
When I've used it i noticed strange behaviour:
>>> find_tuple([0,0])
Number of tuples: 1
1
>>> find_tuple([0,0,0])
Number of tuples: 1
1
>>> find_tuple([0,0,0,0])
Number of tuples: 1
Number of tuples: 2
1
>>> find_tuple([0,0,0,0,0,0])
Number of tuples: 1
Number of tuples: 2
Number of tuples: 3
Number of tuples: 2
2
The function returned 1 instead of 2 and 2 instead of 3 in the last two examples. Why is this happening ?
You need to return the variable for the previous function call to catch it.
replace find_tuple(l, score) with return find_tuple(l, score) inside the if statement
def find_tuple(l, score = 0):
for i, item in enumerate(l):
try:
if item == 0 and l[i+1] == 0:
score = score + 1
print("Number of tuples: {}".format(score))
l = l[i+2:]
return find_tuple(l, score)
except:
break
return score
Your recursion logic doesn't seem correct to me. You don't need to use for loop in a recursive function. You handle a part of job, reduce the problem and call the same function again to rest of the problem. At the end, use the returned value from the function. Also, you need to put a condition to stop.
Something like this could work for your problem:
def find_tuple(l, score = 0):
if len(l)<=1:
return score
if l[0] == 0 and l[1] == 0:
score += 1
return find_tuple(l[2:], score)
print(find_tuple([0,0]))
print(find_tuple([0,0,0]))
print(find_tuple([0,0,0,0]))
print(find_tuple([0,0,0,0,0,0]))
Output:
1
1
2
3

Division by values in a dictionary (Python)

I am building a prime generator (I know, another one and probably not a very good one at that, but that is for another question.) I am using a dictionary for my collection of primes and ignoring composites by dividing by the previous primes. However it doesn't seem to be iterating properly in the final stage of the function and I get a number of incorrect results. isWhole is a self explanatory call to another function. This is my code where x = the number of primes to be generated:
def prime_generator(x):
count = 2
counter = 2
p = {1: 2}
while len(p) <= x:
if count % 2 == 0:
count += 1
continue
test1 = (math.sqrt(count))
if isWhole(test1) == True:
count += 1
continue
for k, a in p.items():
if count % a == 0:
break
else:
p[ counter ] = count
counter += 1
break
count += 1
return p
Your design intent is not entirely clear, but you may be intending to have the else clause apply to the for loop rather than the if statement. Try un-indenting the entire else clause so that it only runs if the loop terminates without hitting a break.

Why won't the loop function as intended? Details below:

I have asked a similar question before, but here's a slightly different problem I'm encountering after fixing some previous issues:
There are two txt files that are structured similarly into columns. File 1 has the following columns: tagname1, aapos, synonymous; and file 2 has the following: tagname2, aapos1, aapos2. What I want to do is compare every single tagname1 in file 1 to every single tagname2 in file 2 and see if they match. For every single match, I want the program to then check if the aapos value corresponding to that specific tagname1 falls betweeen aapos1 and aapos2, as stated by my second if statement. If after going through all of file 2, it is unable to find a match for aapos, ONLY then do I want to execute the following if statements and check if synonymous in file 1 is equal to 0 or 1 and add 1 to syn2 or nonsyn2, depending on the case. The same applies if for a specific tagname1, the program goes through the entire list of tagname2 in file 2, and is unable to find a match.
However, according to my code, the program only runs once and I get a value of 1 for snps and a value of one for nonsyn2. I'm not sure why this is the case.
for x in range(1,15):
flag = 0
snps = 0
for b in range (1,15):
if tagname1[x]== tagname2[b]:
flag = 1
if int(aapos1[b]) <= int(aapos[x])<= int(aapos2[b]):
snps = snps + 1
if snps == 0:
if int(synonymous[x]) == 0:
nonsyn2 = nonsyn2 + 1
elif int(synonymous[x]) == 1:
syn2 = syn2 + 1
elif flag == 0:
if int(synonymous[x]) == 0:
nonsyn2 = nonsyn2 + 1
elif int(synonymous[x]) == 1:
syn2 = syn2 + 1
Are you sure you mean to break in the lower if statements?
eg:
if int(synonymous[x]) == 0:
nonsyn2 = nonsyn2 + 1
break
These are within the first for loop (for x in range(1,15):) so if one of these conditions is met you will be dropped out of the loop.
If this happens on your first loop, it might be why you only see it run once.
I'm not entirely sure I understand what your trying to do, but the keyword 'continue' might be what you want. It will make the program go to the next iteration of your loop.

string comparison in a while loop

I've written a small piece of code that should detect if there are any matching characters in the same place in the 2 strings. If there is , the score in incremented by 1, if there is 2 or more consecutive matching characters , the score is incremented by 3, if there is no matching character, the score is decremented by 1.
The problem is though , when i try to run the code, it gives me a error: string index out of range.
What might be wrong ? thank you very much.
def pairwiseScore(seqA, seqB):
count = 0
score = 0
while count < len(seqA):
if seqA[count] == seqB[count]:
score = score + 1
count = count + 1
while seqA[count] == seqB[count]: # This is the line the error occurs
score = score + 3
count = count + 1
elif seqA[count] != seqB[count]:
score = score - 1
count = count + 1
return score
Do both strings have the same lenght? otherwise you should consider using something like:
while count < min(len(seqA), len(seqB)):
Also, the zip function might come in handy here to pair off the letters in each word. It is a python builtin. e.g.
def letter_score(s1, s2):
score = 0
previous_match = False
z = zip(s1, s2)
for pair in z:
if pair[0] == pair[1]:
if previous_match:
score += 3
else:
score += 1
previous_match = True
else:
score -= 1
previous_match = False
return score
Indexes are numberd 0 to n.
len(someString)
will give you n + 1.
Let's say that your string is length 10, and the indexes are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Your while loop checks the condition that count is < 10. So far so good.
Ok now let's say that count is equal to 9. Immediately within the first while loop, you increment count.
So now count = 10.
Now attempting to access someString[count] will give you an IndexError because the indices only go up to 9.
The error message says it: You're trying to access a character beyond the boundaries of your string. Consider this:
>>> s = "hello"
>>> len(s)
5
>>> s[4]
'o'
Now when (at the start of the first while loop) count is 1 below len(seqA), then you're incrementing count and then you're doing seqA[count] which will throw this exception.
Let's assume you're calling pairwisescore("a", "a"):
score = 0
count = 0
while count < len(seqA): # 0 < 1 --> OK
if seqA[count] == seqB[count]: # "a" == "a" --> OK
score = score + 1 # score = 1
count = count + 1 # count = 1
while seqA[count] == seqB[count]: # seqA[1] doesn't exist!
In the second while loop you must test that count is less than len(seqA):
while count < len(seqA) and seqA[count] == seqB[count]:
...
and, also there's possibly other bug: If the length of seqB is less than length of seqA, you'll again see runtime exception. so, you should change every occurence of count < len(seqA) with count < min(len(seqA), len(seqB)).
def pairwiseScore(seqA, seqB):
count = 0
score = 0
isOne = False
isTwoOrMore = False
while count < min(len(seqA), len(seqB)):
if seqA[count] == seqB[count]:
if isTwoOrMore:
score = score + 3
count = count + 1
else:
if isOne:
isTwoOrMore = True
score = score + 1
count = count + 1
isOne = True
elif seqA[count] != seqB[count]:
score = score - 1
count = count + 1
isOne = False
isTwoOrMore = False
return score
a = 'apple'
b = 'aplle'
print(pairwiseScore(a, b))
I think this one solve the problem, I added a "counting" bool variable. And to respond to the question, your program didn't compare the length of the second string.
while count < min(len(seqA), len(seqB)):
The problem is that you do this:
count = count + 1
Both before the inner while loop and at the end of it. But you then continue using seqA[count] before checking it against len(seqA) again - so, once it goes too high, Python will try to read past the end of seqA, and you'll get that error (when, if the condition had been checked again after incrementing, the loop would have ended).
Using a Python for loop will get around bugs like this, since Python will manage count for you:
for a,b in zip(seqA, seqB):
if a == b:
score += 1
You can implement the extra-points bit easily in this by keeping track of whether the previous character is a match, rather than trying to work out how many after this one are. A boolean variable last_matched that you keep updated would help with this.

Categories