I needed to write a program that converts normal speech into pig latin. I wrote
def translate(*string)
word = []
word_string = []
s = []
i = 0
a = nil
#enters input from user into individual arrays spots
#removes spaces
string[0].scan(/\w+/).each { |x| word << x }
#goes through each word and does operation
while i < word.count
word[i].scan(/./) { |x| word_string << x }
#checks if starts with vowel
if word_string[0].include?("aeiou" || "AEIOU")
word_string = word_string << "ay"
s[i] << word_string.join('')
#checks if starts with Qu or qu
elsif word_string[0] + word_string[1] == "qu" || word_string[0] + word_string[1] == "Qu"
word_string.delete_at(0) && word_string.delete_at(1)
word_string << "quay"
s[i] = word_string.join('')
#checks if starts with 3 consonants
unless (word_string[0] + word_string[1] + word_string[2]).include?("aeiou")
a = word_string[0] + word_string[1] + word_string[2]
word_string.delete_at(0) && word_string.delete_at(1) && word_string.delete_at(2)
word_string << (a + "ay")
s[i] = word_string.join('')
a = nil
#checks if starts with 2 consonants
unless (word_string[0] + word_string[1]).include?("aeiou")
a = word_string[0] + word_string[1]
word_string.delete_at(0) && word_string.delete_at(1)
word_string << (a + "ay")
s[i] = word_string.join('')
a = nil
#check if starts with 1 consonants
else
a = word_string[0]
word_string.delete_at(0)
word_string << (a + "ay")
s[i] = word_string.join('')
a = nil
end
i += 1
end
s.join(" ")
end
It returned to me a error saying
pig_latin.rb:58: syntax error, unexpected $end, expecting kEND
I looked into the error, it means that I either missed a end somewhere or I have one too many, but I am unable to find it. I have a def end, a while end and a if end so the issue isn't there. I thought it might be somewhere in the first few linkes where I wrote the scans to sort the text originally but it doesn't seem like its there either. I need another pair of eyes to take a look, I can't find it. Also if there would be a better way to write this, please let me know.
This is more how the code should look, if it was written in more of a Ruby way:
def translate(string)
pig_latin = []
words = string.split(/\W+/)
words.each do |word|
case word
when /^[aeiou]/i
pig_latin << (word + "ay")
when /^qu/i
word << word[0,2] << 'ay'
pig_latin << word[2 .. -1]
when /^[^aeiou]{3}/i
word << word[0,3] << 'ay'
pig_latin << word[3..-1]
when /^[^aeiou]{2}/i
word << word[0, 2] << 'ay'
pig_latin << word[2 .. -1]
else
word << word[0] << 'ay'
pig_latin << word[1 .. -1]
end
end
pig_latin.join(' ')
end
puts translate('the rain in spain stays mainly on the plain')
=> ethay ainray inay ainspay aysstay ainlymay onay ethay ainplay
I'd have checked for consonants differently.
How it works is left to the reader to figure out. If this was a homework assignment, take the time to figure out how it works, because knowing what it does is important. Copying other people's work... well, this is on the internet now so anyone can search and find it, so don't plagiarize.
Related
def encrypt(string)
alphabet = ("a".."b").to_a
result = ""
idx = 0
while idx < string.length
character = string[idx]
if character == " "
result += " "
else
n = alphabet.index(character)
n_plus = (n + 1) % alphabet.length
result += alphabet[n_plus]
end
idx += 1
end
return result
end
puts encrypt("abc")
puts encrypt("xyz")
I'm trying to get "abc" to print out "bcd" and "xyz" to print "yza". I want to advance the letter forward by 1. Can someone point me to the right direction?
All I had to do was change your alphabet array to go from a to z, not a to b, and it works fine.
def encrypt(string)
alphabet = ("a".."z").to_a
result = ""
idx = 0
while idx < string.length
character = string[idx]
if character == " "
result += " "
else
n = alphabet.index(character)
n_plus = (n + 1) % alphabet.length
result += alphabet[n_plus]
end
idx += 1
end
return result
end
puts encrypt("abc")
puts encrypt("xyz")
Another way to solve the issue, that I think is simpler, personally, is to use String#tr:
ALPHA = ('a'..'z').to_a.join #=> "abcdefghijklmnopqrstuvwxyz"
BMQIB = ('a'..'z').to_a.rotate(1).join #=> "bcdefghijklmnopqrstuvwxyza"
def encrypt(str)
str.tr(ALPHA,BMQIB)
end
def decrypt(str)
str.tr(BMQIB,ALPHA)
end
encrypt('pizza') #=> "qjaab"
decrypt('qjaab') #=> "pizza"
Alternatively if you don't want to take up that memory storing the alphabet you could use character codings and then just use arithmetic operations on them to shift the letters:
def encrypt(string)
result = ""
idx = 0
while idx < string.length
result += (string[idx].ord == 32 ? (string[idx].chr) : (string[idx].ord+1).chr)
idx += 1
end
result
end
Other strange thing about ruby is that you do not need to explicitly return something at the end of the method body. It just returns the last thing by default. This is considered good style amongst ruby folks.
Your question has been answered, so here are a couple of more Ruby-like ways of doing that.
Use String#gsub with a hash
CODE_MAP = ('a'..'z').each_with_object({}) { |c,h| h[c] = c < 'z' ? c.next : 'a' }
#=> {"a"=>"b", "b"=>"c",..., "y"=>"z", "z"=>"a"}
DECODE_MAP = CODE_MAP.invert
#=> {"b"=>"a", "c"=>"b",..., "z"=>"y", "a"=>"z"}
def encrypt(word)
word.gsub(/./, CODE_MAP)
end
def decrypt(word)
word.gsub(/./, DECODE_MAP)
end
encrypt('pizza')
#=> "qjaab"
decrypt('qjaab')
#=> "pizza"
Use String#gsub with Array#rotate
LETTERS = ('a'..'z').to_a
#=> ["a", "b", ..., "z"]
def encrypt(word)
word.gsub(/./) { |c| LETTERS.rotate[LETTERS.index(c)] }
end
def decrypt(word)
word.gsub(/./) { |c| LETTERS.rotate(-1)[LETTERS.index(c)] }
end
encrypt('pizza')
#=> "qjaab"
decrypt('qjaab')
#=> "pizza"
I have written two functions where in one of the the break if array.length == num statements works, while the other does not. Here are the methods and their tests, starting with the working:
def primes(n)
ret = []
return [] if n < 1
for num in 2..+1.0/0.0
ret << num if prime?(num)
break if ret.count == n
end
ret
end
def prime?(num)
(1..num).select {|x| num % x == 0}.count == 2
end
puts "\nPrimes:\n" + "*" * 15 + "\n"
puts primes(0) == []
puts primes(1) == [2]
puts primes(2) == [2,3]
puts primes(6) == [2,3,5,7,11,13]
I'm working on the pig_latin_spec example from The Odin Project and having some difficulty handling special cases in which a word contains "qu" and those characters need to be treated as one phoneme. For example "quiet" => "ietquay" and "square" => "aresquay".
Here's the offending if statement:
if word.include? "qu"
start_index = word.index("q")
prefix = word[0..(start_index+1)] + "ay"
pig_word = word[(start_index+2)..-1] + prefix
end
This produces "ietquay" but not "aresquay". Instead I get "uaresqay". What am I overlooking?
Here's the entire program:
def translate(input)
all_words = []
pig_word = ""
input.split.map do |word, i|
if 'aeiou'.include? word[0]
pig_word = word + "ay"
else
prefix = word[0] + "ay"
pig_word = word[1..-1] + prefix
if word.include? "qu"
start_index = word.index("q")
prefix = word[0..(start_index+1)] + "ay"
pig_word = word[(start_index+2)..-1] + prefix
end
unless 'aeiou'.include? word[1]
prefix = word[0..1] + "ay"
pig_word = word[2..-1] + prefix
unless 'aeiou'.include? word[2]
prefix = word[0..2] + "ay"
pig_word = word[3..-1] + prefix
end
end
end
all_words << pig_word
end
"#{[all_words].join(" ")}"
end
Your special-casing for "qu" is being run, but then replaced by the logic in the unless with this condition:
'aeiou'.include? word[1]
because the condition is false for word = "square" (because "q" is not in "aeiou").
A quick patch/fix is to use if...elsif to make the two competing conditional blocks mutually exclusive, and when the word contains "qu" it will not check further.
if word.include? "qu"
start_index = word.index("q")
prefix = word[0..(start_index+1)] + "ay"
pig_word = word[(start_index+2)..-1] + prefix
elsif ! 'aeiou'.include? word[1]
prefix = word[0..1] + "ay"
pig_word = word[2..-1] + prefix
unless 'aeiou'.include? word[2]
prefix = word[0..2] + "ay"
pig_word = word[3..-1] + prefix
end
end
Note this may not lead to a perfect translator, it just fixes the issue you have brought here as a question.
Implementing Neil's suggested edit, here is the fully-functional code:
def translate(input)
all_words = []
pig_word = ""
input.split.map do |word, i|
first_vowel = word.index(/[aeiou]/) #dat first vowel occurence
if first_vowel == 0 #words that begin with a vowel
pig_word = word + "ay"
elsif word.include? "qu" #words that don't begin with a vowel but include "qu".
first_vowel = word.index(/[aeio]/) #first vowel not counting "u"
start_index = word.index("q")
prefix = word[0..(first_vowel-1)] + "ay"
pig_word = word[first_vowel..-1] + prefix
end
if first_vowel >= 1 #words that don't begin with vowels or have "qu"
prefix = word[0..first_vowel-1] + "ay"
pig_word = word[first_vowel..-1] + prefix
end
all_words << pig_word
end
"#{[all_words].join(" ")}"
end
So ive been trying to write the pig latin method with its different instances. However, the join method at the end simply joins the original words of the string as opposed to the modified words that have gone through the iteration:
def translate(string)
alphabet = ("a".."z").to_a
vowels = %w{a e i o u}
consonant = alphabet - vowels
words = string.split
words.each do |word|
if vowels.include?(word[0])
word = word + "ay"
elsif word[0..2] == "sch"
word = word[3..-1] + word[0..2] + "ay"
elsif word[0..1] == "qu"
word = word[2..-1] + word[0..1] + "ay"
elsif word[1..2] == "qu"
word = word[3..-1] + word[0..2] + "ay"
elsif consonant.include?(word[0]) && consonant.include?(word[1]) && consonant.include?(word[2])
word = "#{word[3..-1]}#{word[0..2]}ay"
elsif consonant.include?(word[0]) && consonant.include?(word[1])
word = "#{word[2..-1]}#{word[0..1]}ay"
elsif consonant.include?(word[0])
word = "#{word[1..-1]}#{word[0]}ay"
end
end
p words.join(" ")
end
translate("apple pie")
translate("cherry")
translate("school")
translate("square")
translate("three")
translate("apple")
translate("cat")
This is what it gives me when it runs:
"apple pie"
"cherry"
"school"
"square"
"three"
"apple"
"cat"
Try using map instead of each
Like so:
def translate(string)
alphabet = ("a".."z").to_a
vowels = %w{a e i o u}
consonant = alphabet - vowels
words = string.split
result = words.map do |word|
if vowels.include?(word[0])
word = word + "ay"
elsif word[0..2] == "sch"
word = word[3..-1] + word[0..2] + "ay"
elsif word[0..1] == "qu"
word = word[2..-1] + word[0..1] + "ay"
elsif word[1..2] == "qu"
word = word[3..-1] + word[0..2] + "ay"
elsif consonant.include?(word[0]) && consonant.include?(word[1]) && consonant.include?(word[2])
word = "#{word[3..-1]}#{word[0..2]}ay"
elsif consonant.include?(word[0]) && consonant.include?(word[1])
word = "#{word[2..-1]}#{word[0..1]}ay"
elsif consonant.include?(word[0])
word = "#{word[1..-1]}#{word[0]}ay"
end
word
end
p result.join(" ")
end
translate("apple pie")
translate("cherry")
translate("school")
translate("square")
translate("three")
translate("apple")
translate("cat")
I suggest reading this answer Array#each vs. Array#map
because it will shed some light on the difference between map and each. Your each block is returning the original words array, so that is why it was never changed.
Using regular expressions and a case statement, you can compact it quite a bit:
def pig_latin(word)
case word
when /^[aeiou]/ #starts with a vowel
word + "ay"
when /^([s?qu|[^aeiou]{1,3})/ #starts with qu, squ or 1-3 consonants
rep = $1
word.sub(rep, "") + "#{rep}ay"
end
end
def translate(string)
string.split.map { |word| pig_latin(word) }.join " "
end
resulting in the following tests being all true:
puts translate("apple pie") == "appleay iepay"
puts translate("cherry") == "errychay"
puts translate("school") == "oolschay"
puts translate("square") == "aresquay"
puts translate("three") == "eethray"
puts translate("apple") == "appleay"
puts translate("cat") == "atcay"
Note that I simplified the second regular expression with the assumption that the only consonant that can precede "qu" at the beginning of a word is "s" (which is the case according to /usr/share/dict/words).
Hello I'm having trouble with my pig latin code, I'm working on the Learn First files from Ruby, what I basically wanted to do was the following, if a word began with a vowel=> apple would become appleay, if it began with a consonant => banana => ananabay, if it began with two consonants => cherry => errychay, it can also translate two words => eat pie would become eatay iepay and so on and so forth, here is my code:
def translate(string)
vowels = [ "a" , "e" , "i" , "o" , "u"]
alphabet = ("a" .. "z").to_a
consonants = alphabet - vowels
string_split = string.split
string_split.map! do |w|
if vowels.include?(w[0])
w + 'ay'
elsif consonants.include?(w[0]) &&
consonants.include?(w[1])
w [2..-1] + w [0..1]+ 'ay'
elsif w [0..1] == "qu"
w[2..-1] + "quay"
elsif w[0..2] == "thr"
w [3..-1]+"thray"
elsif w[0..2]== "sch"
w[3..-1]+"schay"
elsif consonants.include?(w[0])
w[ 1..-1] + w[0..0] + 'ay'
else
w
end
end
return string_split.join(" ")
end
very simple, it's elsif not elseif
def translate(string)
vowels = [ "a" , "e" , "i" , "o" , "u"]
alphabet = ("a" .. "z").to_a
consonants = alphabet - vowels
string_split = string.split
string_split.map! do |w|
if vowels.include?(w[0])
w + 'ay'
elsif consonants.include?(w[0]) &&
consonants.include?(w[1])
w [2..-1] + w [0..1]+ 'ay'
elsif w [0..1] == "qu"
w[2..-1] + "quay"
elsif w[0..2] == "thr"
w [3..-1]+"thray"
elsif w[0..2]== "sch"
w[3..-1]+"schay"
else consonants.include?(w[0])
w[ 1..-1] + w[0..0] + 'ay'
end
end
return string_split.join(" ")
end