Tip:
Highlight text to annotate it
X
So, I'll show you some loops and then I'll tell you that I don't want you to think
about them as loops and then I'll tell you why. Most of them look pretty innocuous
and even if you haven't seen Ruby you can kinda figure out that says, Oh, I see. For
each of these array values, I'm going to pass it to this block, this procedure, as
the parameter string, and I'm just gonna print it out. That's pretty
straightforward. Well that looks pretty innocuous. I've seen loops before. That
looks like a range of numbers and the variable is available inside the loop.
Okay, I, I understand that. Now this one's a little more interesting, one up to ten.
Well that's pretty cute. And then inside the loop I can get the value of it as num.
Okay, so these all kind of look like, okay, here's another one. I can print rah,
rah, rah. Three dot times. And, by the way, remember if you've been reading the
book that braces are also in, in some cases an acceptable, acceptable substitute
for DO END. So if you have an entire block or a function that fits on one line,
sometimes you'll see it surrounded by braces. So these braces in the last
example could be do and end instead of braces, that's what they stand for in this
context. Again, if in doubt, use do and end, that's perfectly fine. Okay, so these
all kind of look like loops, but in fact they're really all manifestations of a
single underlying mechanism that's considerably more general. And the way
that I usually sum this up is, if you find yourself writing explicit iteration with
an index, like a for I loop, you're probably doing something wrong. Because
the idiom in Ruby is to use integrators that allow objects to manage their own
traversal. So again, if you're coming from the Java world, this is going to seem a
little bit inside out. We'll do a direct comparison in a moment. The examples that
we just saw, one of them was about traversing a range. So I can take a range
and say, for each item in that range, grab the item and do some stuff to it. I did
[inaudible] which has a starting point and an ending point. That was another way to
do it. I could also have an array and say each element in the array I'll grab and do
something with that element. I can take hashes so I can do each key in a hash or I
can factor each pair, a key and a value together, which means that, inside my
block, inside the body of my due, I'll be able to have both the key and the value
passed to me. And I did this really simple, zero [inaudible] iterator which
just said some number of times do something. So what do all these things
have in common? They all have in common the inside out iteration that Ruby. Does a
little bit differently from Java. In each case, we're taking an object on the left
like a range or an array or a hash and we are asking it via the method each to
manage traversal of its elements and hand us the elements one at a time. In the case
of E pair, we're actually getting a pair of things, a key and its corresponding
value one at a time. But this is kind of different from the Java idiom where you
say initialize and integrate to our map thing, and while the interater has the
next thing, give me the next thing. Now do some stuff and check again. And in a
minute we'll see why this is a more general mechanism that you can subvert for
your own nefarious purposes. So some people call this expression orientation in
Ruby, which means that. I can now take collections of things. Like this array, I
can call sort on it. I can call unique, which removes duplicates. And I can call
reverse on the result of that. So I'm sort of chaining these expressions together.
And remember, none of these are actually modifying X. Right? X dot sort creates a
new copy of X, and sorts it. X dot unique dot reverse creates a copy here, and
another copy here, and returns that. So there are actually all, non-destructive
methods. Unless I use the dangerous method. X reverse! ***! That actually
will reverse X in place. Right? And these are examples of methods where there's both
a safe version, and a destructive and unsafe version. There's Map, just like
there is in Scheme and Python and other languages. I can take an array like x and
for each element, a Fruit, I can call reverse on the Fruit. Now in a minute
you'll say, wait a minute. Up here you were calling reverse on an array, but here
you're calling reverse on what, strings? Yes, in fact we're gonna get to that in a
moment. That is the magic of duck typing. And we'll get that. You try to pronounce
those words. We can collect. Meaning select out the elements that match a
criteria. And we could say. Every, for every element in x manager your own
traversal. Hand me elements one at a time. And, I'll just keep the ones that include
the letter e. And I'll get back a new collection from this. So again, if you
took the classic version of 61 a with scheme. This actually is a very familiar
idiom. And by the way. Remember when I said earlier on that even though things
have classes, classes aren't that important, it's all about what do you
respond to? That's the reason that reverse works on individual strings inside the
array as well as on the array itself, right? So the Pavlovian bell should be
saying aha, what you mean is, the array class defines a reverse method that
reverses the array elements, but as well, the string class defines reverse to
reverse the characters in a string. And to a first order that's correct. Well, we're
going to modify it and refine it in just a moment, but that's actually the right way
to think about it. I can ask whether any of the elements of x. Have a length
greater than five and just get back true or false. So, again, what's a real-life
example of you know, chaining one method after another after another? Let's go back
to our friend Pastebin for an example. And again, don't worry if this code looks
complicated. The goal is to get you accustomed to reading Ruby; this is
admittedly not the most beautiful Ruby, because I wrote if for myself. If any of
you has the Bay Area Clipper card that's valid on all public transit, I have it, I
use it all the time and I like to be able easily check my balance and they have the
world's worst non restful website to do it. So I just wrote a scraper. I didn't
reveal my clipper user name and password, but what does this do? Well, I'm using a
great ruby library called Mechanize, which is designed to be-, behave like a
scriptable browser. Right? It can pretend to do anything a human would do, if a
human were using the browser, so. What am I going to do? This whole thing by the
way, starting from line nine and going down to line eighteen, this is one
statement. I told you this isn't beautiful code, this is me needing to get something
done in a hurry, but it's real so I'm trying to be honest. So, I'm starting a
new mechanize session and on that session I'm calling posts to log into the Clipper
Card site. And on the result of that post, which is the body of the page that comes
back from log-in, I'm looking for the link that has an [inaudible], an anchor which
is the card value. So this is where you would click and it says get card value.
And on the result of that link, I'm going to click, which causes mechanize to follow
the link. On the result of that, which is another page, I'm gonna create a
[inaudible] object. On the result, which I'm gonna look for some tags, say clipper
cash, which is the little thing that tells you how much money you have. And then the
result of that, I'm going to call firsts, and then I have to traverse the HTML tree
a little bit, find a regular expression that has dollar sign followed by some
numbers, and wallah. I wrap it in HTML and it comes back. Again, you are not expected
to understand this in double quotes, but what's kind of the key message here.
Method chaining is hugely powerful, right. I was able to essentially write a string
of actions that looks like what I want to do instead of having temporary variables
for each one of those steps. And although it looks a little intimidating when you
first see it, once you get used to writing code this way, it goes much faster. So,
you know, if somebody comes back and says I wrote 100 lines of Ruby this afternoon,
they did a lot of work, right. One hundred lines of Ruby is really concise. You can
get a lot done in 100 lines of code. So, that was today's not so beautiful real
life example. >> Doesn't it make it a lot more difficult to debug if you're chaining
a bunch of things because it's harder to get the intermediate value? >> So the
question is when you do this kind of, you know, chaining a zillion things doesn't it
make it more difficult to debug? The answer is yes. But I didn't sort of type
all that code in it then ran it and sat back and watched, right? This was a trial
and error process. I had to actually try intermediate steps. Then I did some stuff
in the terminal window. But the result of what I did is step, step, step, step, step
because I optimized it to make it easier to read.