简体   繁体   中英

Method fail when I follow Ruby rubocop style guide and use .zero? instead of == 0

I am trying to complete a coding exercise in Ruby, which is as follows:

TODO: starting with an array of integers, return an array with integers and 'Fizz', 'Buzz' or 'FizzBuzz'

Write a method fizz_buzz , which takes a number as an argument, and return an array of number elements from 1 to number , but replaces some of them according to these rules:

  • If the number is divisible by 3 , then replace it by 'Fizz'
  • If the number is divisible by 5 , then replace it by 'Buzz'
  • If the number is divisible by both 3 and 5 , then replace it by 'FizzBuzz' Write

If I follow a rubocop style guide , which we have been asked to use after completed the task, and, as instructed, use .zero instead of == 0? my method fails and I don't get why.

My now failing solution with the edits required by my style guide:

def fizz_buzz(number)
  fail ArgumentError, "#{number} is less than 1" if number < 1
  a = [number]
  while number > 1
    number = number - 1
    a.unshift(number)
    a.map! { |x| (x % 15).zero? ? 'FizzBuzz' : x }
    a.map! { |x| (x % 3).zero? ? 'Fizz' : x }
    a.map! { |x| (x % 5).zero? ? 'Buzz' : x }
  end
  a
end

  should return the array [ 1, 2, 'Fizz' ] for number 3 (FAILED - 1)
  should return the array [ 1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7 ] for number 7 (FAILED - 2)
  should return an array with 'FizzBuzz' at the 15th element of the array (15 is divisible by both 3 and 5) (FAILED - 3)

Failures:

  1) fizz_buzz should return the array [ 1, 2, 'Fizz' ] for number 3
     Failure/Error: a.map! { |x| (x % 5).zero? ? 'Buzz' : x }

 NoMethodError:
   undefined method `zero?' for "Fizz":String
 # ./lib/fizz_buzz.rb:12:in `block in fizz_buzz'
 # ./lib/fizz_buzz.rb:12:in `map!'
 # ./lib/fizz_buzz.rb:12:in `fizz_buzz'
 # ./spec/fizz_buzz_spec.rb:13:in `block (2 levels) in <top (required)>'

  2) fizz_buzz should return the array [ 1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7 ] for number 7
     Failure/Error: a.map! { |x| (x % 5).zero? ? 'Buzz' : x }

 NoMethodError:
   undefined method `zero?' for "Fizz":String
 # ./lib/fizz_buzz.rb:12:in `block in fizz_buzz'
 # ./lib/fizz_buzz.rb:12:in `map!'
 # ./lib/fizz_buzz.rb:12:in `fizz_buzz'
 # ./spec/fizz_buzz_spec.rb:17:in `block (2 levels) in <top (required)>'

  3) fizz_buzz should return an array with 'FizzBuzz' at the 15th element of the array (15 is divisible by both 3 and 5)
     Failure/Error: a.map! { |x| (x % 5).zero? ? 'Buzz' : x }

 NoMethodError:
   undefined method `zero?' for "Fizz":String
 # ./lib/fizz_buzz.rb:12:in `block in fizz_buzz'
 # ./lib/fizz_buzz.rb:12:in `map!'
 # ./lib/fizz_buzz.rb:12:in `fizz_buzz'
 # ./spec/fizz_buzz_spec.rb:21:in `block (2 levels) in <top (required)>'

My working solution which fails the style guide:

def fizz_buzz(number)
  fail ArgumentError, "#{number} is less than 1" if number < 1
  a = [number]
  while number > 1
    number = number - 1
    a.unshift(number)
    a.map! { |x| (x % 15) == 0 ? 'FizzBuzz' : x }
    a.map! { |x| (x % 3) == 0 ? 'Fizz' : x }
    a.map! { |x| (x % 5) == 0 ? 'Buzz' : x }
  end
  a
end

Solution supplied with the task assigned:

# def fizz_buzz(number)
#   fail ArgumentError, "#{number} should be greater than 1" if number < 1
#   (1..number).map do |i|
#     if (i % 3).zero? && (i % 5).zero?
#       'FizzBuzz'
#     elsif (i % 3).zero?
#       'Fizz'
#     elsif (i % 5).zero?
#       'Buzz'
#     else
#       i
#     end
#   end
# end

Now that we see the failing code, here's why it fails:

You have an array a which I suppose you intended to contain the results. You prepend the numbers to it, and fix the numbers that need to say "Fizz" or "Buzz" or "FizzBuzz". This would work, but for one critical failure: you process the array in every iteration of the loop . If you just fixed it once, just before you return it, it would have been fine. However, this way, what happens is (eg for input value 4 ):

  • You start your array with 4
  • You run the fixes, but 4 is good, does not need fixing
  • You prepend 3
  • You run the fixes; the array is now ["Fizz", 4] . So far so good.
  • You prepend 2 ; the array is [2, "Fizz", 4]
  • You run the fixes. 2 passes with no intervention, then it's "Fizz" 's turn.
  • "Fizz" is not a number, so you don't invoke Number#% , the remainder operator, but String#% , the formatting operator. There's no format sigils in the string "Fizz" , so the formatting operator does nothing interesting and returns the string "Fizz" .
  • Then you test if it's zero. When you tried "Fizz" == 0 , it would have been "Duh, no" and just returned false , since all classes (starting with BasicObject ) define #== . But "Fizz".zero? fails, because unlike Number#zero? , String#zero? is not a thing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM