Why will my program that finds double letters not execute? Python - python

My intent is to have the program list all strings in a text file that have 3 sets of double letters. Here is the function that is supposed to return True if 3 or more double letter sets are found:
def three_double(s):
doubnum = 0
i=0
while i < len(s)-1:
if s[i] == s[i+1]:
doubnum += 1
elif doubnum >= 3:
return True
else:
i += 1
return False
I'm not sure why it doesn't print anything. Here is the rest of the program.
# Function to read and apply the three_double test to each string in
# an input file. It counts the number of results.
def find_three_double(fin):
count = 0
for w in fin:
w = w.strip()
if three_double(w):
print w
count = count + 1
if count == 0:
print '<None found>'
else:
print count, 'found'
# Bring in a package to access files over the web
import urllib
# Access the file containing the valid letters
words_url = "http://thinkpython.com/code/words.txt"
words_file = urllib.urlopen(words_url)
# Apply the actual test
find_three_double(words_file)

I didn't read your code carefully at first, turns out it isn't related to read() or readlines() as you are iterating in find_three_doubles() function.
In your three_double() function:
while i < len(s)-1:
if s[i] == s[i+1]:
doubnum += 1
elif doubnum >= 3:
return True
else:
i += 1
return False
There are two problems:
You need to increment i by 1 otherwise the while loop will never stop if there is a "double".
You also need to change elif to if here because otherwise some qualified words will not be selected.
Fixed Code:
def three_double(s):
doubnum = 0
i=0
while i < len(s)-1:
if s[i] == s[i+1]:
doubnum += 1
if doubnum >= 3:
return True
i += 1
return False
# Function to read and apply the three_double test to each string in
# an input file. It counts the number of results.
def find_three_double(fin):
count = 0
for w in fin:
w = w.strip()
if three_double(w):
print w
count = count + 1
if count == 0:
print '<None found>'
else:
print count, 'found'
# Bring in a package to access files over the web
import urllib
# Access the file containing the valid letters
words_url = "http://thinkpython.com/code/words.txt"
words_file = urllib.urlopen(words_url)
# Apply the actual test
find_three_double(words_file)
Results:
aggressiveness
aggressivenesses
allottee
allottees
appellee
appellees
barrenness
barrennesses
bookkeeper
bookkeepers
bookkeeping
bookkeepings
cheerlessness
cheerlessnesses
committee
committees
greenness
greennesses
heedlessness
heedlessnesses
heelless
hyperaggressiveness
hyperaggressivenesses
keelless
keenness
keennesses
masslessness
masslessnesses
possessiveness
possessivenesses
rottenness
rottennesses
sleeplessness
stubbornness
stubbornnesses
successfully
suddenness
suddennesses
sullenness
sullennesses
toolless
wheelless
whippoorwill
whippoorwills
woodenness
woodennesses
46 found

itertools.groupby can greatly simplify your program (= less bugs)
from itertools import groupby
import urllib
def find_three_double(words_file):
for word in words_file:
word = word.strip()
if sum(sum(1 for i in g) == 2 for k,g in groupby(word)) == 3:
print word
# Access the file containing the valid letters
words_url = "http://thinkpython.com/code/words.txt"
words_file = urllib.urlopen(words_url)
# Apply the actual test
find_three_double(words_file)
Explanation:
inside the generator expression we see groupby(word). This scans the word and gathers the double letters together.
sum(1 for i in g) is applied to each group. It is equivalent to finding the length of the group. If the length is 2, then this is a double letter so sum(1 for i in g) == 2 evaluates to True
The outer sum() adds up all the True and False values, True is added as 1 and False is added as 0. If there are exactly 3 True values, the word is printed

while i < len(s)-1:
if s[i] == s[i+1]:
doubnum += 1
elif doubnum >= 3:
return True
else:
i += 1
If your first check (s[i] == s[i+1]) is True, then you'll never increment i so the loop continues forever.

Related

How do you make a program that detects if a a word in an array is a heterogram? That would output it as True and False?

def hetemoro(s):
hash = [0] * 26
x = 0
repeat = 0
register = 0
result = []
new_list = []
while(repeat <= 2):
activity = ''.join(c for c in s[x] if c.isalpha()) #Necessary in order to allow 's' to exclude all numbers
new_list.append(activity.lower())
repeat += 1
x += 1
x = 0
repeat = 0
while(repeat <= 2):
ignore = 0
a = new_list[x]
n = len(a)-1
for i in range(n):
if hash[ord(a[i]) - ord('a')] == 0:
hash[ord(a[i]) - ord('a')] = 1
else:
value = (s[x], False)
result.append(value)
register += 1
if (register == 3):
print(result)
ignore = 1
x += 1
repeat += 1
if (ignore == 0):
value = (s[x], True)
result.append(value)
register += 1
if (register == 3):
print(result)
x += 1
repeat += 1
return result
Am I missing something? Because the output is just
hetemero(['great', 'dgss', 'may'])
hetemero(['a25a', 'agd', 'ss25'])
[('great', True), ('dgss', False), ('may', False)]
[('a25a', True), ('agd', False), ('ss25', True)]
Which is wrong since 'may' is definitely true, I think the code has some mistakes in the process and I need some help in understanding what I need to add and remove so that it can move smoothly
Is this the function You were going for:
def func(words):
return [(word, len(set(word)) == len(word)) for word in words]
The reason to use sets is because they can't contain duplicate values so if in word there is a letter that repeats it will be eliminated thus the length of the set will be shorter than the word
Well thank you everyone for your contribution I have found the solution with the help of you all.
Namely the person who suggested to add
if len(set(word)) == len(word):
lst.append((words[x], True))
else:
lst.append((words[x], False))
repeat += 1
So yes, the output is correct now.
Working with Set is ok but it won't bring you lazy evaluation since the whole word is scanned to detect duplicated letters.
I would rahter use a dictionnary to keep track of each letter parsed and imediately return False if the next letter encountered is already stored. I will go with this implementation:
def is_heterogram(word):
d = {}
for letter in word:
if letter in d.keys():
return False
d[letter] = None
return True
def hetemero(words):
return [(word, is_heterogram(word)) for word in words]
print(hetemero(['great', 'dgss', 'may']))
print(hetemero(['a25a', 'agd', 'ss25']))

How can I transform my for loop code into the while loop in Python?

I am trying to write a function named has_no_e that takes a string as an argument and returns False if the string contains the letter e and True if the string does not contains the letter e.
I have written the function using the for loop but I am failing to write a similar function that does the same thing using a while loop.
Here is my function with the for loop:
def has_no_e(word):
for letters in word:
if letters == "e":
return False
return True
And the function I have written is:
def has_no_e2(word):
index = 0
while index < len(word):
if word[index] != "e":
index += 1
return False
return True
Can you tell me what is wrong with my function with the while loop? It returns False every time not depending on whether there is "e" or not.
There are two ways to implement the same fix for this. The one that keeps more closely to your existing code is
def has_no_e2(word):
index = 0
while index < len(word):
if word[index] != "e":
index += 1
continue # skip the rest of the loop and go back to the top
return False
return True
The better one is
def has_no_e2(word):
index = 0
while index < len(word):
if word[index] == "e":
return False
index += 1
return True
Note that the following two pieces of code are roughly equivalent:
for elem in iterable:
# code
_index = 0
while _index < len(iterable):
elem = iterable[_index]
# code
_index += 1
It's more complicated than that, because for loops often use iterators instead of indices, but you get the idea.
Try:
index = 0
while index < len(word):
if word[index] != "e":
index += 1
else:
return False
return True
Currently your code returns false at the end of the first loop
def has_no_e1(_string):
return 'e' not in _string
def has_no_e2(_string):
return _string.find('e') == -1
def has_no_e3(_string):
while _string:
if _string[0] == 'e':
return False
_string = _string[1:]
return True
if __name__ == "__main__":
word = 'With the letter `e`'
print(
has_no_e1(word),
has_no_e2(word),
has_no_e3(word),
)
word = 'Without it'
print(
has_no_e1(word),
has_no_e2(word),
has_no_e3(word),
)
You can use the in operator:
def has_no_e(word):
return 'e' not in word
In case you really need a while loop:
def while_has_no_e(word):
characters = list(word)
while characters:
if characters.pop() == 'e':
return False
return True
print(has_no_e('foo'))
print(has_no_e('element'))
print(while_has_no_e('foo'))
print(while_has_no_e('element'))
Out:
True
False
True
False
Some changes and you are good to go, you just need to write whatever condition you wrote in for loop into while, that's it:
def has_no_e2(word):
index = 0
while index < len(word):
if word[index] == "e":
return False
index+=1
return True
Now let me give you a one liner:
>>> string = 'hello'
>>> False if string.count('e') > 0 else True
False
#changing the contents
>>> string = 'hlo'
>>> False if string.count('e') > 0 else True
True
You don't need to create a function, it exists already and it's called find()
has_e = word.find("e")
The function either returns the exact position of the letter or it returns -1 if it doesn't exist.
Reference

how do i run length encode a pattern, rather than a character?

heres my current RLE code
import re
def decode(string):
if string == '':
return ''
multiplier = 1
count = 0
rle_decoding = []
rle_encoding = []
rle_encoding = re.findall(r'[A-Za-z]|-?\d+\.\d+|\d+|[\w\s]', string)
for item in rle_encoding:
if item.isdigit():
multiplier = int(item)
elif item.isalpha() or item.isspace():
while count < multiplier:
rle_decoding.append('{0}'.format(item))
count += 1
multiplier = 1
count = 0
return(''.join(rle_decoding))
def encode(string):
if string == '':
return ''
i = 0
count = 0
letter = string[i]
rle = []
while i <= len(string) - 1:
while string[i] == letter:
i+= 1
count +=1
#catch the loop on last character so it doesn't got to top and access out of bounds
if i > len(string) - 1:
break
if count == 1:
rle.append('{0}'.format(letter))
else:
rle.append('{0}{1}'.format(count, letter))
if i > len(string) - 1: #ugly that I have to do it twice
break
letter = string[i]
count = 0
final = ''.join(rle)
return final
the code might have gotten fucked up when I removed all my comments, but the current code isn't too important. the problem is, I am running RLE on hexadecimal values, that have all been converted to letters so that 0-9 becomes g-p. the problem is that there are a lot of patterns like 'kjkjkjkjkjkjkjkjlmlmlmlmlmlmlm' which doesn't compress at all, because of their not single characters. how would I, if even possible, be able to run my program so that it encodes patterns as well?

search through the word for the letter which comes alphabetically first, and returns that letter

I'm trying to write a code that basically requires me to find the letter that comes alphabetically first and print. Although the printing is required in another function. Need to use the while loop
this what I've gotten so far.
def alphabetically_first(word):
n = 0
p = 0
while n + 1 < len(word):
if word[n] < word[n+1]:
p = word[n]
elif word[n + 1] < word [n]:
p = word[n+1]
n += 1
print p
return

IndexError when checking if input is palindrome using while loop

I'd like to know how do I check if an input is a palindrome with a while loop, using Python.
Thanks:
i tried this
i = 0
n = len(msg_list)
while i < n:
palindrome = msg_list[i]
if palindrome == msg_list[-1]:
print("Palindrome? True")
msg_list.pop(-1)
else:
print("Palindrome? False")
i=i+1
but at the end I receive an error message that the list index is out of range
You don't need to iterate till the end, but only till the middle character. And compare every character to the character at the same index when counted in reverse:
s = "abcca"
length = len(s)
i = 0
while i < length / 2 + 1:
if s[i] != s[-i - 1]:
print "Not Palindrome"
break
i += 1
else:
print "Palidrome"
else part of the while loop is executed, when the loop completes its iteration without any break.
Alternatively, if you can use anything else than a while loop, then this task is just of single line in Python:
if s == s[::-1]:
print "Palindrome"
Oh, it became two lines.
With a while loop
import string
palin = 'a man, a plan, a canal, panama'
def testPalindrome(in_val):
in_val = in_val.lower()
left, right = 0, len(in_val) - 1
while left < right:
char_left, char_right = '#', '#'
while char_left not in string.lowercase:
char_left = in_val[left]
left += 1
while char_right not in string.lowercase:
char_right = in_val[right]
right -= 1
if char_left != char_right:
return False
return True
print testPalindrome(palin)
Without
>>> palindrome = 'a man, a plan, a canal, panama'
>>> palindrome = palindrome.replace(',', '').replace(' ', '')
>>> palindrome
'amanaplanacanalpanama'
>>> d[::-1] == d
True
A short solution using reversed:
for c, cr in s, reversed(s):
if c != cr:
print("Palindrome? False")
break
else:
print("Palindrome? True")
Another way using a while loop. Once two characters don't match, the while loop stops, so it's quite efficient but of course not the best way to do it in Python.
def palindrome(word):
chars_fw = list(word)
chars_bw = list(reversed(word))
chars_num = len(word)
is_palindrome = True
while chars_num:
if chars_fw[chars_num-1] != chars_bw[chars_num-1]:
is_palindrome = False
break
chars_num -= 1
return is_palindrome
Figured I would add another alternative for people still viewing this question. It uses a while loop, and is reasonably concise (although, I still prefer the if word = word[::-1] approach.
def is_palindrome(word):
word = list(word.replace(' ', '')) # remove spaces and convert to list
# Check input
if len(word) == 1:
return True
elif len(word) == 0:
return False
# is it a palindrome....
while word[0] == word[-1]:
word.pop(0)
word.pop(-1)
if len(word) <= 1:
return True
return False
word = "quiniuq"
pairs = zip(word,reversed(word))
a,b = next(pairs)
try:
while a == b:
a,b = next(pairs)
return False # we got here before exhausting pairs
except StopIteration:
return True # a == b was true for every pair
The use of a while loop here is contrived, but it will consume the whole list and perform the test.
If a while loop wasn't a requirement, one would do: all(a == b for a,b in zip(word,reversed(word)))

Categories