While-loop within a while-loop python - python

I am trying to write a while-loop within a while-loop, and for some reason it is not working as it should. I know I'm probably missing something really trivial here, but I just don't understand how its not working!
The purpose of the loop is to compare two strings to see whether or not they contain any 3 consecutive words the same. I first split the two strings into lists of their respective 3 word string combinations which I store in the lists strings and stringscompare. Then I loop through each 3 word string in stringscompare for each 3 word string in strings.
This will probably seem like a pretty long way round for some but I am only a novice programmer, any improvements would be greatly appreciated.
So currently, the second while loop runs all the way through, however the first one only loops through once, for the first string in strings. If the string matches I would like it to break from both loops, however these loops are also in a much bigger for loop which I don't want it to break out of.
e.g.
'this is a string'
'this is another string' --no match
'this is a list a strings' -- matches 'this is a'
'the list is a string' -- should match 'is a string' but currently does not
strings = <list of 3 word strings> [...,...,...]
stringscompare = <list of 3 word strings to compare>
v=0, x=0
while v < len(strings) and stop == False:
while x < len(stringscompare) and stop == False:
if re.search(strings[v], stringscompare[x]):
same.append(dict)
stop = True
x += 1
v +=1

You never reset x inside the outer loop. As a result it'll always be equal or greater to len(stringscompare) after the first iteration of the outer loop.
Set it to 0 in the outer loop:
v = 0
while v < len(strings) and stop == False:
x = 0
while x < len(stringscompare) and stop == False:
Other observations:
Don't use stop == False where not stop will do.
You could just use a for loop, then break out twice:
for s in strings:
for sc in stringscompare:
if re.search(s, sc):
same.append(dict)
break
else:
continue
# only reached if the inner loop broke out
break
or use any() with a generator expression to find the first match:
if any(re.search(s, sc) for s in strings for sc in stringscompare):
same.append(dict)

you use twice the same variable stop I think you should set the stop to false after the loop:
strings = <list of 3 word strings> [...,...,...]
stringscompare = <list of 3 word strings to compare>
v=0, x=0
while v < len(strings) and stop == False:
while x < len(stringscompare) and stop == False:
if re.search(strings[v], stringscompare[x]):
same.append(dict)
stop = True
x += 1
stop = False
v +=1

I would suggest something like this..
strings = [] # <list of 3 word strings> [...,...,...]
stringscompare = [] # <list of 3 word strings to compare>
stop = False
for x in strings:
if stop:
break
for v in stringscompare:
if re.search(v, x):
stop = True
break
A for loop seems a lot more appropriate here. Is this what you're trying to achieve?

Well the most obvious problem with the code here is that you do not set the value of X to be 0 again once the inner loop finishes. I'll explain:
Lets say both 'strings' and 'stringscompare' length is 3. When you first enter the big while loop, v=0 and x=0.
When we enter the second loop, x=0. When we leave the second loop, if no match was found, x = length(stringscompare) = 3.
Now, when we go back to the first while loop it DOES actually go inside the loop again - but it does nothing, as the clause for the 2nd loop cannot be satisfied - X is still 3!
You can fix this by resetting X at the end of loop 2:
strings = <list of 3 word strings> [...,...,...]
stringscompare = <list of 3 word strings to compare>
v=0, x=0
while v < len(strings) and stop == False:
while x < len(stringscompare) and stop == False:
if re.search(strings[v], stringscompare[x]):
same.append(dict)
stop = True
x += 1
x = 0 #resetting X
v +=1
You can easily track downs problems like this one by debugging. There are many methods to debugging, its an art really :P
What I would recommend for you:
If your'e using Eclipse, PyCharm, etc, you can use the built in debuggers which are amazing. You can stop at any point of the program by adding breakpoints, see the value of all the variables, etc.
For example, in this case, I would put a breakpoint on the first loop statement ('while v < len...'). That way, you couldv easly see that the first loop is in fact running, and its the second loop thats the problem.
Printing is your friend. Something simple like this for example:
v=0, x=0
while v < len(strings) and stop == False:
print "About to enter second loop..."
while x < len(stringscompare) and stop == False:
if re.search(strings[v], stringscompare[x]):
same.append(dict)
stop = True
x += 1
v +=1
You could also print the values of variables btw.
There are many other ways to do what you're trying to do (triangle graphs, advanced search methods, maybe a more complex data structure, etc). But for now - I think the best thing for you is to solve problems in the most straightforward way you can - focus on writing quality code (well named variables, indents, comments etc). The geeky, 'top performance' cutting edge tricks will come in later :)
Good luck and have fun!

Related

SNHU Python 'Simon Says': compare two strings for equality character-wise, without using an index?

I was given a prompt to solve and was able to write code that passed, but my question is, is there a more simplified way I could write this without having to create a new named variable (s_index = 0)? The code works just fine but I'm not sure if I solved it the way I was expected to and am open to suggestions for improvement :)
Please note that this section in the work book has us focusing on using continue and break within loops
"Simon Says" is a memory game where "Simon" outputs a sequence of 10 characters (R, G, B, Y)
and the user must repeat the sequence. Create a for loop that compares the two strings.
For each match, add one point to user_score. Upon a mismatch, end the game.
Sample output with inputs: 'RRGBRYYBGY' 'RRGBBRYBGY'
User score: 4
user_score = 0
simon_pattern = input()
user_pattern = input()
s_index = 0
for letter in user_pattern:
if letter == simon_pattern[s_index]:
user_score += 1
s_index += 1
else:
break
print('User score:', user_score)
using functions to encapsulate small specific parts of your logic is often helpful
def do_score(user_pattern="1234",simon_pattern="1235"):
# using enumerate you can get the indices
for s_index,a_char in enumerate(simon_pattern):
if s_index >= len(user_pattern) or a_char != user_pattern[s_index]:
# the index should always match the "sum" so no need to track or compute the sum
return s_index
return len(simon_pattern)
this method takes 2 strings and "scores" them based on the "simon_pattern" returning the score
then just
print(do_score(user_entered_input,simon_pattern))
I will rewrite this to this way: (this way you can completely eliminate the variable index, and simon_pattern[index] to get the letter)
Note - in Python a word is just a sequence of character/letters, you can iterate it directly, no need to use index.
simon = 'RRGBRYYBGY'
user = 'RRGBBRYBGY' # User score: 4
user_score = 0
for user_char, simon_char in zip(user, simon):
if user_char == simon_char: # continue to check/and match...
user_score += 1
else: # break, if no match!
break
print('User score:', user_score)
Strictly, you never need to know the index or index into the strings, you can just use zip() to combine tuples of respective characters from the two (possibly different-length) strings:
def do_score(user_pattern='RRGBRYYBGY', simon_pattern='RRGBBRYBGY'):
score = 0
for uc,sc in zip(user_pattern, simon_pattern):
if uc == sc:
score += 1
else:
break
return score
assert do_score('', '') == 0
assert do_score('RRG', '') == 0
assert do_score('', 'RRG') == 0
assert do_score('RRG', 'RRGB') == 3

White space counter

Your program should ask the user to input the list, then it should call the function, and print the result. with the following condition:
It returns True if the given list has at least 2 white spaces and False otherwise.
My code:
n = ((input("Please input a list of numbers separated by space:")))
t = 0
k = n.count(' ')
for i in range(0,len(n)):
if n[i] > " ":
print("True")
else:
print("False")
print("There are",k,"space which has two length run")
My program counts all of the white spaces, but I want it to only count the 2 white spaces or more and return True or False otherwise
I see several issues with this code.
First, you do not define a function as stated in the requirements.
Second, k = n.count(' ') already counts all the spaces in the input string. You do not need to loop over each character.
Third, even if you did need to loop over the characters, n[i] > " " is definitely not the right way to do what you want.
Fourth, what is the purpose of the t variable?
Fifth, why is input() enclosed in two extra layers of parentheses?
Not clear why you think you need a loop.
Try simply
print(n.count(' ') >= 2)
Or rather
def foo(n):
return n.count(' ') >= 2
print(foo(input("Enter some numbers:")))

comparison within loop while maintaining value

I am trying to make a python program that
1. compares the first and last letter of a word
2. tell whether the words inputted to the program are in alphabetical order
e.g.) alaska baobab cadillac => would pass
e.g.) alaska baobab greg => would not pass
my code is shown below
num_words = int(input())
while num_words > 0:
my_word = str(input())
num_words -= 1
alphabet_order = ord(my_word[0].lower())
if my_word[0].lower() != my_word[-1].lower() or alphabet_order != ord(my_word[0].lower()):
print(my_word)
break
alphabet_order += 1
if alphabet_order == ord('z') + 1:
alphabet_order = ord('a')
else:
print('no mistake')
Hi everyone on stack.
I am pretty new to programming and I am starting to find assigning variables within a loop very cumbersome.
It seems like my variable alphabet_order keeps getting renewed every time when the loop takes in a new input.
What would be a great way to steadily increase the alphabet_order variable by 1 while only renewing ord(my_word[0]) every loop?
Few notes, while loops are good for when you don't know how many times you are going to loop. for loops are good for when you know how many items you are looping. In this case we have a known number of words we are to check so using a for loop makes more sense. This way we don't need to take an input on telling us how many words we expect.
Also your code wasn't dealing with the words you were dealing with the sentence, so instead you should split() your input by the space to get a list of the words to operate on. for loops can iterate through a list which is useful for us.
Note that for loops also can take an else, the else section runs after the for loop is finished all the elements it's iterating through and we know that if the for is finish all the words in the list, then the else section will kick in. If we break out, then the else doesn't run.
The rest you more or less had it, just need a starting ord('a') would have made life easier.
my_word = input() #take a sentence such as alaska baobab cadillac
current_ord = ord('a')
for each in my_word.split(' '):
if each[0].lower() != each[-1].lower() or current_ord != ord(each[0].lower()):
print(f"bad word {each}")
break
current_ord += 1
if current_ord == ord('z') + 1:
current_ord = ord('a')
else:
print('no mistake')
Maybe this is helpful to you. Instead of initializing it inside the loop, declare it outside the loop and assign it differently based on the conditions.
Tip: having while num_words > 0 is redundant because it will terminate automatically when it hits 0 as it is treated as False. And there is no need to convert int to a str type as it is str by default.
num_words = int(input("Enter a number"))
alphabet_order = None
while num_words:
my_word = input("Enter a word")
num_words -= 1
if alphabet_order is None: # happens only once
alphabet_order = ord(my_word[0].lower())
if ord(my_word[0].lower()) >= alphabet_order:
print('ok')
alphabet_order = ord(my_word[0].lower()) # update alphabet_order
else:
print('not ok EXITING')
break # closing loop

Fastest way to determine if two strings differ by a single letter in a large set of strings

I am trying to compare two strings and add one of the strings to a list if they are almost equal (differ by a single letter). What would be the fastest way to do this as my set of words is over 90k and doing this often takes too long?
EDIT: one of the words (comparison_word in code below) does not change.
EDIT2: the words must be of equal length
This is my current code:
for word in set_of_words:
amount = 0
if len(word) == len(comparison_word):
for i in range(len(word)):
if comparison_word[i] != word[i]:
amount += 1
if amount == 1:
list_of_words.append(word)
return list_of_words
You might find zip is a more efficient than indexing:
def almost_equal(set_of_words,comp):
ln = len(comp)
for word in set_of_words:
count = 0
if len(word) == ln:
for a, b in zip(word, comp):
count += a != b
if count == 2:
break
else:
yield word
Demo:
In [5]: list(almost_equal(["foo","bar","foob","foe"],"foa"))
Out[5]: ['foo', 'foe']
The following searches my dictionary of 61K words in about 25 msec.
import re
def search(word, text):
ws = [r'\b{}[^{}]{}\b'.format(w[:i],w[i],w[i+1:]) for i in range(len(word))]
for mo in re.finditer('|'.join(ws), text):
yield mo.group()
with open("/12dicts/5desk.txt") as f:
text = f.read()
for hit in search('zealoos', text):
print(hit) #prints zealous
Presuming that the list of strings is in a file, one string per line, read it in as one long string and use a regular expression to search the string for matches.
search() takes a word like 'what' and turns it into a regular expression like this:
\b[^w]hat\b|\bw[^h]at\b|\bwh[^a]t\b|\bwha[^t]\b
And then scans all the words and find all the near misses--at C-speed.
The idea is to reduce the amount of work being done:
n_comparison_word = len(comparison_word)
for word in set_of_words:
amount = 0
n_word = len(word)
if n_word != n_comparison_word:
continue
for i in range(n_word):
if comparison_word[i] != word[i]:
amount += 1
if amount == 2:
break
if amount == 1:
list_of_words.append(word)
return list_of_words
Some notes:
The value of len(comparison_word) needs to be computed only once (ever).
The value of len(word) needs to computed once (per iteration of the loop).
You know you can stop looking at a word when amount reaches the value 2 (or more - in any case that word can no longer be part of the result).
It may be worth reading this part of the Python documentation regarding the continue and break statements which are both used in the code.
Haven't done exhaustive testing, but if comparison_word is not too long (fewer than 6 letters), and your set_of_words can change, then it might be worth it to compute all acceptable words, store those in a set, a simply iterate through set_of_words and test for word in acceptable_words.
If not, here's my take on your code:
for word in set_of_words:
different_letter_exists = False
length = len(word)
if length == len(comparison_word):
for i, letter in enumerate(word):
if letter != comparison_word[i]:
if different_letter_exists:
break
else:
different_letter_exists = True
if i == length:
list_of_words.append(word)
Essentially: for every word, once you encounter an different letter, different_letter_exists is set to True. If you encounter it again, you break out of the loop. A new word is only added if i == length, which only happens if enumerate gets all the way to the end, which only happens if only one different letter exists.
Good luck :)

Count letters in a word in python debug

I am trying to count the number of times 'e' appears in a word.
def has_no_e(word): #counts 'e's in a word
letters = len(word)
count = 0
while letters >= 0:
if word[letters-1] == 'e':
count = count + 1
letters = letters - 1
print count
It seems to work fine except when the word ends with an 'e'. It will count that 'e' twice. I have no idea why. Any help?
I know my code may be sloppy, I'm a beginner! I'm just trying to figure out the logic behind what's happening.
>>> word = 'eeeooooohoooooeee'
>>> word.count('e')
6
Why not this?
As others mention, you can implement the test with a simple word.count('e'). Unless you're doing this as a simple exercise, this is far better than trying to reinvent the wheel.
The problem with your code is that it counts the last character twice because you are testing index -1 at the end, which in Python returns the last character in the string. Fix it by changing while letters >= 0 to while letters > 0.
There are other ways you can tidy up your code (assuming this is an exercise in learning):
Python provides a nice way of iterating over a string using a for loop. This is far more concise and easier to read than using a while loop and maintaining your own counter variable. As you've already seen here, adding complexity results in bugs. Keep it simple.
Most languages provide a += operator, which for integers adds the amount to a variable. It's more concise than count = count + 1.
Use a parameter to define which character you're counting to make it more flexible. Define a default argument for using char='e' in the parameter list when you have an obvious default.
Choose a more appropriate name for the function. The name has_no_e() makes the reader think the code checks to see if the code has no e, but what it actually does is counts the occurrences of e.
Putting this all together we get:
def count_letter(word, char='e'):
count = 0
for c in word:
if c == char:
count += 1
return count
Some tests:
>>> count_letter('tee')
2
>>> count_letter('tee', 't')
1
>>> count_letter('tee', 'f')
0
>>> count_letter('wh' + 'e'*100)
100
Why not simply
def has_no_e(word):
return sum(1 for letter in word if letter=="e")
The problem is that the last value of 'letters' in your iteration is '0', and when this happens you look at:
word[letters-1]
meaning, you look at word[-1], which in python means "last letter of the word".
so you're actually counting correctly, and adding a "bonus" one if the last letter is 'e'.
It will count it twice when ending with an e because you decrement letters one time too many (because you loop while letters >= 0 and you should be looping while letters > 0). When letters reaches zero you check word[letters-1] == word[-1] which corresponds to the last character in the word.
Many of these suggested solutions will work fine.
Know that, in Python, list[-1] will return the last element of the list.
So, in your original code, when you were referencing word[letters-1] in a while loop constrained by letters >= 0, you would count the 'e' on the end of the word twice (once when letters was the length-1 and a second time when letters was 0).
For example, if my word was "Pete" your code trace would look like this (if you printed out word[letter] each loop.
e (for word[3])
t (for word[2])
e (for word[1])
P (for word[0])
e (for word[-1])
Hope this helps to clear things up and to reveal an interesting little quirk about Python.
#marcog makes some excellent points;
in the meantime, you can do simple debugging by inserting print statements -
def has_no_e(word):
letters = len(word)
count = 0
while letters >= 0:
ch = word[letters-1] # what is it looking at?
if ch == 'e':
count = count + 1
print('{0} <-'.format(ch))
else:
print('{0}'.format(ch))
letters = letters - 1
print count
then
has_no_e('tease')
returns
e <-
s
a
e <-
t
e <-
3
from which you can see that
you are going through the string in reverse order
it is correctly recognizing e's
you are 'wrapping around' to the end of the string - hence the extra e if your string ends in one
If what you really want is 'has_no_e' then the following may be more appropriate than counting 'e's and then later checking for zero,
def has_no_e(word):
return 'e' not in word
>>> has_no_e('Adrian')
True
>>> has_no_e('test')
False
>>> has_no_e('NYSE')
True
If you want to check there are no 'E's either,
def has_no_e(word):
return 'e' not in word.lower()
>>> has_no_e('NYSE')
False
You don't have to use a while-loop. Strings can be used for-loops in Python.
def has_no_e(word):
count = 0
for letter in word:
if letter == "e":
count += 1
print count
or something simpler:
def has_no_e(word):
return sum(1 for letter in word if letter=="e")

Categories