Your translate
method won’t work. The problem is here:
if word[0] == "a" || "e" || "o" || "u" || "i"
and
elsif word[0] != "a" || "e" || "o" || "u" || "i"
You can’t compare that way as the right side of either will not do what you think it will.
Some simple checks will show why there’s something wrong:
'abc'[0] == "a" || "e" || "o" || "u" || "i" # => true
'efg'[0] == "a" || "e" || "o" || "u" || "i" # => "e"
'opq'[0] == "a" || "e" || "o" || "u" || "i" # => "e"
'xyz'[0] == "a" || "e" || "o" || "u" || "i" # => "e"
'abc'[0] != "a" || "e" || "o" || "u" || "i" # => "e"
'efg'[0] != "a" || "e" || "o" || "u" || "i" # => true
'opq'[0] != "a" || "e" || "o" || "u" || "i" # => true
'xyz'[0] != "a" || "e" || "o" || "u" || "i" # => true
Why are those wrong? Let’s look at what’s happening:
When the word starts with ‘a’, the test 'a' == 'a'
is true:
'abc'[0] == "a" # => true
If we ||
(“or”) true and something, we get true back because it was the first “true” value seen:
true || "e" # => true
If the first test failed, then ||
causes the second test to be evaluated, which in your code was "e"
, and wasn’t a test, but Ruby didn’t know that, and thought it was a “true” return value so it became the result of the expression:
false || "e" # => "e"
Knowing that, a correct way to write this would be:
'abc'[0] == "a" || 'abc'[0] == "e" || 'abc'[0] == "o" || 'abc'[0] == "u" || 'abc'[0] == "i" # => true
'efg'[0] == "a" || 'efg'[0] == "e" || 'efg'[0] == "o" || 'efg'[0] == "u" || 'efg'[0] == "i" # => true
'opq'[0] == "a" || 'opq'[0] == "e" || 'opq'[0] == "o" || 'opq'[0] == "u" || 'opq'[0] == "i" # => true
'xyz'[0] == "a" || 'xyz'[0] == "e" || 'xyz'[0] == "o" || 'xyz'[0] == "u" || 'xyz'[0] == "i" # => false
'abc'[0] != "a" && 'abc'[0] != "e" && 'abc'[0] != "o" && 'abc'[0] != "u" && 'abc'[0] != "i" # => false
'efg'[0] != "a" && 'efg'[0] != "e" && 'efg'[0] != "o" && 'efg'[0] != "u" && 'efg'[0] != "i" # => false
'opq'[0] != "a" && 'opq'[0] != "e" && 'opq'[0] != "o" && 'opq'[0] != "u" && 'opq'[0] != "i" # => false
'xyz'[0] != "a" && 'xyz'[0] != "e" && 'xyz'[0] != "o" && 'xyz'[0] != "u" && 'xyz'[0] != "i" # => true
however, that rapidly becomes hard to read and unwieldy, so something more concise is needed:
%w[a e o u].include? 'abc'[0] # => true
%w[a e o u].include? 'efg'[0] # => true
%w[a e o u].include? 'opq'[0] # => true
%w[a e o u].include? 'xyz'[0] # => false
!%w[a e o u].include? 'abc'[0] # => false
!%w[a e o u].include? 'efg'[0] # => false
!%w[a e o u].include? 'opq'[0] # => false
!%w[a e o u].include? 'xyz'[0] # => true
There is a problem with this though; As the array size increases, more loops are required to compare to the [0]
value, which slows the code unnecessarily. A regular expression, written correctly, can get rid of that looping so the speed stays very constant:
'abc'[0][/[aeou]/] # => "a"
'efg'[0][/[aeou]/] # => "e"
'opq'[0][/[aeou]/] # => "o"
'xyz'[0][/[aeou]/] # => nil
Notice though, that instead of getting true/false, the results are the character matched by the pattern or nil. In Ruby, only nil and false are considered false values, and everything else is true, so we can translate those into true, true, true, false respectively, but by taking advantage of the !
operator we can make it even more clear:
!!'abc'[0][/[aeou]/] # => true
!!'efg'[0][/[aeou]/] # => true
!!'opq'[0][/[aeou]/] # => true
!!'xyz'[0][/[aeou]/] # => false
It might seem that we’d have to use !!!
to “not” the results like we’d want when using !=
, but that isn’t necessary. A single !
will do the same thing:
!'abc'[0][/[aeou]/] # => false
!'efg'[0][/[aeou]/] # => false
!'opq'[0][/[aeou]/] # => false
!'xyz'[0][/[aeou]/] # => true
But wait! There’s more! Even that can be improved upon a slight amount by removing the string slice ([0]
) and using a regex anchor. Compare these two, and their benchmark:
require 'fruity'
ALPHABET = ('a'..'z').to_a.join
compare do
slice_it { ALPHABET[0][/[aeou]/] }
regex_it { ALPHABET[/^[aeou]/] }
end
# >> Running each test 8192 times. Test will take about 1 second.
# >> regex_it is faster than slice_it by 39.99999999999999% ± 10.0%
So, using something like:
'abc'[/^[aeou]/] # => "a"
!'abc'[/^[aeou]/] # => false
!!'abc'[/^[aeou]/] # => true
will be fast and compact and let you test to see what a string starts with.
solved Using the ‘pig latin language’ in Ruby [closed]