Methods Exercises - Expert Solution

UPDATED: originally had a question regarding running code in Atom, but was making a simple save error. Now have a question on expert problem.

Hi there Dillon! Would you mind posting a screenshot of the error you’re getting when you run bundle exec rspec ? That would probably help me (or, realistically, someone way more qualified than me :wink: ) diagnose what’s going on.

careless error with saving by me, thanks for the response though @paolospeirn Paolo! I tried to remove the post but doesn’t look like I can, so I suppose I’ll change it to ask another question. On the last problem (the expert one), I solved it a different way at first:

def int_remainder_without_modulo(i_dividend, i_divisor)
  quotient = i_dividend / i_divisor
  i_dividend - (quotient * i_divisor).round
end

This solves the first two tests, however for the third it throws this error:

  int_remainder_without_modulo
    knows 8 mod 3 is 2
    knows 5 mod 6 is 5
    doesn't use the % operator (FAILED - 1)

Failures:

  1) methods.rb int_remainder_without_modulo doesn't use the % operator
     Failure/Error: i_dividend - (quotient * i_divisor)
       #<Double "Integer"> received unexpected message :- with (2)
     # ./lib/methods.rb:108:in `int_remainder_without_modulo'
     # ./spec/methods_spec.rb:144:in `block (3 levels) in <top (required)>'

Finished in 0.01838 seconds (files took 0.1471 seconds to load)
27 examples, 1 failure

Failed examples:

rspec ./spec/methods_spec.rb:141 # methods.rb int_remainder_without_modulo doesn't use the % operator

which I’m having trouble deciphering. It sounds like it thinks I’m using a modulo in my code, but I’m not sure. Let me know if you encountered/solved this.

Thanks!

1 Like

@dtluther: Hello Dillon! This is a really interesting question, so I decided to do some debugging.
I think your solution is valid, there is nothing wrong with it. However, I just worked my way out to pass the rspec.
You don’t have to follow my instruction, but I’ll show you how I would debug your code and fix it.

  • So you passed the first two test, but fail the last one

Let’s look at the Rspec file :

describe "int_remainder_without_modulo" do
    it "knows 8 mod 3 is 2" do
      expect(int_remainder_without_modulo(8, 3)).to be(2)
    end

    it "knows 5 mod 6 is 5" do
      expect(int_remainder_without_modulo(5, 6)).to eq(5)
    end

    it "doesn't use the % operator" do
      a = double("Integer", :/ => 1, :to_f => 2.0)
      expect(a).not_to receive(:%)
      int_remainder_without_modulo(a, 2)
    end
  end
  • If you look at the 2 first tests (ignore the 3rd test) : it’s passing 8 and 3 (1st test), 5 and 6 (2nd test)
  • And the parameters in the int_remainder_without_modulo method are i_dividend and i_divisor
  • So it tells us that the type of i_dividend and i_divisor is Integer
  • Let’s do some debugging
def int_remainder_without_modulo(i_dividend, i_divisor)
   quotient = i_dividend / i_divisor
   byebug # <---------- Bye Bug is right HERE
   i_dividend.to_f.round - (quotient * i_divisor)
end

Now look at the 3rd test: “doesn’t use the % operator”

  • And let’s debug the Rspec file (not method.rb file). Remember to remove the “byebug” line in method.rb file, and run spec on the 3rd test (line 142)
it "doesn't use the % operator" do
      a = double("Integer", :/ => 1, :to_f => 2.0)
      expect(a).not_to receive(:%)
      byebug # <---------- Bye Bug is right HERE
      int_remainder_without_modulo(a, 2)
end

  • You will see that a is passing to i_dividend and 2 is passing to i_divisor.
  • Well, we know for sure that i_divisor is Integer type because number 2 is Integer
  • But what about a? Well, it’s a Double type. REALLY?
  • You can learn learn about Integer, Float, Double, … . Just google : primitive data types.
  • You also see that what number is divisible by a , it still give us a same result 1
    • Ex: a/2 = a/123 = a/456 = a/2.35 = 1
    • Because a has a key (word) “/”, and “/” 's value is 1 --> a/(any number) = 1 (Integer type)
  • It tells us that at this line: int_remainder_without_modulo(a, 2), instead of passing a 2, you pass anything you want like: (a,123) because a/(any nunber) always give us 1
  • Usually in Ruby, division with a Float type will give you a Float type. In this case, it’s a test
  • Read more about RSpec Mock::Double
    –> So it’s a fake “Double”

Now look at your code and let’s do some debugging on 3rd test

  • Remember to remove “byebug” line in the Rspec file, and only run rspec for the 3 test
def int_remainder_without_modulo(i_dividend, i_divisor)
   quotient = i_dividend / i_divisor
   byebug # <---- Bye Bug is right HERE
   i_dividend - (quotient * i_divisor).round
end

  • Well this is what we kinda expect. i_dividend.class’s type is Double, because that “a” in the 3rd test (Rspec file), its type is Double (fake Double), and “a” is passing to the parameter i_dividend.
  • However, we passed first 2 tests, but failed this 3rd test. The reason is the first 2 tests, i_dividend received Integer types, and the 3rd test, i_dividend received this fake Double type.
  • Also, you see that quotient is Integer type, and its value is 1.
  • To guarantee that your code will pass the rspec, your code should able to handle the case for that fake Double type
# Assume the arguments are integers.
  • I’m so sorry for whoever wrote this test, when I look at the rspec file, I just didn’t trust it :slight_smile:
  • And I’m sorry, this may be the bug in rspec.
  • I think the test is meant to test only for “not to use % operator”,and they give us a hint to use the helper function
  • Well, you can figure it our how to use the hint
  • But, it’s more fun to fix your code :smiley:
# HINT: Use dec_remainder_of_two_integers as a helper method

How would we fix this problem?

  • Well, a few things we should know about Ruby are:
    • Variables don’t have type
    • Everything decimal number are Float type by default including Double (it’s different in other languages like Java, C,…)
    • So Float, Double are the same thing, just call it Float
  • To fix this, it’s pretty easy, all you have to do is convert this fake Double to Float
def int_remainder_without_modulo(i_dividend, i_divisor)
   quotient = i_dividend / i_divisor
   i_dividend.to_f - (quotient * i_divisor).round
end

Pass the 3rd test, but fail the 1st test


  • So it tells us that we need to handle the case for both Integer and that fake Double
  • So How would we fix this?
    • convert i_dividend to float and convert it to Integer.
    • You can use “round” method to round a number and convert to Integer type

All Tests Passed

def int_remainder_without_modulo(i_dividend, i_divisor)
   quotient = i_dividend / i_divisor
   i_dividend.to_f.round - (quotient * i_divisor)
end

  • Btw, you may notice that I remove the round method for (quotient * i_divisor)
  • Because we know that quotient and i_divisor are always Integer type:
    • First 2 tests: passing 2 Integer numbers --> quotient = i_dividend / i_divisor is Integer
    • 3rd Test: passing a fake Double type “a”, and whenever a/(any number) = quotient = i_dividend / i_diviso always give us a number 1 which is an Integer type.

I hope this help you.
Enjoy coding

@paolospeirn: Hey classmate, see you in two weeks :blush: