Related
I´ve searched for other "string index out of range" cases, but they were not useful for me, so I wanted to search for help here.
The program has to do this: "Write a function kth_word(s, k) that given a string s and an integer k≥ 1 returns the kth word in string s. If s has less than k words it returns the empty string. We assume all characters of s are letters and spaces. Warning: do not use the split string method."
Here is my code:
def kth_word(s, k):
new =""
word_count = 0
for i in range(0, len(s)):
if s[i] == " " and s[i+1] != " ":
word_count+=1
#try to find how many characters to print until the space
if word_count == k-1:
while i!= " " and i<=len(s): #if it is changed to i<len(s), the output is strange and wrong
new+=s[i]
i=i+1
print(new) #check how new is doing, normally works good
return new
print(kth_word('Alea iacta est', 2))
(I tried my best to implement the code in a right way, but i do not know how)
And depending on the place where you live return new it gives or an error or just an empty answer
You iterate from 0 to len(s)-1 in your first for loop, but you're addressing i+1 which, on the last iteration, is len(s).
s[len(s)] is an IndexError -- it is out of bounds.
Additionally your while loop is off-by-one.
while i!= " " and i<=len(s):
# do something referencing s[i]
Your first condition makes no sense (i is a number, how could it be " "?) and your second introduces the same off-by-one error as above, where i is maximally len(s) and s[len(s)] is an error.
Your logic is a bit off here, too, since you're wrapping this inside the for loop which is already referencing i. This appears to be a takewhile loop, but isn't really doing that.
Warning: do not use the split string method.
So groupby / islice from itertools should work:
from itertools import groupby, islice
def kth_word(s, k):
g = (j for i, j in groupby(s, key=lambda x: x==' ') if not i)
return ''.join(next(islice(g, k-1, k), ''))
words = 'Alea iacta est'
res = kth_word(words, 2) # 'est'
We handle StopIteration errors by setting the optional parameter in next to ''.
You're not allowed to use str.split. If you could, the answer would just be:
def kth_word(s, k):
return s.split()[k]
But if you could write a function that does the same thing str.split does, you could call that instead. And that would certainly show that you understand everything the assignment was testing for—how to loop over strings, and do character-by-character operations, and so on.
You can write a version with only the features of Python usually taught in the first week:
def split(s):
words = []
current = ''
for ch in s:
if ch.isspace():
if current:
words.append(current)
current = ''
else:
current += ch
if current:
words.append(current)
return words
If you know additional Python features, you can improve it in a few ways:
Build current as a list instead of a str and ''.join it.
Change those append calls to yield so it splits the string lazily (even better than str.split).
Use str.find or str.index or re.search to find the next space instead of searching character by character.
Abstract out the space-finding part into a general-purpose generator—or, once you realize what you want, find that function in itertools.
Add all of the features we're missing from str.split, like the ability to pass a custom delimiter instead of breaking on any whitespace.
But I think even the basic version—assuming you understand it and can explain how it works—ought to be enough to get an A on the assignment.
And, more importantly, you're practicing the best way to solve problems: reduce them to simpler problems. split is actually easier to write than kth_word, but once you write split, kth_word becomes trivial.
You actually have at least five problems here, and you need to fix all of them.
First, as pointed out by Adam Smith, this is wrong:
for i in range(0, len(s)):
if s[i] == " " and s[i+1] != " ":
This loops with i over all the values up to but not including len(s), which is good, but then, if s[i] is a space, it tries to access s[i+1]. So, if your string ended with a space, you would get an IndexError here.
Second, as ggorlen pointed out in a comment, this is wrong:
while i!= " " and i<=len(s):
new+=s[i[]
When i == len(s), you're going to try to access s[i], which will be an IndexError. In fact, this is the IndexError you're seeing in your example.
You seem to realize that's a problem, but refuse to fix it, based on this comment:
#if it is changed to i<len(s), the output is strange and wrong
Yes, the output is strange and wrong, but that's because fixing this bug means that, instead of an IndexError, you hit the other bugs in your code. It's not causing those bugs.
Next, you need to return new right after doing the inner loop, rather than after the outer loop. Otherwise, you add all of the remaining words rather than just the first one, and you add them over and over, once per character, instead of just adding them once.
You may have been expecting that doing that i=i+1 would affect the loop variable and skip over the rest of the word, but (a) it won't; the next time through the for it just reassigns i to the next value, and (b) that wouldn't help anyway, because you're only advancing i to the next space, not to the end of the string.
Also, you're counting words at the space, but then you're iterating from that space until the next one. Which means (except for the first word) you're going to include that space as part of the word. So, you need to do an i += 1 before the while loop.
Although it would probably be a lot more readable to not try to reuse the same variable i, and also to use for instead of while.
Also, your inner loop should be checking s[i] != " ", not i!=" ". Obviously the index, being a number, will never equal a space character.
Without the previous fix, this would mean you output iacta est
with an extra space before it—but with the previous fix, it means you output nothing instead of iacta.
Once you fix all of these problems, your code works:
def kth_word(s, k):
word_count = 0
for i in range(0, len(s) - 1):
if s[i] == " " and s[i+1] != " ":
word_count+=1
#try to find how many characters to print until the space
if word_count == k-1:
new =""
j = i+1
while j < len(s) and s[j] != " ":
new+=s[j]
j = j+1
print(new) #check how new is doing, normally works good
return new
Well, you still have a problem with the first word, but I'll leave it to you to find and fix that one.
Your use of the variable 'i' in both the for loop and the while loop was causing problems. using a new variable, 'n', for the while loop and changing the condition to n < len(s) fixes the problem. Also, some other parts of your code required changing because either they were pointless or not compatible with more than 2 words. Here is the fully changed code. It is explained further down:
def kth_word(s, k):
new = ""
word_count = 0
n = 0
for i in range(0, len(s) - 1):
if s[i] == " " and s[i + 1] != " ":
word_count += 1
#try to find how many characters to print until the space
if word_count < k:
while n < len(s): #if it is changed to i<len(s), the output is strange and wrong
new+=s[n]
n += 1
print(new) #check how new is doing, normally works good
return new
print(kth_word('Alea iacta est', 2))
Explanation:
As said in Adam Smith's answer, 'i' is a number and will never be equal to ' '. That part of the code was removed because it is always true.
I have changed i = i + 1 to i += 1. It won't make much difference here, but this will help you later when you use longer variable names. It can also be used to append text to strings.
I have also declared 'n' for later use and changed for i in range(0, len(s)): to for i in range(0, len(s) - 1): so that the for loop can't go out of range either.
if word_count == k-1: was changed to if word_count < k: for compatibility for more words, because the former code only went to the while loop when it was up to the second-last word.
And finally, spaces were added for better readability (This will also help you later).
My brain cannot comprehend why this isn't working. I'm not very experienced and just trying to practice loops.
I'm trying to create a function that takes a string (currently one word) and capitalizes letters at random. With this code python throws a TypeError: list indices must be integers or slices, not strings
Here's what I have:
import random
list = []
def hippycase(string):
for letter in string:
list.append(letter)
for index in list:
if random.randint(1,2) == 1:
list[index] = list[index].upper()
else:
list[index] = list[index].lower()
return list
print(hippycase("pineapple"))
Any ideas or tips? Thanks
EDIT: Since this has been marked as a duplicate as someone thinks is at the following link, I'll try and clear up what is different:
Accessing the index in Python 'for' loops
I'm not trying to actively seek the index, I'm just practicing for loops which coincidentally goes through the index of the iterable sequentially. I also think if a fellow noob coder is searching this might be more helpful.
Here's a slightly improved version of your code
def hippycase(string):
charlist = []
for char in string:
if random.randint(1,2) == 1:
charlist.append(char.upper())
else:
charlist.append(char.lower())
return charlist
Notice that in this version we're looking only at the characters in your string, we don't care about the indices - this helps to reduce confusion.
If I were writing this to actually "hippycase" a string I would probably return "".join(charlist), so the calling function would get back a string (which is what they probably expect)
Also, it is bad practice to overwrite the list reserved word.
The variable "index" that you are using is a letter from the string, because you are iterating over it. To fix this error, use the range() function, which will allow you to access each element in the list by index:
list = []
def hippycase(string):
for letter in string:
list.append(letter)
for index in range(len(list)): #here, we are accessing the elements by index
if random.randint(1,2) == 1:
list[index] = list[index].upper()
else:
list[index] = list[index].lower()
return list
print(hippycase("pineapple"))
Another way is simple list comprehension:
the_string = "pineapple"
print ''.join([i.upper() if random.randint(1, 2) == 1 else i for i in the_string])
I'm wondering why my function does not call the index of the character in the string. I used a for loop for this, and for some reason it's just listing all of the possible indices of the string. I made a specific if statement, but I don't get why it's not following the instructions.
def where_is(char,string):
c=0
for char in string:
if char==(string[c]):
print (c)
c+=1
else:
print ("")
where_is('p','apple')
Your loop is overwriting your parameter char. As soon as you enter your loop, it is overwritten with a character from the string, which you then compare to itself. Rename either your parameter or your loop variable. Also, your counter increment c+=1 should also be outside of your if. You want to increase the index whether or not you find a match, otherwise your results are going to be off.
And just as a matter of style, you don't really need that else block, the print call will just give you extra newlines you probably don't want.
Firstly, the index you used is not being increased in the else part and secondly, I generally prefer a while loop to a for loop when it comes to iterating through strings. Making slight modifications to your code, have a look at this :
def where_is(char,string):
i=0
while i<len(string):
if char==(string[i]):
print (i)
else:
print ("")
i+=1
where_is('p','apple')
Input : where_is('p','apple')
Output: 1 2
Check it out here
The problem is that the given code iterated over everything stored in the string and as it matched everytime the value of 'c' increased and got printed.
I think your code should be:
def where_is(char, string):
for i in range(len(string)):
if char == (string[i]):
print(i)
where_is('p', 'apple')
This prints the index of all the 'p's in 'apple'.
As mentioned in the comments, you don't increment correctly in your for loop. You want to loop through the word, incrementing each time and outputting the index when the letter is found:
def where_is(char, word):
current_index = 0
while current_index < len(word):
if char == word[current_index]:
print (current_index)
current_index += 1
where_is('p', 'apple')
Returning:
1
2
Alternatively, by using enumerate and a list comprehension, you could reduce the whole thing down to:
def where_is(char, word):
print [index for index, letter in enumerate(word) if letter == char]
where_is('p', 'apple')
which will print:
[1,2]
You then also have the option of return-ing the list you create, for further processing.
def main():
string = raw_input("string:")
pattern = raw_input("pattern:")
end = len(string)
insertPattern(string,pattern)
def insertPattern(string,pattern):
end= len(string)-1
print "Iterative:",
for x in range(end):
if x == end:
print string[x]
if x < end:
print string[x]+pattern,
main()
I'd like this to output
Instead it's outputting
How would I modify the code to fix this? Assignment requires that I do this without lists or join.
You've got three problems here.
First, the reason you're getting that Iterative: at the beginning is because you explicitly asked for it with this line:
print "Iterative:",
Just take it out.
The reason you're getting spaces after each * is a bit trickier. The print statement's "magic comma" always prints a space. There's no way around that. So, what you have to do is not use the print statement's magic comma.
There are a few options:
Use the more-powerful print function from Python 3.x, which you can borrow in 2.7 with a __future__ statement. You can pass any separator you want to replace the space, even the empty string.
Use sys.stdout.write instead of print; that way you get neither newlines nor spaces unless you write them explicitly.
Build up the string as you go along, and then print the whole thing at the end.
The last one is the most general solution (and also leads to lots of other useful possibilities, like returning or storing the built-up string), so I'll show that:
def insertPattern(string,pattern):
result = ''
end= len(string)-1
for x in range(end):
if x == end:
result += string[x]
if x < end:
result += string[x]+pattern
print result
Finally, the extra * at the end is because x == end can never be true. range(end) gives you all the numbers up to, but not including end.
What you probably wanted was end = len(string), and then if x == end-1.
But you can simplify this quite a bit. The only reason you need x is to get string[x], and to distinguish either the first or last value from the others (so you know not to add an extra * either before the first or after the last). You can solve the last one with a flag, or by just treating the first one special. And then, you can just iterate over string itself, instead of over its indices:
def insertPattern(string,pattern):
result = string[0]
for ch in string[1:]:
result += pattern + ch
print result
And once you've done that, you may realize that this is almost identical to what the str.join method does, so you can just use that:
def insertPattern(string,pattern):
print pattern.join(string)
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have a list and I want to return the first number that out of order,
this is what I've written. and it's not working..
a=input("enter list")
out_of_order(a)
def out_of_order(list):
foundit=0
for i in list:
while foundit==0:
if (i+1 < i):
print(i)
foundit=1
if (foundit==0)
print("none")
for the list [4,8,9,10,2,12,16] It should return 2
You are confusing list items and indices. As it stands, you are testing for:
if i+1 < i:
which will never, ever be True - whatever value i takes, i+1 will be one louder larger.
I think what you were trying to do is compare adjacent items by index:
for i in range(len(lst)):
if lst[i+1] < lst[i]:
(note that you shouldn't use list as your own variable name), but:
this will give you problems if you reach the end of the list (where lst[i+1] will cause an IndexError, so you have to either try or alter the range appropriately); and
it is not generally considered Pythonic to use len(range(...)).
Instead, the best way is to compare pairs of elements:
def out_of_order(lst):
for a, b in zip(lst, lst[1:]):
if b < a:
print(b)
break
else:
print("none")
You also make a few other mistakes:
As #timgeb points out, your input will be strings not integers;
You have a SyntaxError (missing colon, incorrect indentation) at the end; and
You are using an integer as a flag, when Python has perfectly serviceable booleans True and False.
First of all, you are getting a string from input, not a list. You could get a list of numbers from the input like this:
inputstr = input('enter comma separated integers! ')
inputlist = inputstr.split(',')
inputnums = [int(x) for x in inputlist]
Another thing is that you should not use list as a variable name, because that's already used for the builtin list. Now, assuming that your a is a proper list, you want to compare one element of the list with the next element:
def out_of_order(lst):
for i in range(len(lst) - 1):
if lst[i+1] < lst[i]:
print(i+1,lst[i+1])
break
Demo:
enter comma separated integers! 100,101,102,100,101,102
3 100 # output of out_of_order(inputnums)
for i in list:
This iterates through the list by letting i be each element. Naturally, i+1 is never less than i.
I haven't used python in a while, but I believe that when you use 'i' it is referring to a number from the list. For your example in the first iteration i would be 4, in the second i would be 8, and so on. Calling i+1 doesn't return the next number in the sequence, it just adds one to it. When you're saying
if (i+1 < i)
in the first iteration you're pretty much saying if ((4+1) < 4), which is never true.
a=input("enter list")
out_of_order(a)
def out_of_order(list):
foundit=0
for i in range(len(list)):
while foundit==0:
if i < len(list)-1:
if (list[i+1] < list[i]):
print(list[i+1])
foundit=1
else
break;
if (foundit==0):
print("none")