Ben Simpson Ben Simpson - 3 years ago 112
Ruby Question

for-loop or while-loop doesn't work on third and fourth iteration

I'm working on a Ruby assignment that tasks you with building a small, test-driven program that translates some words and phrases into pig latin.

First, let me post the code I've got so far.

#method to determine if a vowel is a letter
def is_vowel(letter)
vowels = ['a', 'e', 'i', 'o', 'u']
if vowels.include?(letter)

def cut_and_paste(consonants, word)
word = "#{word}#{consonants}ay"
word.sub!(consonants, '')

def translate(input)
consonants = ''
words = input.split(" ")
new_words = []
for n in 0...words.length
if words[n][0..2] == 'squ'
#handles words beginning with 'squ'
consonants = 'squ'
new_words << cut_and_paste(consonants, words[n])
elsif words[n][0..1] == 'qu'
#handles words beginning with 'qu'
consonants = 'qu'
new_words << cut_and_paste(consonants, words[n])
elsif is_vowel(words[n][0])
#handles words beginning with a vowel
new_words << words[n] + 'ay'
i = 0
#check each letter until vowel
while is_vowel(words[n][i]) == false
consonants = "#{consonants}#{words[n][i]}"
i += 1
#removes consonants at beginning of word, moves them to the end of word, and adds 'ay.'
new_words << cut_and_paste(consonants, words[n])
new_words = new_words.join(" ")
return new_words

The code works fine for single words, but when the input is:

"the quick brown fox"

the return comes back as

"ethay ickquay brownay foxay"

For some reason, the code can handle the first "multi-consonant" word, but when it goes back through the loop, something is going wrong. I'm unsure if it is an issue with the for-loop or the while-loop, but since I got the same error in a previous verion that didn't use a for-loop, I suspect the while.

Since I set the value of i inside the loop, I though it would reset to 0 for each new word, and I don't see any reason it wouldn't.

I previously used a
loop with the same result.

If it helps, the specific example that fails is:

it "translates many words" do
s = translate("the quick brown fox")
expect(s).to eq("ethay ickquay ownbray oxfay")

Here's the error I get when testing:

translates a word beginning with a vowel
translates a word beginning with a consonant
translates a word beginning with two consonants
translates two words
translates a word beginning with three consonants
counts 'sch' as a single phoneme
counts 'qu' as a single phoneme
counts 'qu' as a consonant even when it's preceded by a consonant
translates many words (FAILED - 1)


1) #translate translates many words
Failure/Error: expect(s).to eq("ethay ickquay ownbray oxfay")

expected: "ethay ickquay ownbray oxfay"
got: "ethay ickquay brownay foxay"

(compared using ==)
# ./04_pig_latin/pig_latin_spec.rb:65:in `block (2 levels) in <top (required)>'

Finished in 0.0193 seconds (files took 0.12399 seconds to load)
9 examples, 1 failure

Failed examples:

rspec ./04_pig_latin/pig_latin_spec.rb:63 # #translate translates many words

Answer Source

Plenty to talk about here, but you'll find it much easier to follow your own logic if you can break things down into smaller pieces.

The translate method is supposed to translate a series of words into pig Latin. Each word can be treated independently, so let's start by doing that. Let's write translate to take a phrase, break it into words, send each word off to another method to be translated, and then join the result into a translated string:

def translate(input) { |word| translate_word(word) }.join(' ')

That's the whole method! The real power here is in the Enumerable#map method. That will take any array, pass each element to the supplied block, and return a new array with the resulting elements.

Now you just need to write the translate_word method, which will take as its only argument an untranslated word, and will return a translated word:

def translate_word(word)
  ...single-word translation logic here...
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download