my program loops indefinitely when assigning a specific string to a variable and I can´t figure out why

66 views Asked by At

I am trying to learn Ruby with the Test-First material and I´m stuck at this point. This is my code:

def translate(word)
  word=word.split("")
  while (!vowel(word[0])) do 
    first_letter=word[0]
    word.each_index do |i|
        word[i]=word[i+1]
    end
    word[word.length-1]=first_letter
  end

  return word.join + "ay"
end

def vowel(letter)
  if letter == ("a" || "e" || "i" || "o" || "u")
    return true
  end
end

The goal is to implement a function that translates a word to "pig latin" by looking if the word starts with a vowel. If this is the case, the function just appends "ay". If it´s beginning with a consonant, it puts the first letter at the end of the word until there is a vowel in the first position.

The problem I have starts when assigning the first letter, which I saved at the start of the while loop, to the last position of the word , with

word[word.length-1]=first_letter

This causes the program to loop indefinitely.

The test is based on the string "banana", it should read "ananabay" after processing. In this case, first_letter contains "b". So I tested assigning different letters to the last position of word, everything but "b" works out fine. What I don´t get is that with the while block not depending on the last letter, but on the first letter of the word, it still causes an indefinite loop assigning "b" to word[word.length-1].

I would be very thankful for some insight.

1

There are 1 answers

2
Wand Maker On

Note that

letter == ("a" || "e" || "i" || "o" || "u")

is equivalent to

letter == "a"

as ("a" || "e" || "i" || "o" || "u") will evaluate to first truthy value which happens to be "a" in this case.

What you need is

(letter == "a") || (letter == "e") || (letter == "i") || (letter == "o") || (letter == "u")

which can also be written as:

["a","e","i","o","u"].include?(letter)

or

%w(a e i o u).include?(letter)

or

"aeiou".chars.include?(letter)

Due to above mentioned logic error, the vowel method will return nil whenever letter is not "a", and if there is a word that does not contain vowel a, then, the while loop never terminates. Hence, it needs to be corrected to something like below. Also, while we are at it, lets add ? to method name to indicate that it will return a boolean value.

def vowel?(letter)
  "aeiou".chars.include?(letter)
end

Moving the first character of the string to end of string can also be simplified by using code like below:

word = word[1..-1].concat(word[0])

Lets extract this logic into a new method:

def rotate(word)
    word[1..-1].concat(word[0])
end

You can also simplify the while loop by switching to until expression.

word = rotate(word) until vowel?(word.chars.first)

Hence, your translate method can be simplified to something like below:

def translate(word)
  word = rotate(word) until vowel?(word.chars.first)
  word.concat("ay")
end

There is still one issue, what happens to words that do not have any vowels, the loop will be infinite loop. We need to fix it

def translate(word)
  if word.chars.any? { |i| vowel?(i) }  
    word = rotate(word) until vowel?(word.chars.first)
  end
  word.concat("ay")
end

Complete code:

def translate(word)
  if word.chars.any? { |i| vowel?(i) }  
    word = rotate(word) until vowel?(word.chars.first)
  end
  word.concat("ay")
end

def rotate(word)
    word[1..-1].concat(word[0])
end

def vowel?(letter)
  "aeiou".chars.include?(letter)
end


p translate("banana")
#=> "ananabay"

p ["pig", "banana", "trash", "happy", "duck", "glove"].map(&method(:translate))
#=> ["igpay", "ananabay", "ashtray", "appyhay", "uckday", "oveglay"]