0:00

In this segment, I want to show you a larger example, both to put together a lot

of what we've already seen in Ruby as well as to show a few additional features just

as we go along. So, what I've chosen to do is to find a

small class called MyRational that is actually a lot like the rational modules

for fractions that we saw back when we studied the module system NML.

I'm calling the class MyRational because it turns out that the current version of

Ruby has wonderful built-in support for fractions that actually already uses a

class called Rational. But we're going to build our own for

purpose of an example. And as I mentioned, we're going to see a

number of new features as we go along just to show you that Ruby is a large language.

And that I can't show you all the different kinds of useful expressions

there are. And to encourage you to learn more on your

own if you're interested. Before I just get into the code and show

you what I have, let me remind you a little bit about how the rationals we

studied in ML work because this is a very similar piece of code that lets you see

the same thing in a different programming style.

So, we will have numerators and denominators.

Our little library, our class is always going to keep thing in reduced form, so 3

over two, not 9 over 6, and it will always make sure the denominator is positive.

The numerator may be positive or negative and then we'll have methods, what we used

to have functions for, to add together rationals and for converting a rational to

a string. So without further do, let's just go over

to the code and look at what I have. So I have a class MyRational, so instances

of this class will represent fractions. I have an initialization method here.

It takes in a numerator, and this is kind of cute, I'll let the denominator be

optional. So that you can have a default of 1.

So, that if you just pass 1 argument to MyRational dot new then presumably, you

want a whole number. Now, I do need some logic in here.

The denominator is 0, that's an error, I don't allow 0 denominators in my

fractions. So Ruby has a raised primitive that takes

a string and that does something like exceptions and errors that we've seen in

other languages. Here is an elsif so nested if then elsif

notice the unusual and non English spelling of else here.

And then if the denominator is less than 0, then I am going to have two statements

here, two expressions where I initialize two instance variables num and den to be

negative numerator and negative denominator.

Right here, these are you know, the local variables that were the parameters to

initialize. Else, I initialize them this way.

You can put semicolons in, but they're optional when they're on separate lines.

And then, the last thing I need to do to start the en-variance that I have for how

I represent fractions is called self.reduce.

Okay, it's a 0 argument method, so I can put the parentheses in if I want, but they

are optional. And because it turns out it's a private

method, I'll show it to you down below. You can't write self dot, you have to just

write, reduce. So, that's initialize.

Now, once I have a fraction, it, we need to be able to convert it to a string, and

in typical object-oriented style, it has a method to string, if you will, that, that

we call on a fraction and we get back a string.

So, fractions know how to convert themselves to string.

In sort of OOP terminology, and in Ruby, it's conventional to have such methods to

be called to underscore s. Many, many classes have this method

defined and it's the standard way so that anything that knows how to convert itself

to a string should implement this method. Now, there are many ways we could

implement this. I'm actually going to show you a few of

them. But here's a good first way, so to_s here

starts by taking whatever is in the numerator instance variable and calling

its to_s. It turns out that numbers have a to_s

method and this will get us the string representation of the numerator.

Then, if the denominator is not 1, then let's concatenate so we can use plus on

strings that concatenate to the right, a slash character, and then continue to

concatenate the denominator converted to a string.

If the denominator is 1, then we'll just not do any of this, and when we're all

done with that, we'll return ans, which will be a string, which is what we want.

So this works fine let me show you two other ways just to show you some other

Ruby features that are perhaps a little bit of over kill here.

Here's a second version that can way of converting it to string.

Let's just create a little local variable, initially the empty string.

Then, let's add to that string via concatenation let's change it to /

concatenated with to_s of the denominator, but only if the denominator is minus 1.

Now, this is somewhat strange if you haven't seen this in scripting languages,

it's fairly popular in these languages. It's just a different kind of conditional,

that's written e1 if e2, and it reads somewhat backwards, this says if e2 is

true then do e 1. So some people find this easier to when

it's sort of a one liner. It's a convenient way to just write

something on one line that says, will do this action but only if the denominator is

1, so there you go. In any case whatever dens is after this

line, we need to concatenate on the front converting the numerator into a string and

since this is the last expression in our method that's what will be returned and

that's exactly what we want. One more version of two string, this uses

things like Racket's quasi quote and unquote which I explain in an optional

segment at the end of that unit. Basically I'm just going to return a

string, but inside of double quotes, whenever you have hash and then brace, up

to the matching brace, what that does is evaluate this expression in here and then

convert it to a string. And so it turns out another way to do this

is to convert the numerator to a string, this will implicitly call its to_s method

I believe. Followed by this conditional which is if

the denominator equals 1 then no, nothing else slash concatenated on.

If you're interested in this, it's generally called interpolation, expression

interpolation in Ruby. And you can learn more of it on your own

we won't have much need for it. So that's converting to strings.

Now let's see something more interesting. Let's write a version of add, I'm calling

it addbay because it's actually going to mutate the object itself.

This is a somewhat common convention in Ruby.

So what I'm going to do is take in another rational and update myself.

Whatever object I call addbay on, will have its numerator denominator replaced by

adding to it. The numerator and the denominator in r.

So what I need to do is get r's numerator and denominator.

Now this is the most interesting part. Because I can not say something like

r.@num, because instance variables of r are a private to r that is a different

object I can't do that. So r is going to have to provide some

methods for me to get to numerator and denominator.

So those are defined below on my rational and I made them And protected.

Because they're protected, this code, which is part of the same class, even

though they're not the same object, will be able to access them, even though they

are not fully public methods. So far in, A equals R dot num, B equals

dot den. I could then let C and D be my numerator

and denominator. Then a little bit of arithmetic to update

via mutation. Num to be a times d plus b times c, den to

be b times d. Call reduce, because that fraction might

not be reduced, and then I find it convenient to return the object itself.

I showed you an earlier example where that turns out to be convenient and I'll show

you a use of this sort of method where that's convenient in just a minute.

So that's an imperative addition. If we wanted a functional addition, we

could do that using our imperative addition.

So here's how that might work. How about I define a method called plus?

This will actually be quite nice. So, I'll able to do calls like r1 dot plus

r2. But it turns out that in Ruby, plus the

regular plus is just syntactic sugar for calling the plus method on the left

argument with the right argument. So by calling this method plus I will

actually be able to just use the addition operator on my rationals.

And that's kind of syntactically cute as well.

So here's how I will do this. What I will do is I will make a local

variable ans, that's a new rational that holds this objects val, same values as

numerator and denominator. So this is just making a copy, if you

will, where I'm just passing to the new thing.

The my numerator and my denominator and the initialized method above we'll do the

correct thing for that new rational. Now given that new rational let's go ahead

and imperatively add to it r and then just return that new rational.

So plus always returns a new fraction, it never updates.

This object's numerator and denominator field.

Okay, so we've seen most of it here. Here is our protected getter methods for

numerator and denominator. The comment here, points out that we could

have used the shorter syntax of doing something like attr reader.

But I find these a little easier to read, so fine.

And then I just haven't shown you reduce yet.

So, here is reduce, it's just the same logic we had before.

It causes helper function gcd for greatest common divisor, it passes the absolute

value of the numerator. And numbers all have this method abs

define on them, so that works fine. And then gcd which is right here, we've

seen a few times in the course, recursion works just fine in Ruby, so this gcd

method is just calling itself. This is a self.gcd, but you can't write

the self because it's private on itself to do the right calculation to compute the

greatest common divisor of an x and a y. So that is our class.

Now let me just show you a little top-level method here.

We need to find a method outside of a an explicit class that just gets put in the

object class. And this method just creates and uses some

rationals. So what does it do, it creates a rational

3 over 4. Puts it in the r1 local variable.

Then notice the use of plus here, this is actually going to call the plus method

that we defined. So, another way to write this would have

been r1 dot plus with r1 and then, the result of that dot plus and then

MyRational or minus 5 over 2. As you see there, and then I just have

some printings so put s r2 to_s. So put s just puts out its argument so

when I convert this to a string hopefully I will get, I think minus 1.

Because I'll have 3 4ths plus 3 4ths minus 5 halves.

Then I do some imperative update on r2, where I add to it imperatively r1 and then

take that result, so it turns out this returns the same object that r2 refers to,

adds to it imperatively again minus 1 4th and then I try out all my different

printing methods on that result. And so sure enough if I just come over

here quickly, if I load example.rb, I just get two back just as I successfully loaded

because use rationals is a method and I haven't called that method yet.

And if I call it, I will see the appropriately print printed out thing.

This is the first print where I was just printing I think it was r, yes r2 here

which is minus 1. But then after I imperatively update it

once with r1 which is 3 4th and then again with a minus 1 4th, that's going to have

the overall effect of adding a half to it. So, all my other printing shows three

different ways of printing out minus 1 over 2.

And that's our larger example, showing a number of new features of Ruby and also an

object-oriented programming style, where my rational objects were things with

methods that knew how to perform the appropriate computation on the fraction.