Pig-latin translator parsing "qu" erratically - ruby

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

Related

Ruby: returning modified string after iteration

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).

pig latin ruby translator issue

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

How do I end the while loop in my tic-tac-toe program?

I wrote a tic-tac-toe program which is user vs user. There is s while loop in the code which makes it run over and over for turns. However, this loop doesn't break when someone wins. Any suggestions?
Here is My code:
class Game
def initialize
#board=Array.new
#board[1]="__|"
#board[2]="__"
#board[3]="|__"
#board[4]="\n__|"
#board[5]="__"
#board[6]="|__"
#board[7]="\n |"
#board[8]=" "
#board[9]="| "
#turn="x"
#win_status = false
end
def win_status
return #win_status
end
def show_board
#board.each do |i|
print i
end
end
def set_turn #switches turns
if #turn == "x"
#turn = "o"
else #turn == "o"
#turn = "x"
end
end
def make_move
puts "Enter x coordinate"
x=gets.to_i
puts "Enter y coordinate"
y=gets.to_i
#board[1]="_"+#turn+"|" if y==1 && x==1
#board[2]="_"+#turn if y==2 && x==1
#board[3]="|_"+#turn if y==3 && x==1
#board[4]="\n_"+#turn+"|" if y==1 && x==2
#board[5]="_"+#turn if y==2 && x==2
#board[6]="|_"+#turn if y==3 && x==2
#board[7]="\n "+#turn+"|" if y==1 && x==3
#board[8]=" "+#turn if y==2 && x==3
#board[9]="|"+#turn+" \n" if y==3 && x==3
end
def win_combo
#win_combo = [*[#board[1][1] + #board[2][1] + #board[3][2]], [#board[4][2] + #board[5][1] + #board[6][2]], [#board[7][1] + #board[8][1] + #board[9][1]],[#board[1][1] + #board[4][2] + #board[7][1]], [#board[2][1] + #board[5][1] + #board[8][1]], [#board[3][2] + #board[6][2] + #board[9][1]], [#board[1][1] + #board[5][1] + #board[9][1]], [#board[3][2] + #board[5][1] + #board[7][1]]]
end
def check_win
#if some row or column or diagonal is "xxx" or "ooo" then set #win_status = true
#win_combo.each do |str|
if str == "xxx" or str == "ooo"
#win_status = true
end
end
puts #win_status
end
end
g = Game.new
while g.win_status != true
g.set_turn
g.make_move
g.show_board
g.win_combo
g.check_win
end
You don't check if you won (check_win) and you don't change #win_combo
while g.win_status != true
#puts g.check_win
g.set_turn
g.make_move
g.show_board
g.win_combo #this line
g.check_win #and this line
end
And in your #win_combo suppose to be array of strings not array of arrays of strings.
For example, your old code:
[["ooo"], ["_xx"], [" "], ["o_ "], ["ox "], ["ox "], ["ox "], ["ox "]]
new one:
["ooo", ["_x_"], [" "], ["o_ "], ["ox "], ["o_ "], ["ox "], ["ox "]]
Just add * ad the beginning(as I did here) or just don't make arrays:
#win_combo = [*[#board[1][1] + #board[2][1] + #board[3][2]], [#board[4][2] + #board[5][1] + #board[6][2]], [#board[7][1] + #board[8][1] + #board[9][1]],[#board[1][1] + #board[4][2] + #board[7][1]], [#board[2][1] + #board[5][1] + #board[8][1]], [#board[3][2] + #board[6][2] + #board[9][1]], [#board[1][1] + #board[5][1] + #board[9][1]], [#board[3][2] + #board[5][1] + #board[7][1]]]
Edit:
Ok, * applied only to the first array, my bad.
This is a correct version.
You can see where I've put () instead of []; By this I made array of strings.
Additionally you had you have checked wrong position on following combinations.
#win_combo = [
(#board[1][1] + #board[2][1] + #board[3][2]), # ok
(#board[4][2] + #board[5][1] + #board[6][2]), # ok
(#board[7][2] + #board[8][1] + #board[9][1]), # this # [7][2] not [7][1]
(#board[1][1] + #board[4][2] + #board[7][2]), # this # [4][2] not [4][1]; [7][2]
(#board[2][1] + #board[5][1] + #board[8][1]), # ok
(#board[3][2] + #board[6][2] + #board[9][1]), # ok
(#board[1][1] + #board[5][1] + #board[9][1]), # ok
(#board[3][2] + #board[5][1] + #board[7][2]) # this [7][2] not [7][1]
]
puts "\n##win_combo\n"
#win_combo
end
ps. puts "\n##win_combo\n" is for debuging your application. Delete it in release version.

Creating a pig latin translator in ruby, having trouble with multiple words

My code works fine for one word, so the searcher Proc works fine. I was trying to get it to work for multiple words by storing them in an array and then passing each word to the proc and putting them together, but when I test something such as "eat pie" it returns this. Can you tell me what I'm doing wrong?
Failures:
1) translate translates two words
Failure/Error: s.should == "eatay iepay"
expected: "eatay iepay"
got: "eat pieay eat pieayay" (using ==)
# ./04_pig_latin/pig_latin_spec.rb:41:in `block (2 levels) in <top (required)>'
Heres my code:
def translate(x)
array = x.split(' ')
searcher = Proc.new{
if x.index(/[aeiou]/) == 0
x = x + "ay"
elsif x[0..1] == "qu"
first = x.chr
x.reverse!.chop!.reverse!
second = x.chr
x.reverse!.chop!.reverse!
x = x + first + second + "ay"
elsif x.index(/[aeiou]/) == 1
first = x.chr
x.reverse!.chop!.reverse!
x = x + first + "ay"
elsif x[1..2] == "qu"
first = x.chr
x.reverse!.chop!.reverse!
second = x.chr
x.reverse!.chop!.reverse!
third = x.chr
x.reverse!.chop!.reverse!
x = x + first + second + third +"ay"
elsif x.index(/[aeiou]/) == 2
first = x.chr.chr
x.reverse!.chop!.reverse!
second = x.chr
x.reverse!.chop!.reverse!
x = x + first + second + "ay"
elsif x.index(/[aeiou]/) == 3
first = x.chr
x.reverse!.chop!.reverse!
second = x.chr
x.reverse!.chop!.reverse!
third = x.chr
x.reverse!.chop!.reverse!
x = x + first + second + third +"ay"
else
x
end
}
if array.count == 1
searcher.call
return x
else
return array.collect(&searcher).join(' ')
end
end
The problem is that you are referring to x in your Proc searcher which is closed over the argument x passed into translate, when what you really mean to do is process each element of the array one at a time.
I altered the structure of your code to be easier to reason about - by eliminating the anonymous Proc and used an idiomatic Ruby map to process the string - whether its one or more words.
#!/usr/bin/env ruby
def translate_word(x)
if x.index(/[aeiou]/) == 0
x = x + "ay"
elsif x[0..1] == "qu"
first = x.chr
x.reverse!.chop!.reverse!
second = x.chr
x.reverse!.chop!.reverse!
x = x + first + second + "ay"
elsif x.index(/[aeiou]/) == 1
first = x.chr
x.reverse!.chop!.reverse!
x = x + first + "ay"
elsif x[1..2] == "qu"
first = x.chr
x.reverse!.chop!.reverse!
second = x.chr
x.reverse!.chop!.reverse!
third = x.chr
x.reverse!.chop!.reverse!
x = x + first + second + third +"ay"
elsif x.index(/[aeiou]/) == 2
first = x.chr.chr
x.reverse!.chop!.reverse!
second = x.chr
x.reverse!.chop!.reverse!
x = x + first + second + "ay"
elsif x.index(/[aeiou]/) == 3
first = x.chr
x.reverse!.chop!.reverse!
second = x.chr
x.reverse!.chop!.reverse!
third = x.chr
x.reverse!.chop!.reverse!
x = x + first + second + third +"ay"
else
x
end
end
words = ARGV[0].split(' ').map { |word| translate_word(word) }.join(' ')
puts words
Verified by chmod +x pig_latin then:
./pig_latin "eat pie"
./pig_latin "eat"

unexpected end kend ruby piglatin program

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.

Resources