Python : Made code, doesn't know why it works - python

I made code that reverses a string but I don't know why it works. Would greatly appreciate if someone could help explain in detail.
str = 'hello'
newstr = ''
count = -1
for i in range (len(str)):
newstr += str[count]
count = count - 1
print (newstr)

Execute your script in a Python debugger as follows:
$ python -m pdb script.py
and use (s) and variable names to understand how does it work.

This program works because you are reading the str from the last element to the first. Your count variable points to a given element and decreases its value. In Python, negative indices mean reading from the last element.

stri = 'hello' # Make string stri
newstr = '' # Make string newstr
count = -1 # make int count
for i in range (len(stri)): # setting i to 0,1,2,3,4 -> this is a loop
newstr += stri[count] # Add letter to newstr from last letter to first
count = count - 1 # making count go down
print (newstr) # print "olleh"
Dont use str as variable name, because it's a build in function

It's quite simple if you take it one bite at a time. In python, setting count = -1 means that you want to take the last item in your string ( which can be seen as an array of characters). Your for i in range (len(str)):' means that you are looping based on the length of your string. The line newstr += str[count] will add the last letter of the string to an empty string. ( so if the word was "apple", it would ad an 'e' to newstr ). finally, your count variable will decrement each time the loop is performed, which will allow you reverse your string.
Example:
myStr = "apple"
count = len(myStr) // length is = 5
myStr[count] // this means that it will access the fifth letter
count = count - 1
myStr[count] // now it is accessing the 4th letter

Related

Is there a problem with my string replacement code?

string = input()
for i in range(len(string)):
if i % 3 == 0:
final = string.replace(string[i], "")
print(final)
I was asked the question: "Given a string, delete all its characters whose indices are divisible by 3."
The answer for the input Python is yton. However, my code gives Pyton.
The code makes sense to me but I'm a beginner. Any help?
The problem is that while you are looping, you are overriding the final variable every time the index is divisible by 3.
Instead, try defining the final variable before you start the loop, and add the letters as you loop over them, and only when they their index is NOT divisible by 3 (thus, ignoring the ones where the index IS divisible by 3).
Something like this should work:
string = input()
final = ""
for i in range(len(string)):
if i % 3 != 0:
final += string[i]
print(final)
In your current code, final is used through each iteration of the loop. It continues updating by replacing one character. In each iteration, final is replaced by a different string with one letter from string removed. After the loop has completed, it effectively only replaced one letter, which in this case is "h".
Use this instead (thanks to Mateen Ulhaq for the idea):
print("".join(x for i, x in enumerate(input()) if i % 3 != 0))
string=input()
final=""
for i in range(len(string)):
if i % 3 != 0:
final+=string[i]
print(final)
In your code, the line final = string.replace(string[i], "") would run like this.
Supposing the input is "hellobaby":
i=0, final="ellobaby"
i=3, final="helobaby"
i=6, final="hellobby"

Cannot remove two vowels in a row

I have to enter a string, remove all spaces and print the string without vowels. I also have to print a string of all the removed vowels.
I have gotten very close to this goal, but for some reason when I try to remove all the vowels it will not remove two vowels in a row. Why is this? Please give answers for this specific block of code, as solutions have helped me solve the challenge but not my specific problem
# first define our function
def disemvowel(words):
# separate the sentence into separate letters in a list
no_v = list(words.lower().replace(" ", ""))
print no_v
# create an empty list for all vowels
v = []
# assign the number 0 to a
a = 0
for l in no_v:
# if a letter in the list is a vowel:
if l == "a" or l == "e" or l == "i" or l == "o" or l == "u":
# add it to the vowel list
v.append(l)
#print v
# delete it from the original list with a
del no_v[a]
print no_v
# increment a by 1, in order to keep a's position in the list moving
else:
a += 1
# print both lists with all spaces removed, joined together
print "".join(no_v)
print "".join(v)
disemvowel(raw_input(""))
Mistakes
So there are a lot of other, and perhaps better approaches to solve this problem. But as you mentioned I just discuss your failures or what you can do better.
1. Make a list of input word
There are a lot of thins you could do better
no_v = list(words.lower().replace(" ", ""))
You don't replaces all spaces cause of " " -> " " so just use this instead
no_v = list(words.lower().translate( None, string.whitespace))
2. Replace for loop with while loop
Because if you delete an element of the list the for l in no_v: will go to the next position. But because of the deletion you need the same position, to remove all the vowels in no_v and put them in v.
while a < len(no_v):
l = no_v[a]
3. Return the values
Cause it's a function don't print the values just return them. In this case replace the print no_v print v and just return and print them.
return (no_v,v) # returning both lists as tuple
4. Not a mistake but be prepared for python 3.x
Just try to use always print("Have a nice day") instead of print "Have a nice day"
Your Algorithm without the mistakes
Your algorithm now looks like this
import string
def disemvowel(words):
no_v = list(words.lower().translate( None, string.whitespace))
v = []
a = 0
while a < len(no_v):
l = no_v[a]
if l == "a" or l == "e" or l == "i" or l == "o" or l == "u":
v.append(l)
del no_v[a]
else:
a += 1
return ("".join(no_v),"".join(v))
print(disemvowel("Stackoverflow is cool !"))
Output
For the sentence Stackoverflow is cool !\n it outputs
('stckvrflwscl!', 'aoeoioo')
How I would do this in python
Not asked but I give you a solution I would probably use. Cause it has something to do with string replacement, or matching I would just use regex.
def myDisemvowel(words):
words = words.lower().translate( None, string.whitespace)
nv = re.sub("[aeiou]*","", words)
v = re.sub("[^a^e^i^o^u]*","", words)
return (nv, v)
print(myDisemvowel("Stackoverflow is cool !\n"))
I use just a regular expression and for the nv string I just replace all voewls with and empty string. For the vowel string I just replace the group of all non vowels with an empty string. If you write this compact, you could solve this with 2 lines of code (Just returning the replacement)
Output
For the sentence Stackoverflow is cool !\n it outputs
('stckvrflwscl!', 'aoeoioo')
You are modifying no_v while iterating through it. It'd be a lot simpler just to make two new lists, one with vowels and one without.
Another option is to convert it to a while loop:
while a < len(no_v):
l = no_v[a]
This way you have just a single variable tracking your place in no_v instead of the two you currently have.
For educational purposes, this all can be made significantly less cumbersome.
def devowel(input_str, vowels="aeiou"):
filtered_chars = [char for char in input_str
if char.lower() not in vowels and not char.isspace()]
return ''.join(filtered_chars)
assert devowel('big BOOM') == 'bgBM'
To help you learn, do the following:
Define a function that returns True if a particular character has to be removed.
Using that function, loop through the characters of the input string and only leave eligible characters.
In the above, avoid using indexes and len(), instead iterate over characters, as in for char in input_str:.
Learn about list comprehensions.
(Bonus points:) Read about the filter function.

A program to find number of occurrences of a sub-string in a string without using library function

So far I have reached here, but program always returns 1.
I don't want to use library function. Please help!
Since, I'm a beginner I want the code to be as simple as possible. Thanks!
str = input("Enter The String: ")
substr = input("Enter The Sub-String: ")
l = len(str)
count = 0
es = ''
for i in range (l):
es += str[i]
if es == substr:
if str[i+1] != ' ':
count = 0
else:
count += 1
es = ''
print (count)
Firstly, don't call your own variable str; it masks a Python built-in.
Secondly, you are iterating over the wrong value, the length of the substring not the string.
Thirdly, your logic is completely wrong, you build the input string up to the length of the substring, testing each time if it is the substring, and reset count if you reach a space.
Instead, try:
count = 0
for i in range(len(string)):
if string[i:i+len(substring)] == substring:
count += 1
print(count)
This moves a "window" of the same length as the substring along the string, counting 1 for each match.
Better yet, as JAB suggsts, just use count!
If you want it as simple as possible, you should use the built-in functionality Python provides. And I would not recommend using str as a variable name as that's one of the built-in types; it works because str is not a reserved keyword, but hiding the str class may not be a good idea.
s = input("Enter The String: ")
sub_s = input("Enter The Sub-String: ")
print(s.count(sub_s))
From the docstring for str.count:
S.count(sub[, start[, end]]) -> int
Return the number of non-overlapping occurrences of substring sub in string S[start:end]. Optional arguments start and end are interpreted as in slice notation.

noob python question, extract letter from list

not sure why this isn't working.
error I'm getting: ord() expected a character, but string of length 0 found
code:
phrase = 'test'
number = 0
text = 0
random1 = ''
while (number <= len(phrase)):
letter = phrase[number:number+1]
text = ord(letter) - ord('a')
....
number = number + 1
if a print letter, i get the t for the first iteration
thanks,
You are failing on your last iteration. Because you let number take the value len(phrase), you are trying to slice your string beyond the end.
For example:
>>> "abc"[3:4]
''
String indices range from 0 to len(s)-1.
BTW: Python gives you much nicer ways to iterate over strings:
phrase = 'test'
for letter in phrase:
text = ord(letter) - ord('a')
....
You are iterating past the end of phrase, try this:
phrase = 'test'
number = 0
text = 0
random1 = ''
while (number < len(phrase)):
letter = phrase[number:number+1]
text = ord(letter) - ord('a')
Note the:
while (number < len(phrase)):
You do indeed have to show more code. What is i? Are you incrementing it each loop?
Typically, if you ARE incrementing i to act just like a for loop, you want to loop such that number < len(phrase), not <=.
Also, you can replace
letter = phrase[number:number+1]
with
letter = phrase[number]
Use for..in instead to process each letter in your string:
Code:
phrase = 'test'
for letter in phrase:
print letter
Output:
t
e
s
t
This would be the more "pythonic" way of doing character iteration. This way you're guaranteed to hit every letter without having to consider hitting the end, as what's happening in your code.
You want a char and not a string, right? Try to replace your
letter = phrase[number:number+1]
with
letter = phrase[number]

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