Imagine you wanted to write a small program that calculates the length of the hypotenuse of a right-angled triangle. It might look something as follows:

1 #! /usr/bin/ruby

2

3 # Calculate the hypotenuse of a right-angled triangle.

4

5 **abort** "You should pass the lengths of the catheti" **unless** ARGV.size == 2

6

7 a, b = ARGV.shift.to_f, ARGV.shift.to_f

8 hyp = **Math**::sqrt(a ** 2 + b ** 2)

9 puts "The length of the hypotenuse is #{hyp}."

Notice something? Line 8 doesn’t look so nice. Wouldn’t it be nice if we could do a (a.sq + b.sq).sqrt instead? Well, you can.

**Adding Numeric#square**

The first thing we are going to do is add a method called Numeric#square. Since I am lazy I will also create alias it as Numeric#sqr. Here is a little test program (note that I added a Numeric#cube method as well, since I don’t have anything better to do with my time):

1 #! /usr/bin/ruby

2

3 # Calculate squares and cubes.

4

5 class **Numeric**

6 def square

7 **return** self ** 2

8 end

9 alias :sqr :square

10

11 def cube

12 **return** self ** 3

13 end

14 end

15

16 **abort** "You have to pass in some numbers." **if** ARGV.empty?

17

18 ARGV.each **do** |n|

19 **begin**

20 num = n =~ /\./ ? Float(n) : Integer(n)

21 puts "#{num}² = #{num.sqr}, #{num}³ = #{num.cube} (#{num.class})"

22 **rescue**

23 puts "Skipping #{n} because it doesn’t smell like a number.."

24 **next**

25 **end**

26 **end**

Now line 20 looks highly suspect. What it does is convert the string n to either a floating point number (Float) or an integer (Fixnum) depending on whether or not it finds a decimal point. I’m just doing this to demonstrate that Numeric#square is available to both Fixnum and Float objects. That’s why we chose to modify Numeric.

If you wonder how to get the ² character in your editor, in insert mode, hit CTRL+K followed by 2S. That is, if you are using vim. If you are using another editor, chances are you have to upgrade to the Professional Enterprise++ version of your software.

Running the program above on some numbers I get:

% ruby sc.rb 2 4 5.2 -2

2² = 4, 2³ = 8 (Fixnum)

4² = 16, 4³ = 64 (Fixnum)

5.2² = 27.04, 5.2³ = 140.608 (Float)

-2² = 4, -2³ = -8 (Fixnum)

**Adding Numeric#sqrt**

Adding Numeric#sqrt is similar. Let’s put it to the test!

1 #! /usr/bin/ruby

2

3 # Calculate square roots.

4

5 class **Numeric**

6 def sqrt

7 require ‘complex‘ **if** self < 0

8 **Math**::sqrt(self)

9 end

10 end

11

12 **abort** "I need some numbers to work with." **if** ARGV.empty?

13

14 ARGV.each **do** |arg|

15 **begin**

16 num = Float(arg)

17 puts "√#{num} = #{num.sqrt}"

18 **rescue**

19 puts "You know, #{arg} is a weird number.."

20 **next**

21 **end**

22 **end**

This program contains one extra detail, which you can find on line 7. If the number is negative then we bring in the complex module before the call to Math::sqrt. That way it seamlessly uses complex numbers as and when they are needed. Now you may or may not think this is a good idea, but whatever the case may be, Ruby allows you to do whatever you think is right.

Testing this on a few numbers, I get:

% ruby sr.rb 100 -4.0 2.3 -64

√100.0 = 10.0

√-4.0 = 2.0i

√2.3 = 1.51657508881031

√-64.0 = 8.0i

**Back to our right-angled triangle**

Okay, now that we have tested that all the details work as expected, we can get back to our program, which was all about calculating the hypotenuse of a right-angled triangle. Here is our new version of that program:

1 #! /usr/bin/ruby

2

3 # Calculate the hypotenuse of a right-angled triangle.

4

5 class **Numeric**

6 def sq

7 self ** 2

8 end

9

10 def sqrt

11 **Math**::sqrt self

12 end

13 end

14

15 **abort** "Please pass in the lengths of the catheti." **unless** ARGV.size == 2

16

17 a, b = ARGV.shift.to_f, ARGV.shift.to_f

18 hyp = (a.sq + b.sq).sqrt

19 puts "The length of the hypotenuse is #{hyp}."

I’ve cut out things from the Numeric class that we don’t need for our program, such as Numeric#cube and requiring complex, since we cannot get a negative number from the sum of two numbers squared. Also, I got extra lazy and shortened the square method to Numeric#sq (which seems to make sense, since square meters is abbreviated sq m).

Our second version of the hypotenuse calculator is a whole lot longer, 19 lines instead of 9 lines. But wasn’t it worth it, just to clean up that single ugly Math::sqrt(a ** 2 + b ** 2) expression? 😀

## Leave a Reply