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)
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).
For my programming class, I need to a create a program that takes in a string and two letters as an argument. Whenever the first letter appears in the string, it is replaced with the second letter. I can do this by making the final string into a list. However, our professor has stated that he wants it to be a string, not a list. The code shown below is what I used to make the program work if the final result was to appear in a list.
def str_translate_101(string, x, y):
new_list = []
for i in string:
if i == x:
new_list.append(y)
if i != x:
new_list.append(i)
return new_list
I tried to make one where it would output a string, but it would only return the first letter and the program would stop (which I'm assuming happens because of the "return")
def str_translate_101(string, old, new):
for i in string:
if i == old:
return new
else:
return i
I then tried using the print function, but that didn't help either, as nothing was outputted when I ran the function.
def str_translate_101(string, old, new):
for i in string:
if i == old:
print(new)
else:
print(i)
Any help would be appreciated.
An example of how the result should work when it works is like this:
str_translate_101('abcdcba', 'a', 'x') ---> 'xbcdcbx'
You can use join to merge a list into a string:
def str_translate_101(string, x, y):
new_list = []
for i in string:
if i == x:
new_list.append(y)
else:
new_list.append(i)
return ''.join(new_list)
or use the one-liner
str_tranlsate_101 = str.replace
The simplest solution would be, instead of storing the character in a list you can simply declare an empty string and in the 'if' block append the character to the string using the augmented '+=' operator. E.g.
if i == x:
concat_str += y
As for the return, basically, it will break out of the for loop and return to where the function was called from. This is because it only has 1 objective, which once achieved it will not bother to process any further code and simply go back to where the function was called from.
I am trying to make a reverse function which takes an input (text) and outputs the reversed version. So "Polar" would print raloP.
def reverse(text):
list = []
text = str(text)
x = len(text) - 1
list.append("T" * x)
for i in text:
list.insert(x, i)
x -= 1
print "".join(list)
reverse("Something")
As others have mentioned, Python already provides a couple of ways to reverse a string. The simple way is to use extended slicing: s[::-1] creates a reversed version of string s. Another way is to use the reversed function: ''.join(reversed(s)). But I guess it can be instructive to try implementing it for yourself.
There are several problems with your code.
Firstly,
list = []
You shouldn't use list as a variable name because that shadows the built-in list type. It won't hurt here, but it makes the code confusing, and if you did try to use list() later on in the function it would raise an exception with a cryptic error message.
text = str(text)
is redundant. text is already a string. str(text) returns the original string object, so it doesn't hurt anything, but it's still pointless.
x = len(text) - 1
list.append("T" * x)
You have an off-by-one error here. You really want to fill the list with as many items as are in the original string, this is short by one. Also, this code appends the string as a single item to the list, not as x separate items of one char each.
list.insert(x, i)
The .insert method inserts new items into a list, the subsequent items after the insertion point get moved up to make room. We don't want that, we just want to overwrite the current item at the x position, and we can do that by indexing.
When your code doesn't behave the way you expect it to, it's a Good Idea to add print statements at strategic places to make sure that variables have the value that they're supposed to have. That makes it much easier to find where things are going wrong.
Anyway, here's a repaired version of your code.
def reverse(text):
lst = []
x = len(text)
lst.extend("T" * x)
for i in text:
x -= 1
lst[x] = i
print "".join(lst)
reverse("Something")
output
gnihtemoS
Here's an alternative approach, showing how to do it with .insert:
def reverse(text):
lst = []
for i in text:
lst.insert(0, i)
print "".join(lst)
Finally, instead of using a list we could use string concatenation. However, this approach is less efficient, especially with huge strings, but in modern versions of Python it's not as inefficient as it once was, as the str type has been optimised to handle this fairly common operation.
def reverse(text):
s = ''
for i in text:
s = i + s
print s
BTW, you really should be learning Python 3, Python 2 reaches its official End Of Life in 2020.
You can try :
def reverse(text):
return text[::-1]
print(reverse("Something")) # python 3
print reverse("Something") # python 2
Easier way to do so:
def reverse(text):
rev = ""
i = len(text) - 1
while i > -1:
rev += text[i]
i = i - 1
return rev
print(reverse("Something"))
result: gnihtemoS
You could simply do
print "something"[::-1]
The first part of the question is to check if input A and input B are anagrams, which I can do easily enough.
s = input ("Word 1?")
b = sorted(s)
c = ''.join(b)
t = input("Word 2?")
a = sorted(t)
d = ''.join(b)
if d == c:
print("Anagram!")
else:
print("Not Anagram!")
The problem is the second part of the question - I need to check if two words are anagrams if all of the punctuation is removed, the upper case letters turned to lower case, but the question assumes no spaces are used. So, for example, (ACdB;,.Eo,."kl) and (oadcbE,LK) are anagrams. The question also asks for loops to be used.
s = input ("Word 1?")
s = s.lower()
for i in range (0, len(s)):
if ord(s[i]) < 97 or ord(s[i]) >122:
s = s.replace(s[i], '')
b = sorted(s)
c = ''.join(b)
print(c)
Currently, the above code is saying the string index is out of range.
Here's the loop you need to add, in psuedocode:
s = input ("Word 1?")
s_letters = ''
for letter in s:
if it's punctuation: skip it
else if it's uppercase: add the lowercase version to s_letters
else: add it to s_letters
b = sorted(s_letters)
Except of course that you need to add the same thing for t as well. If you've learned about functions, you will want to write this as a function, and call it twice, instead of copying and pasting it with minor changes.
There are three big problems with your loop. You need to solve all three of these, not just one.
First, s = s.replace(s[i], '') doesn't replace the ith character with a space, it replaces the ith character and every other copy of the same character with a space. That's going to screw up the rest of your loop if there are any duplicates. It's also very slow, because you have to search the entire string over and over again.
The right way to replace the character at a specific index is to use slicing: s = s[:i] + s[i+1:].
Or, you could make this a lot simpler by turning the string into a list of characters (s = list(s)), you can mutate it in-place (del s[i]).
Next, we're going through the loop 6 times, checking s[0], s[1], s[2], s[3], s[4], and s[5]. But somewhere along the way, we're going to remove some of the characters (ideally three of them). So some of those indices will be past the end of the string, which will raise an IndexError. I won't explain how to fix this yet, because it ties directly into the next problem.
Modifying a sequence while you loop over it always breaks your loop.* Imagine starting with s = '123abc'. Let's step through the loop.
i = 0, so you check s[0], which is 1, so you remove it, leaving s = '23abc'.
i = 1, so you check s[1], which is 3, so you remove it, leaving s = '2abc'.
i = 2, so you check s[2], which is b, so you leave it, leaving s = '2abc'.
And so on.
The 2 got moved to s[0] by removing the 1. But you're never going to come back to i = 0 once you've passed it. So, you're never going to check the 2. You can solve this in a few different ways—iterating backward, doing a while instead of an if each time through the for, etc.—but most of those solutions will just exacerbate the previous problem.
The easy way to solve both problems is to just not modify the string while you loop over it. You could do this by, e.g., building up a list of indexes to remove as you go along, then applying that in reverse order.
But a much easier way to do it is to just build up the characters you want to keep as you go along. And that also solves the first problem for your automatically.
So:
new_s = []
for i in range (0, len(s)):
if ord(s[i]) < 97 or ord(s[i]) >122:
pass
else:
new_s.append(s[i])
b = sorted(new_s)
And with that relative minor change, your code works.
While we're at it, there are a few ways you're overcomplicating things.
First, you don't need to do ord(s[i]) < 97; you can just do s[i] < 'a'. This makes things a lot more readable.
But, even more simply, you can just use the isalpha or islower method. (Since you've already converted to lower, and you're only dealing with one character at a time, it doesn't really matter which.) Besides being more readable, and harder to get wrong, this has the advantage of working with non-ASCII characters, like é.
Finally, you almost never want to write a loop like this:
for i in range(len(s)):
That forces you to write s[i] all over the place, when you could have just looped over s in the first place:
for ch in s:
So, putting it all together, here's your code, with the two simple fixes, and the cleanup:
s = input ("Word 1?")
s = s.lower()
new_s = []
for ch in s:
if ch.isalpha():
new_s.append(ch)
b = sorted(new_s)
c = ''.join(b)
print(c)
If you know about comprehensions or higher-order functions, you'll recognize this pattern as exactly what a list comprehension does. So, you can turn the whole 4 lines of code that build new_s into either of these one-liners, which are more readable as well as being shorter:
new_s = (ch for ch in s if ch.isalpha)
new_s = filter(str.isalpha, s)
And in fact, the whole thing can become a one-liner:
b = sorted(ch for ch in s.lower() if ch.isalpha)
But your teacher asked you to use a for statement, so you'd better keep it as a for statement.
* This isn't quite true. If you only modify the part of the sequence after the current index, and you make sure the sequence aways has the right length by the time you get to each index even though it may have had a different length before you did (using a while loop instead of a for loop, to reevaluate len(seq) each time, makes this part trivial instead of hard), then it works. But it's easier to just never do it to than learn the rules and carefully analyze your code to see if you're getting away with it this time.
I have the following problem: I would like to write a function in Python which, given a string, returns a string where every group of two characters is swapped.
For example given "ABCDEF" it returns "BADCFE".
The length of the string would be guaranteed to be an even number.
Can you help me how to do it in Python?
To add another option:
>>> s = 'abcdefghijkl'
>>> ''.join([c[1] + c[0] for c in zip(s[::2], s[1::2])])
'badcfehgjilk'
import re
print re.sub(r'(.)(.)', r'\2\1', "ABCDEF")
from itertools import chain, izip_longest
''.join(chain.from_iterable(izip_longest(s[1::2], s[::2], fillvalue = '')))
You can also use islices instead of regular slices if you have very large strings or just want to avoid the copying.
Works for odd length strings even though that's not a requirement of the question.
While the above solutions do work, there is a very simple solution shall we say in "layman's" terms. Someone still learning python and string's can use the other answers but they don't really understand how they work or what each part of the code is doing without a full explanation by the poster as opposed to "this works". The following executes the swapping of every second character in a string and is easy for beginners to understand how it works.
It is simply iterating through the string (any length) by two's (starting from 0 and finding every second character) and then creating a new string (swapped_pair) by adding the current index + 1 (second character) and then the actual index (first character), e.g., index 1 is put at index 0 and then index 0 is put at index 1 and this repeats through iteration of string.
Also added code to ensure string is of even length as it only works for even length.
string = "abcdefghijklmnopqrstuvwxyz123"
# use this prior to below iteration if string needs to be even but is possibly odd
if len(string) % 2 != 0:
string = string[:-1]
# iteration to swap every second character in string
swapped_pair = ""
for i in range(0, len(string), 2):
swapped_pair += (string[i + 1] + string[i])
# use this after above iteration for any even or odd length of strings
if len(swapped_pair) % 2 != 0:
swapped_adj += swapped_pair[-1]
print(swapped_pair)
badcfehgjilknmporqtsvuxwzy21 # output if the "needs to be even" code used
badcfehgjilknmporqtsvuxwzy213 # output if the "even or odd" code used
Here's a nifty solution:
def swapem (s):
if len(s) < 2: return s
return "%s%s%s"%(s[1], s[0], swapem (s[2:]))
for str in ("", "a", "ab", "abcdefgh", "abcdefghi"):
print "[%s] -> [%s]"%(str, swapem (str))
though possibly not suitable for large strings :-)
Output is:
[] -> []
[a] -> [a]
[ab] -> [ba]
[abcdefgh] -> [badcfehg]
[abcdefghi] -> [badcfehgi]
If you prefer one-liners:
''.join(reduce(lambda x,y: x+y,[[s[1+(x<<1)],s[x<<1]] for x in range(0,len(s)>>1)]))
Here's a another simple solution:
"".join([(s[i:i+2])[::-1]for i in range(0,len(s),2)])