Sorry if I'm being really ignorant, I've started learning to code Python recently (first language) and have been working on this task on codewars.com to create a single word pig latin programme. It is pretty messy, but it seems to work aside from the fact that the message:
Traceback:
in
in pig_latin
IndexError: string index out of range
...comes up. I have looked online and I sort of gather it is likely some piece of code that is just out of line or i need a -1 somewhere or something. I was wondering if anyone could help me identify where this would be. It's not helped of course by the fact that I have made this difficult for myself with my inefficiency :P thanks
def pig_latin(s):
word = 'ay'
word2 = 'way'
total=0
total2=0
lst = []
val = None
#rejecting non character strings
for c in s:
if c.isalpha() == False:
return None
#code for no vowels and also code for all consonant strings
for char in s:
if char in 'aeiou':
total+=1
if total==0:
return s + 'ay'
else:
pass
elif char not in 'aeiou':
total2+=1
if total2 == len(s):
answer_for_cons = s + word
return answer_for_cons.lower()
#first character is a vowel
if s[0] in 'aeiou':
return s + word2
#normal rule
elif s[0] not in 'aeiou':
for c in s:
if c in 'aeiou':
lst.append(s.index(c))
lst.sort()
answer = s[lst[0]:len(s)] + str(s[:lst[0]]) + word
return answer.lower()
The only point where an index is implicated is when you call s[0]. Have you maybe tried running pig_latin with an empty string?
Also, the formatting of your code makes no sense. I am assuming it was lost in the pasting? Everything below val = None should be at least one indent further right.
Now that the indentation is fixed, the code seems to run, but it does raise
IndexError: string index out of range
if we pass pig_latin an empty string. That's because of
if s[0] in 'aeiou':
That will fail if s is the empty string because you can't do s[0] on an empty string. s[0] refers to the first char in the string, but an empty string doesn't have a first char. And of course pig_latin returns None if we pass it a string that contains non-alpha characters.
So before you start doing the other tests, you should check that the string isn't empty, and return something appropriate if it is empty. The simplest way to do that is
if not s:
return ''
I suggest returning s or the empty string if you get passed an invalid string, rather than returning None. A function that returns different types depending on the value of the input is a bit messy to work with.
There are various simplifications and improvements that can be made to your code. For example, there's no need to do elif char not in 'aeiou' after you've already done if char in 'aeiou', since if char in 'aeiou' is false then char not in 'aeiou' must be true. However, we can simply that whole section considerably.
Here's your code with a few other improvements. Rather than using index to find the location of the first vowel we can use enumerate to get both the letter and its index at the same time.
def pig_latin(s):
word = 'ay'
word2 = 'way'
#return empty and strings that contain non-alpha chars unchanged
if not s or not s.isalpha():
return s
#code for no vowels
total = 0
for char in s:
if char in 'aeiou':
total += 1
if total == 0:
return s.lower() + word
#first character is a vowel
if s[0] in 'aeiou':
return s.lower() + word2
#normal rule. This will always return before the end of the loop
# because by this point `s` is guaranteed to contain at least one vowel
for i, char in enumerate(s):
if char in 'aeiou':
answer = s[i:] + s[:i] + word
return answer.lower()
# test
data = 'this is a pig latin test string aeiou bcdf 123'
s = ' '.join([pig_latin(w) for w in data.split()])
print(s)
output
isthay isway away igpay atinlay esttay ingstray aeiouway bcdfay 123
Related
I have a assignment where I need to
sometimes it might be useful to convert text from lowerCamelCase to snake_case. The main trick is to find the correct place where to insert an underscore. Let's make a rule that it's right before a capital letter of the next word. If the first letter is capitalized, convert it to lowercase and don't forget to insert an underscore before it.
I wrote my code and for some reason it doesn't return anything, it's completely empty, my ide says I have no errors
word = input()
new_word = ''
for char in word:
if char.isupper():
new_word.join('_' + char.lower())
else:
new_word.join(char)
print(new_word)
The assignment runs multiple tests with different words, and here they are
Sample Input 1:
python
Sample Output 1:
python
Sample Input 2:
parselTongue
Sample Output 2:
parsel_tongue
I legitimately don't see any reason why it's not printing, any ideas why
It's because the 1st test case is all lower case.
new_word will be empty because loop's inner condition won't execute at all.
Here's the correct and cleaner code I wrote
word = input()
counter = 0
new_word = ""
for char in word:
if not(char.islower()) and counter > 0:
new_word = new_word + '_' + char.lower()
else:
new_word = new_word + char.lower()
counter += 1
print(new_word)
You are almost there. You have to concatenate the characters to new_word and not join.
This is Concatenation. char gets appended to new_word:
new_word += char
join() will just concatenate and return the strings passed to it. But it is not saved to new_word.
Use concatenation instead of join in your code.
word = input('Input: ')
new_word = ''
for char in word:
if char.isupper():
new_word += '_' + char.lower()
else:
new_word += char
print(f'Output: {new_word}')
Input: python
Output: python
Input: parselTongue
Output: parsel_tongue
As your title says "list comprehension", here is an approach that utilizes a comprehension:
snake_word = ''.join(f'_{c.lower()}' if c.isupper() else c for c in word)
The function needs to behave as follow:
The first letter occurrence can be upper or lower case (newPhrase).
Non-alpha characters - are left unchanged.
So far I thought of :
def keepFirstLetter(phrase):
'''Returns a new string that contains only the first occurrence of a
letter from the original phrase.
letterSeenSoFar = ''
newPhrase = ''
if (letterSeenSoFar == '' or letterSeenSoFar[-1] != letterSeenSoFar):
letterSeenSoFar += c
for letter in letterSeenSoFar:
if letter.isalpha:
newPhrase += char
else:
newPhrase += letter
return newPhrase
You seem to be on the right track. If you want to improve you efficiency, you can store the seen letters as a set. Searching a set is O(1).
def unique_first(s):
letters = set()
out = ''
for x in s:
if not x.isalpha():
out += x
continue
if not x.lower() in letters:
out += x
letters.add(x.lower())
return out
The most straightforward, sure-to-have-learned solution is probably:
def keepFirstLetter(phrase):
output = ''
for letter in phrase:
if (letter.lower() not in output.lower()) or (not letter.isalpha()):
output += letter
return output
print(keepFirstLetter('Amy says, "Me?"')) # Amy s, "e?"
(the parens around the two if clauses are optional)
James's solution still gets my vote though.
My name was in the question so let me take a try.
I learned set.add() from James for the first time. (Thank you, James.) James' code is shorter and runs faster (3.48us vs. 3.76us on my PC).
def keepFirstLetter(phrase):
phrase = list(phrase)
'''Returns a new string that contains only the first occurrence of a
letter from the original phrase.'''
letterSeenSoFar = []
newPhrase = ''
for char in phrase:
# if char is not an alphabet, add char to the newPhrase as is
if not char.isalpha():
newPhrase += char
# if char is an alphabet and not seen so far, add char to the newPhrase and append it to letterSeenSoFar
elif char.lower() not in letterSeenSoFar:
letterSeenSoFar.append(char.lower())
newPhrase += char
return newPhrase
print(keepFirstLetter('Amy says, " Me?"'))
This outputs:
Amy s, " e?"
So I'm trying to do a code that will shift every letter in a word back by a number of letters in the alphabet (wrapping around for the end). For example, if I want to shift by 2 and I input CBE, I should get AZC. or JOHN into HMFL. I got a code to work for only one letter, and I wonder if there's a way to do a nested for loop for python (that works?)
def move(word, shift):
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
original = ""
for letter in range(26, len(alphabet)):
if alphabet[letter] == word: #this only works if len(word) is 0, I want to be able to iterate over the letters in word.
original += alphabet[letter-shift]
return original
You could start like this
def move(word, shift):
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
return "".join([alphabet[alphabet.find(i)-shift] for i in word])
Basically, this list comprehension constructs a list of the single letters. Then, the index of the letter in the alphabet is found by the .find method. The (index - shift) is the desired new index, which is extracted from alphabet. The resulting list is joined again and returned.
Note that it does obviously not work on lowercase input strings (if you want that use the str.upper method). Actually, the word should only consist of letters present in alphabet. For sentences the approach needs to treat whitespaces differently.
Don't find the letter in the alphabet that way -- find it with an index operation. Let char be the letter in question:
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
...
char_pos = alphabet.index(char)
new_pos = (char_pos - shift) % len(alphabet)
new_char = alphabet[new_pos]
Once you understand this, you can collapse those three lines to a single line.
Now, to make it operate on an entire word ...
new_word = ""
for char in word:
# insert the above logic
new_word += new_char
Can you put all those pieces together?
You'll still need your check to see that char is a letter. Also, if you're interested, you can build a list comprehension for all the translated characters and the apply ''.join() to get your new word.
For instance ...
If the letter is in the alphabet (if char in alphabet), shift the given distance and get the new letter, wrapping around the end if needed (% 26). If it's not a capital letter, then use the original character.
Make a list from all these translations, and then join them into a string. Return that string.
def move(word, shift):
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
return ''.join([alphabet[(alphabet.find(char) - shift) % 26]
if char in alphabet else char
for char in word])
print move("IBM", 1)
print move("The 1812 OVERTURE is COOL!", 13)
Output:
HAL
Ghe 1812 BIREGHER is PBBY!
A_VAL = ord('a')
def move(word, shift):
new_word = ""
for letter in word:
new_letter = ord(letter) - shift
new_word += chr(new_letter) if (new_letter >= A_VAL) else (26 + new_letter)
return new_word
Note that this will only work for lowercase words. As soon as you start mixing upper and lowercase letters you'll need to start checking for them. But this is a start. I discarded your nested loop idea because you should avoid those if at all possible.
You could use : chr() give you the character for a ascii number, ord() give you the ascii number for the matching character.
Here is an old Vigenere project :
def code_vigenere(ch,cle):
text = ch.lower()
clef = cle.lower()
L = len(cle)
res = ''
for i,l in enumerate(text):
res += chr((ord(l) - 97 + ord(cle[i%L]) - 97)%26 +97)
return res
I need to find the first vowel of a string in python, and I'm a beginner. I'm instructed to move the characters before the first vowel to the end of the word and add '-ay'. For example "big" becomes "ig-bay" and "string" becomes "ing-stray" (piglatin, basically).
This is what I have so far:
def convert(s):
ssplit = s.split()
beginning = ""
for char in ssplit:
if char in ('a','e','i','o','u'):
end = ssplit[char:]
strend = str(end)
else:
beginning = beginning + char
return strend + "-" + beginning + "ay"
I need to find a way to stop the "if" statement from looking for further vowels after finding the first vowel - at least I think it's the problem. Thanks!
Break things down one step at a time.
Your first task is to find the first vowel. Let's do that:
def first_vowel(s):
for index, char in enumerate(s):
if char in 'aeiou':
return index
raise Error('No vowel found')
Then you need to use that first vowel to split your word:
def convert(s):
index = first_vowel(s)
return s[index:] + "-" + s[:index] + 'ay'
Then test it:
print(convert('pig'))
print(convert('string'))
Full code, runnable, is here: https://repl.it/Dijj
The exception handling, for words that have no vowels, is left as an exercise.
Add a break where you want the for loop to end: https://docs.python.org/2/tutorial/controlflow.html
Python has the break and continuestatements for loop control. You can set a boolean that you trigger such that:
if flag:
break
#do code
#set flag
You can use a break statement as soon as you found a vowel.
You also do not need to use any split() functions.
One big mistake you did was using char to get the SubString. You need to use the index of that char to get the SubString instead.
Take a look at this:
def convert(s):
beginning = ""
index = 0;
for char in s:
if char in ('a','e','i','o','u'):
end = str(s[index:])
break
else:
beginning = beginning + char
index = index + 1
return str(end) + "-" + beginning + "ay"
Side note. You can use a regex:
>>> import re
>>> cases=['big','string']
>>> for case in cases:
... print case+'=>', re.sub(r'^([^aeiou]*)(\w*)', '\\2-\\1ay', case)
...
big=> ig-bay
string=> ing-stray
I am trying to add text with vowels in certain words (that are not consecutive vowels like ie or ei), for example:
Word: 'weird'
Text to add before vowel: 'ib'
Result: 'wibeird'
Thus the text 'ib' was added before the vowel 'e'. Notice how it didn't replace 'i' with 'ib' because when the vowel is consecutive I don't want it to add text.
However, when I do this:
Word: 'dog'
Text to add before vowel: 'ob'
Result: 'doboog'
Correct Result Should Be: 'dobog'
I've been trying to debug my program but I can't seem to figure out the logic in order to make sure it prints 'wibeird' and 'dobog' correctly.
Here is my code, substitute first_syl with 'ob' and word with 'dog' after you run it first with 'weird.
first_syl = 'ib'
word = 'weird'
vowels = "aeiouAEIOU"
diction = "bcdfghjklmnpqrstvwxyz"
empty_str = ""
word_str = ""
ch_str = ""
first_vowel_count = True
for ch in word:
if ch in diction:
word_str += ch
if ch in vowels and first_vowel_count == True:
empty_str += word_str + first_syl + ch
word_str = ""
first_vowel_count = False
if ch in vowels and first_vowel_count == False:
ch_str = ch
if word[-1] not in vowels:
final_str = empty_str + ch_str + word_str
print (final_str)
I am using Python 3.2.3. Also I don't want to use any imported modules, trying to do this to understand the basics of strings and loops in python.
Have you considered regular expressions?
import re
print (re.sub(r'(?<![aeiou])[aeiou]', r'ib\g<0>', 'weird')) #wibeird
print (re.sub(r'(?<![aeiou])[aeiou]', r'ob\g<0>', 'dog')) #dobog
Never use regex when you don't have to. There's a famous quote that goes
Some people, when confronted with a problem, think
“I know, I'll use regular expressions.” Now they have two problems.
This can easily be solved with basic if-then statements. Here's a commented version explaining the logic being used:
first_syl = 'ib' # the characters to be added
word = 'dOg' # the input word
vowels = "aeiou" # instead of a long list of possibilities, we'll use the
# <string>.lower() func. It returns the lowercase equivalent of a
# string object.
first_vowel_count = True # This will tell us if the iterator is at the first vowel
final_str = "" # The output.
for ch in word:
if ch.lower() not in vowels: # If we're at a consonant,
first_vowel_count = True # the next vowel to appear must be the first in
# the series.
elif first_vowel_count: # So the previous "if" statement was false. We're
# at a vowel. This is also the first vowel in the
# series. This means that before appending the vowel
# to output,
final_str += first_syl # we need to first append the vowel-
# predecessor string, or 'ib' in this case.
first_vowel_count = False # Additionally, any vowels following this one cannot
# be the first in the series.
final_str += ch # Finally, we'll append the input character to the
# output.
print(final_str) # "dibOg"