[MUSIC] In this segment I want to show you a larger example both to put together a lot of what we have already seen in Ruby as well as to show a few additional features just as we go along. So what I have chosen to do is define 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 in ML. 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 things in reduced form. So 3/2 not 9/6. And it will always make sure the denominator is positive. The numerator maybe positive or negative. And then we'll have methods, what we used to have functions for, to add together two rationals and for converting a rational to a string. So without further ado, 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 an enumerator and then 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 one argument to MyRational.new, 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. Ruby has a raise 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 elses. Notice the somewhat unusual, and certainly non-English, spelling of else here. And then if the denominator is less than 0, then I'm 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 the local variables that were the parameters to initialize. Else I initialize them this way. You can put semicolons in but they're are optional when they're on separate lines. And then the last thing I need to do to start the invariance that I have for how I represent fractions is call self.reduce, okay? It's a 0 argument method, so I can put the () 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. You have to just write reduce. So that's initialize. Now once I have a fraction, we need to be able to convert it to a string. And in typical object-oriented style, it has a method, toString, if you will, that we call on a fraction and we get back a string. So fractions know how to convert themselves to strings. In OOP terminology. And in Ruby it's conventional to have such methods be called to_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 + on strings to 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. 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 overkill here. Here's a second version way of converting it to a string. Let's just create a little local variable initially in the empty string. Then let's add to that string via concatenation. Let's change it to slash 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 e1. So some people find this easier to read when it sort of one liner. It's convenient way to just write something on one line. It says, well do this action but only if the dominator is not 1. So there you go. In any case, whatever dens is after this line, we need to concatenate on the front converting the numerator to a string. And then since this is the last expression on our method, that's what will be returned and that's what exactly what we want. One more version of to string. This uses things like Racket's quasiquote and unquote which I explained 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 that another way to do this is to convert the numerator to a string. This will implicitly called it to_s method I believe. Followed by this conditional which is den==1 then ""else"/" 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. Am calling it add! because it's actually going to mutate the object itself. This is somewhat common convention in Ruby. So what I'm going to do is take in another rational and update myself. Whatever object I call add! on will have its numerator and denominator replaced by adding to it the numerator and denominator in R. So what I need to do is get R's numerator and denominator. Now this is perhaps the most interesting part. Because I cannot say something like r.@num because instance variables of r are 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've made them 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 fine, a = r.num, b = r.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 * d) + (b * c). den to be b * d. Call reduce because that fraction might not be reduced. And then I find it convenient to return the object itself. I showed you in 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 be able to do calls like r1.+ r2. But it turns out that in Ruby +, the regular +, is just syntactic sugar for calling the + method on the left argument with the right argument. So by calling this method, +, I will actually be able to just use the addition operator on my rationals. And that's syntactical 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 these objects of about the 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. My enumerator and my denominator and the initialized method above will 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 + 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 calls its 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 this 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. When you define a method outside of an explicit class, it 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/4 puts it in the r1 local variable. Then notice the use of + here. This is actually going to call the + method that we defined. So another way to write this would have been r1.+ with r1 and then on the result of that .+ and then my rational and -5/2 as you see there. And then I just have some printing. So put S r2 to_s so to_ss just prints out its argument. So when I convert this to a string, hopefully I will get, I think minus 1 because I'll have 3/4+3/4 minus 5/2. Then I do some imperative update on r2 where I add to an 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/4. 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 printed out thing. This is the first print where I was just printing, I think it was r2 here, which is minus 1. But then after I imperatively update it once with r1, which is 3/4 and then again with a minus 1/4 that's going to have the overall effect of adding a 1/2 to it. So all my other printing shows three different ways of printing out minus 1/2. And that's our larger example showing a number of new features of Ruby. And also an object-oriented programing style where my rational objects with things with methods that knew how to perform the appropriate computation on the fraction.