SDGGD
SDGGD
SDGGD
In this segment I want to talk about two other classes that are found in the Ruby standard library, hashes and ranges. They're both similar to arrays in many ways and they're very common in Ruby programs. So it's worth showing them. But much more interesting, from the concepts of programming languages is how conceive, how they interact with duck typing to provide something that's very much like functional programming and the benefits of high order functions. So hashes are a lot like arrays, except the keys can be anything, so I like to think of an array as mapping from keys to values where the keys are numbers. The, the keys 2 returns the value that's in index 2, and the key 4 returns the thing that's in index 4. In hashes, you do have keys and values, but the keys can be anything and it's common for them to be strings or symbols, but they can be any object at all. As a result, there's no natural ordering. We, we think of the elements of an array as sorted, two, three, four, five. But, with hashes, since the keys can literally be anything. We give that up for the flexibility of having any sort of key and there's naturally different syntax for creating hashes. But then, once we have them, a lot of the same methods works including bracket syntax for getting something out or, or setting something in it. So you can also think of a hash as really a record where the field names can be anything and the field contents can be anything. And so, we often use hashes where we need a little data structure with a bunch of pieces where we want indicative names for the pieces. For example, if you needed to pass a lot of configurations options to a method, often, that method will take those arguments as part of a single hash value. So before moving on to ranges, how about I show you some of that, because that was a lot of verbiage without showing you an example. So, you can build an empty hash with just brace, brace. You could also do this with hash.new, same thing in this case. And then so h1 now has currently has
nothing in it, but I could put something in it, like arrays, you can just assign to any position. But here, I can make the key a string, and then I could map that to some other string like Found A. No need for these things to be the same type, and of course, I mean h1 and not hw1. Okay? I could also have in the same hash, something indexed by false, a totally different object. And now, if I print h1, you'll see it prints as a mapping embraces. The key string a goes to the string Found A and the key false goes to the string Found false. And it prints with these fat arrows, equal or greater than sign. Okay? Like arrays, you could ask for indices that don't exist and you just get back the nil object. Unlike arrays, it has some methods defined that are not defined on arrays like keys, which returns an array of all the keys currently in the hash, and similarly values, which returns an array of all the values currently in the hash. So it's just a mapping from keys to values. Just quickly, there's some syntax for just building these things directly. Let me hit return here so I could, if I can type this out well enough, create a little mapping where I have three strings for keys and three numbers for values. Okay? And I get this little mapping and I could ask, you know, SML and I would get I don't know why I keep thinking it's HW. There we go. I could also ask h2.size and I would get 3, there are three mappings in that hash. You might imagine that various iterators are defined on hashes, but each takes a pair of a key and a value. Alright. So that's a difference, but it's a different class. It defines that each method,, method differently, so, you know, maybe I could do something like print out a key value pair in some reasonably nice ways. And on h2, that would print them out one at, oh, one per line as you see here. Okay, so that's hashes. It's actually common sometimes to use symbols, which we would write :foo instead
of strings like foo for the keys that's a more efficient approach, but you do see both in Ruby programs and that's hashes. Now, let's go back to the slides and talk about ranges. It's the other one I wanted to show you. So ranges act a whole lot like a rays of contiguous numbers, but they're more efficiently represented. So for example, just here I could make the range one to a million, and that was very fast. And this is not actually some huge object that's an array with a million elements, one, two, three, four up to a million. It would be, if I called it to_a method, which creates an array out of a range. But just the range is, is just this little object that acts like the contiguous numbers from one to a million. It's just an object. You can think of it as having a lower bound and an upper bound, something that would have something like two instance variables if we knew how it was implemented instead of a million. But like arrays, a lot of things are defined on it, a lot of methods are defined on it. So for example, if I wanted to add up all the number from 1 to 100, I could call inject, which you might remember is Ruby's name for reduce and I'll get the sum of the numbers from one to 100, Which is actually 5,050. Okay? So that's kind of neat. And now, we just have three different things arrays, hashes, and ranges. And we can talk about when is it better style to use one or another. So in short, use ranges when you can, because they're more efficient and they express better. I mean the numbers from 2 to 10 or from x to y where x and y are variables that we look up to create numbers. And use hashes when numeric keys make your code too hard to understand, that when you really do have a mapping from some kind of set of strings to some other values or something like that, then a hash is the right thing to use. Okay? So to finish this up, let's talk about how we have these three things that do have some different methods, like we saw hashes have a keys method and the values method, but they also have a lot of the same methods.
So in particular, most of the methods that arrays have ranges also have. So it turns out this is particularly nice for duck typing OOP style programming and for the benefits of higher order functions. So let me show you a quick example of that. Suppose I define a method foo that takes in sum a. Now, when I defined foo, I was really thinking of a as an array. That's why I called it a and I call its count method and because I want to count in this array. How many values x have x squared less than 50? You could have whatever sort of arithmetic property you wanted to have in there. And sure enough, if I call foo with an array like 3, 5, 7, 9 I will get, I think 3, because 3 squared is less than 55 squared, less than 50 and 7 squared is less than 50 and that's great. But because ranges also have a count method which takes in a block and works the same way I could just as well call foo with the range 3 to 9. And if I do, I think I'll get back 5, because 3, 4, 5, 6, and 7 are all squared less than 50, but 8 and 9 are not. This is really duck typing. I can call foo with any method that has a count, any object, excuse me, that has a count method that can take a block and do an appropriate thing with it. And in terms of functional programming, this is what I emphasize back when we studied ML, high order functions for iterators are really good at. It's a separation of concerns, one part of your code can implement the code that does the iteration that knows how to iterate over some data and another piece of your code can use that iterator to compute something useful. So here are computing something useful as foo and we have two things that know how to iterate. We have the count method in the array class, which presumably walks down the array one element at a time, and we have the count method in the range class, which does something different. Which does not actually have the entire array, but just goes through the numbers from the lower bound up to the higher bound. So this is the same separation of concern I emphasized in ML, now seen in an
object-oriented programming context. So I always like to see the same concepts come up in different settings. That's why I wanted to show you ranges here before we move on to discuss subclassing.