Python if statement is only executed once - python

I try to make a simple spell checking programm by given two strings and adapt the first to the second one. If the strings have the same length my code works fine but if they're different, then the problems start. It only executes the if-statements once and stops after that. If I remove the break points, I get an IndexError: list index out of range.
Here is my code:
#!python
# -*- coding: utf-8 -*-
def edit_operations(first,second):
a = list(first)
b = list(second)
counter = 0
l_a = len(a)
l_b = len(b)
while True:
if a == b:
break
if l_a > l_b:
if a[counter] != b[counter]:
a[counter] = ""
c = "".join(a)
print "delete", counter+1, b[counter], c
counter += 1
l_a -= 1
break
if l_a < l_b:
if a[counter] != b[counter]:
c = "".join(a)
c = c[:counter] + b[counter] + c[counter:]
print "insert", counter+1, b[counter], c
counter += 1
l_a += 1
break
if a[counter] != b[counter]:
a[counter] = b[counter]
c = "".join(a)
print "replace", counter+1, b[counter], c
counter += 1
else:
counter += 1
if __name__ == "__main__":
edit_operations("Reperatur","Reparatur")
edit_operations("Singel","Single")
edit_operations("Krach","Stall")
edit_operations("wiederspiegeln","widerspiegeln")
edit_operations("wiederspiglen","widerspiegeln")
edit_operations("Babies","Babys")
edit_operations("Babs","Babys")
edit_operations("Babeeees","Babys")
This is the output I get:
replace 4 a Reparatur
replace 5 l Singll
replace 6 e Single
replace 1 S Srach
replace 2 t Stach
replace 4 l Stalh
replace 5 l Stall
delete 3 d widerspiegeln
replace 3 d widderspiglen
replace 4 e wideerspiglen
replace 5 r widerrspiglen
replace 6 s widersspiglen
replace 7 p widersppiglen
replace 8 i widerspiiglen
replace 9 e widerspieglen
replace 11 e widerspiegeen
replace 12 l widerspiegeln
delete 4 y Babes
insert 4 y Babys
delete 4 y Babeees
By the last 3 lines you can see my problem and I'm kinda desperate right now.
Hopefully someone could give me a hint what is wrong with it

The answer to the question in the title -- i.e., the if statement executed only once -- is already in a comment to your question, that is, there are two breaks in the two if blocks if l_a < l_b: and if l_a < l_b:.
In general, break statement interrupts the closest loop that it finds, no matter how nested the block where break finds itself is.
However, other problems do appear in your code:
the size of the list a is kept the same, however the same counter is used for iterating over the letters of the two strings. In case the length of the two strings are different, this problem leads eventually to the error IndexError: list index out of range, because the only condition that allows to exit the loop is when the two strings are the same. Also, when l_a > l_b, the same character of b that mismatched with a should be checked with the character next to the deleted one, however this does not happen because of the same counter.
When l_a < l_b the list a is not modified; just a new list c is created with the additional letter. Please look at list documentation.
counter is not updated correctly, as, when the two strings differ in length, it is incremented only if the letters different. This leads to an infinite loop.
In general, consider using a debugger in order to figure out the issues (look at the debuggers available in python https://wiki.python.org/moin/PythonDebuggingTools). It is possible to find online or in a bookstore many resources to learn how to debug code.

You should make use of the list.insert() function to insert a character into the list, the del operator to remove a single character from the list, and move the a==b comparison into the while loop conditional. The variable counter should indicate the index of the next character to be compared, and should not be incremented if the characters are not equal. Like this:
#! python3
def edit_operations(first,second):
a = list(first)
b = list(second)
counter = 0
while a != b:
if a[counter] != b[counter]:
if len(a) > len(b):
print("delete", counter + 1, a[counter])
del a[counter]
elif len(b) > len(a):
print("insert", counter + 1, b[counter])
a.insert(counter, b[counter])
else:
print("replace", counter + 1, b[counter])
a[counter] = b[counter]
else:
counter += 1
print("".join(a))
if __name__ == "__main__":
edit_operations("Reperatur","Reparatur")
edit_operations("Singel","Single")
edit_operations("Krach","Stall")
edit_operations("wiederspiegeln","widerspiegeln")
edit_operations("wiederspiglen","widerspiegeln")
edit_operations("Babies","Babys")
edit_operations("Babs","Babys")
edit_operations("Babeeees","Babys")
I've changed the print statements a little.

I really don't understand what your question is but if you need a spelling checker just use this library

Related

scope of a variable in python for this question

I don't know why, but I am getting value of scope as final as 0 even len(s) as zero in the last line of countfrequency(s) function.
import collections
def countfrequency(s):
final = 0
flag = 1
d = dict(collections.Counter(s))
for item in d:
if d[item] <= k:
flag = 0
if flag == 1: #Here
final = max(final, len(s))
print(final)
s = "ababbc"
k = 2
for x in range(len(s)):
for y in range(1, len(s)):
countfrequency(s[x:y + 1])
It is because of 2 reasons :
Value of flag is 0 at last so it wont change the value of final
Length function takes object as a parameter and when unchanged it gives 0
So you can can either make flag 1 so that control goes inside if condition or print the value of len(s) out side the if condition
In addition to the answer posted by shaktiraj jadeja, the modified code is as follows:
import collections
def countfrequency(s, k):
final = 0
flag = 0
d = dict(collections.Counter(s))
# print(d)
for item in d:
if d[item] > k:
flag = 1
break
if flag == 1: #Here
# print("Inside:", final, len(s))
final = max(final, len(s))
print(final)
s = "ababbc"
k = 2
for x in range(len(s)):
for y in range(1, len(s)):
# print(s[x:y])
countfrequency(s[x:y + 1], k)
To start with there is no problem of scope.
Now lets get back to the problem
Lets define a rule.
Rule: If a sub string has each character repeated more than k(=2) times in it. Then it is a good substring. Else it is a bad substring
Then your code simply prints the length of good sub string or 0 in case of bad substring
In short in your example string s= "ababbc" contains no good substring
if you try S = "aaaaaa" you will see many numbers printed other than 0 (exactly 11 0's and 10 other numbers)
Now either this was your confusion or you wrote the wrong code for some logic
I hope this helps

i have a problem with strings and for loop in python

I have a string and I saved it inside a variable.
I want to change even characters to capitalize with for loop.
so i give my even characters and capitalized it. But
i cant bring them with odd characters .
can someone help me?
here is my code:
name = "mohammadhosein"
>>> for even in range(0, len(name), 2):
... if(even % 2 == 0):
... print(name[even].title(), end=' ')
...
M H M A H S I >>>
>>> ###### I want print it like this:MoHaMmAdHoSeIn```
I assume you are quite new to programing, so use a the following for loop:
name = "mohammadhosein"
output = ''
for i, c in enumerate(name):
if i % 2 == 0:
output += c.upper()
else:
output += c
# output will be 'MoHaMmAdHoSeIn'
enumerate will give you a pair of (i, c) where i is an index, starting at 0, and c is a character of name.
If you feel more comfortable with code you can use a list comprehension and join the results as follows:
>>> ''.join([c.upper() if i % 2 == 0 else c for i, c in enumerate(name)])
'MoHaMmAdHoSeIn'
As #SimonN has suggested, you just needed to make some small changes to your code:
for index in range(0, len(name)):
if(index % 2 == 0):
print(name[index].upper(), end='') # use upper instead of title it is more readable
else:
print(name[index], end='')
print()

Is there a way to increment the iterator if an 'if' condition is met

I'm solving this HackerRank challenge:
Alice has a binary string. She thinks a binary string is beautiful if and only if it doesn't contain the substring '010'.
In one step, Alice can change a 0 to a 1 or vice versa. Count and print the minimum number of steps needed to make Alice see the string as beautiful.
So basically count the number of '010' occurrences in the string 'b' passed to the function.
I want to increment i by 2 once the if statement is true so that I don't include overlapping '010' strings in my count.
And I do realize that I can just use the count method but I wanna know why my code isn't working the way I want to it to.
def beautifulBinaryString(b):
count = 0
for i in range(len(b)-2):
if b[i:i+3]=='010':
count+=1
i+=2
return count
Input: 0101010
Expected Output: 2
Output I get w/ this code: 3
You are counting overlapping sequences. For your input 0101010 you find 010 three times, but the middle 010 overlaps with the outer two 010 sequences:
0101010
--- ---
---
You can't increment i in a for loop, because the for loop construct sets i at the top. Giving i a different value inside the loop body doesn't change this.
Don't use a for loop; you could use a while loop:
def beautifulBinaryString(b):
count = 0
i = 0
while i < len(b) - 2:
if b[i:i+3]=='010':
count += 1
i += 2
i += 1
return count
A simpler solution is to just use b.count("010"), as you stated.
If you want to do it using a for loop, you can add a delta variable to keep track of the number of positions that you have to jump over the current i value.
def beautifulBinaryString(b):
count = 0
delta = 0
for i in range(len(b)-2):
try:
if b[i+delta:i+delta+3]=='010':
count+=1
delta=delta+2
except IndexError:
break
return count
You don't need to count the occurrences; as soon as you find one occurrence, the string is "ugly". If you never find one, it's beautiful.
def is_beautiful(b):
for i in range(len(b) - 2):
if b[i:i+3] == '010':
return False
return True
You can also avoid the slicing by simply keeping track of whether you've started to see 010:
seen_0 = False
seen_01 = False
for c in b:
if seen_01 and c == '0':
return False
elif seen_1 and c == '1':
seen_01 = True
elif c == '0':
seen_0 = True
else:
# c == 1, but it doesn't follow a 0
seen_0 = False
seen_01 = False
return True

Finding the length of longest repeating?

I have tried plenty of different methods to achieve this, and I don't know what I'm doing wrong.
reps=[]
len_charac=0
def longest_charac(strng)
for i in range(len(strng)):
if strng[i] == strng[i+1]:
if strng[i] in reps:
reps.append(strng[i])
len_charac=len(reps)
return len_charac
Remember in Python counting loops and indexing strings aren't usually needed. There is also a builtin max function:
def longest(s):
maximum = count = 0
current = ''
for c in s:
if c == current:
count += 1
else:
count = 1
current = c
maximum = max(count,maximum)
return maximum
Output:
>>> longest('')
0
>>> longest('aab')
2
>>> longest('a')
1
>>> longest('abb')
2
>>> longest('aabccdddeffh')
3
>>> longest('aaabcaaddddefgh')
4
Simple solution:
def longest_substring(strng):
len_substring=0
longest=0
for i in range(len(strng)):
if i > 0:
if strng[i] != strng[i-1]:
len_substring = 0
len_substring += 1
if len_substring > longest:
longest = len_substring
return longest
Iterates through the characters in the string and checks against the previous one. If they are different then the count of repeating characters is reset to zero, then the count is incremented. If the current count beats the current record (stored in longest) then it becomes the new longest.
Compare two things and there is one relation between them:
'a' == 'a'
True
Compare three things, and there are two relations:
'a' == 'a' == 'b'
True False
Combine these ideas - repeatedly compare things with the things next to them, and the chain gets shorter each time:
'a' == 'a' == 'b'
True == False
False
It takes one reduction for the 'b' comparison to be False, because there was one 'b'; two reductions for the 'a' comparison to be False because there were two 'a'. Keep repeating until the relations are all all False, and that is how many consecutive equal characters there were.
def f(s):
repetitions = 0
while any(s):
repetitions += 1
s = [ s[i] and s[i] == s[i+1] for i in range(len(s)-1) ]
return repetitions
>>> f('aaabcaaddddefgh')
4
NB. matching characters at the start become True, only care about comparing the Trues with anything, and stop when all the Trues are gone and the list is all Falses.
It can also be squished into a recursive version, passing the depth in as an optional parameter:
def f(s, depth=1):
s = [ s[i] and s[i]==s[i+1] for i in range(len(s)-1) ]
return f(s, depth+1) if any(s) else depth
>>> f('aaabcaaddddefgh')
4
I stumbled on this while trying for something else, but it's quite pleasing.
You can use itertools.groupby to solve this pretty quickly, it will group characters together, and then you can sort the resulting list by length and get the last entry in the list as follows:
from itertools import groupby
print(sorted([list(g) for k, g in groupby('aaabcaaddddefgh')],key=len)[-1])
This should give you:
['d', 'd', 'd', 'd']
This works:
def longestRun(s):
if len(s) == 0: return 0
runs = ''.join('*' if x == y else ' ' for x,y in zip(s,s[1:]))
starStrings = runs.split()
if len(starStrings) == 0: return 1
return 1 + max(len(stars) for stars in starStrings)
Output:
>>> longestRun("aaabcaaddddefgh")
4
First off, Python is not my primary language, but I can still try to help.
1) you look like you are exceeding the bounds of the array. On the last iteration, you check the last character against the character beyond the last character. This normally leads to undefined behavior.
2) you start off with an empty reps[] array and compare every character to see if it's in it. Clearly, that check will fail every time and your append is within that if statement.
def longest_charac(string):
longest = 0
if string:
flag = string[0]
tmp_len = 0
for item in string:
if item == flag:
tmp_len += 1
else:
flag = item
tmp_len = 1
if tmp_len > longest:
longest = tmp_len
return longest
This is my solution. Maybe it will help you.
Just for context, here is a recursive approach that avoids dealing with loops:
def max_rep(prev, text, reps, rep=1):
"""Recursively consume all characters in text and find longest repetition.
Args
prev: string of previous character
text: string of remaining text
reps: list of ints of all reptitions observed
rep: int of current repetition observed
"""
if text == '': return max(reps)
if prev == text[0]:
rep += 1
else:
rep = 1
return max_rep(text[0], text[1:], reps + [rep], rep)
Tests:
>>> max_rep('', 'aaabcaaddddefgh', [])
4
>>> max_rep('', 'aaaaaabcaadddddefggghhhhhhh', [])
7

Counting differences between two strings

I'm trying to count the number of differences between two imported strings (seq1 and seq2, import code not listed), but am getting no result when running the program. I want the output to read something like "2 differences." Not sure where I'm going wrong...
def difference (seq1, seq2):
count = 0
for i in seq1:
if seq1[i] != seq2[i]:
count += 1
return (count)
print (count, "differences")
You could do this pretty flatly with a generator expression
count = sum(1 for a, b in zip(seq1, seq2) if a != b)
If the sequences are of a different length, then you may consider the difference in length to be difference in content (I would). In that case, tag on an extra piece to account for it
count = sum(1 for a, b in zip(seq1, seq2) if a != b) + abs(len(seq1) - len(seq2))
Another weirdish way to write that which takes advantage of True being 1 and False being 0 is:
sum(a != b for a, b in zip(seq1, seq2))+ abs(len(seq1) - len(seq2))
zip is a python builtin that allows you to iterate over two sequences at once. It will also terminate on the shortest sequence, observe:
>>> seq1 = 'hi'
>>> seq2 = 'world'
>>> for a, b in zip(seq1, seq2):
... print('a =', a, '| b =', b)
...
a = h | b = w
a = i | b = o
This will evaluate similar to sum([1, 1, 1]) where each 1 represents a difference between the two sequences. The if a != b filter causes the generator to only produce a value when a and b differ.
When you say for i in seq1 you are iterating over the characters, not the indexes. You can use enumerate by saying for i, ch in enumerate(seq1) instead.
Or even better, use the standard function zip to go through both sequences at once.
You also have a problem because you return before you print. Probably your return needs to be moved down and unindented.
in your script there are to mistakes
"i" should be integer, not char
"return" should be in function the same level as print, not in cycle "for"
try not to use "print" in such way in functions
here is working version:
def difference (seq1, seq2):
count = 0
for i in range(len(seq1)):
if seq1[i] != seq2[i]:
count += 1
return (count)
So I had to do what you are asking to do and I came up with a very simple solution. Mine is a little different because I check the string to see which is bigger and put them in the correct variable for comparison later. All done with Vanilla python:
#Declare Variables
a='Here is my first string'
b='Here is my second string'
notTheSame=0
count=0
#Check which string is bigger and put the bigger string in C and smaller string in D
if len(a) >= len(b):
c=a
d=b
if len(b) > len(a):
d=a
c=b
#While the counter is less than the length of the longest string, compare each letter.
while count < len(c):
if count == len(d):
break
if c[count] != d[count]:
print(c[count] + " not equal to " + d[count])
notTheSame = notTheSame + 1
else:
print(c[count] + " is equal to " + d[count])
count=count+1
#the below output is a count of all the differences + the difference between the 2 strings
print("Number of Differences: " + str(len(c)-len(d)+notTheSame))
Correct code would be:
def difference(seq1, seq2):
count = 0
for i in range(len(seq1)):
if seq1[i] != seq2[i]:
count += 1
return count
First the return statement is done at the end of the function, therefore it should not be part of the for loop or the for loop would just run once.
Second the for loop wasn't correct because you weren't really telling giving the for loop an integer, therefore the correct code would be to give it a range the length of seq1, so:
for i in range(len(seq1)):

Categories

Resources