Using the 'pig latin language' in Ruby [cl

2019-09-21 05:48发布

I am struggling finding a way to verify these methods and was wondering if anyone knew a basic way to do this?

class PigLatinTest < MiniTest::Unit::TestCase

  def test_word_beginning_with_a
    assert_equal "appleay", PigLatin.translate("apple")
  end

  def test_other_word_beginning_e
    assert_equal "earay", PigLatin.translate("ear")
  end

  def test_word_beginning_with_p
    assert_equal "igpay", PigLatin.translate("pig")
  end

For example the first one might be:

module PigLatin

  class Word
    def initialize(word)
        @word = word.to_s
    end
    # remember to use the .to_s method

    def translate(word)
        if word[0] == "a" || "e" || "o" || "u" || "i"
            word = word + "ay"
        elsif word[0] != "a" || "e" || "o" || "u" || "i"
            word = word-word[0]+"ay"
        end
    end
  end

  # you can add method here even outside of the class ...
end

------------in another file

module PigLatin

  class Word

    # remember to use the .to_s method

  end

  # you can add method here even outside of the class ...
end

标签: ruby methods
1条回答
Ridiculous、
2楼-- · 2019-09-21 06:24

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.

查看更多
登录 后发表回答