Afrikaans
Akan
Albanian
Amharic
Arabic
Armenian
Azerbaijani
Basque
Belarusian
Bemba
Bengali
Bihari
Bosnian
Breton
Bulgarian
Cambodian
Catalan
Cebuano
Cherokee
Chichewa
Chinese (Traditional)
Corsican
Croatian
Czech
Danish
Dutch
English
Esperanto
Estonian
Ewe
Faroese
Filipino
Finnish
French
Frisian
Ga
Galician
Georgian
German
Greek
Guarani
Gujarati
Haitian Creole
Hausa
Hawaiian
Hebrew
Hindi
Hmong
Hungarian
Icelandic
Igbo
Indonesian
Interlingua
Irish
Italian
Japanese
Javanese
Kannada
Kazakh
Kinyarwanda
Kirundi
Kongo
Korean
Krio (Sierra Leone)
Kurdish
Kurdish (Soranî)
Kyrgyz
Laothian
Latin
Latvian
Lingala
Lithuanian
Lozi
Luganda
Luo
Luxembourgish
Macedonian
Malagasy
Malay
Malayalam
Maltese
Maori
Marathi
Mauritian Creole
Moldavian
Mongolian
Myanmar (Burmese)
Montenegrin
Nepali
Nigerian Pidgin
Northern Sotho
Norwegian
Norwegian (Nynorsk)
Occitan
Oriya
Oromo
Pashto
Persian
Polish
Portuguese (Brazil)
Portuguese (Portugal)
Punjabi
Quechua
Romanian
Romansh
Runyakitara
Russian
Samoan
Scots Gaelic
Serbian
Serbo-Croatian
Sesotho
Setswana
Seychellois Creole
Shona
Sindhi
Sinhalese
Slovak
Slovenian
Somali
Spanish
Spanish (Latin American)
Sundanese
Swahili
Swedish
Tajik
Tamil
Tatar
Telugu
Thai
Tigrinya
Tonga
Tshiluba
Tumbuka
Turkish
Turkmen
Twi
Uighur
Ukrainian
Urdu
Uzbek
Vietnamese
Welsh
Wolof
Xhosa
Yiddish
Yoruba
Zulu
So we've already seen some built-in iterators, like each, times, and up to, and folks sometimes
think those are the only iterators you can use.
But it turns out you can actually write your own iterator methods.
So let's look at writing a couple basic iterators ourselves.
So let's suppose we wanted to write an iterator method called once, and we want to pass it
a block, or associate a block with that method call, and the block's just going to say,
running your block, just like that.
How would we implement this method?
Well, to find the method, it's just a normal method.
Inside of that method, to pass control over to the block, we call yield.
So before we do that, I'm just going to print out before yield, and then after it, I'm going
to print out after yield.
And let's run it and see what happens.
So it prints before yield, then it prints running your block, then it prints after yield.
So what happened was we ran the method once.
It ran its first statement here, before yield, then yield, pass control over to this block,
and it printed running your block.
Then the block exited, it was done.
So then control passes back into the method, it finishes by printing after yield.
Let's say we want a different iterator.
Maybe we want the iterator to be called twice.
We want to yield to the block twice.
Well, we can just call yield multiple times inside of the method, change this over to
twice.
Now, if we run it, sure enough, we get before yield, it runs the block twice, and then we
get after yield.
How about we want the iterator to be called three times, but in this case, we want to
do something slightly different.
We want the iterator to pass the current iteration, like we've seen before with other iterator
methods.
So we want it to pass us the current number, and then inside of there, we're just going
to print out the number that it gives us.
Number like that.
So the method's going to be called three times, and then instead of before yield, I'm just
going to print here.
I'm going to say ready.
We're going to yield.
Now, any parameters that we pass to yield are going to get passed off to the associated
block.
So we could call yield and pass in the number one, right?
And then I'm going to print, put as set, and then we could call yield again.
Yield is a method here.
I can use the actual parentheses for two, or I could just say yield two because parentheses
in Ruby are optional.
So then I can say yield two, this would be go, and then I could say yield three, just
like that.
So now if we run that, we see it yielded to our block three times, and it gave it the
numbers one, two, and three.
Now that's pretty cool.
Let's look at that in slow motion.
We call the three times method with an associated block.
The method begins to execute, the first thing it does is print ready.
Then the method calls yield, passing the number one.
At that point, the associated block is invoked.
The value we pass to yield, the number one, is assigned to the block parameter number.
The block prints one.
Then the block returns control back over to the method.
The method prints set.
Then it yields to the block again, passing in two, and the block prints two.
Then we go back to the method, it prints go, and then we yield to the block a third time,
passing three, and the block prints three.
And then we come back to the method, and it ends.
So the cool thing about these iterator methods is they decouple the actual looping from what
happens during each iteration.
Here, let me show you.
So we could change this inside of here.
Instead of just printing out the number, we could say maybe it's the number times two,
for example.
If we run this code, the iterator does exactly the same thing, but when it turns control
over to our block, now we're multiplying the number by two.
Or maybe we want to just take number times number, one, four, and nine.
So our iterator method stays the same.
We don't have to change it at all, but we can change what happens during each iteration
simply by changing what's inside of the block.
Now blocks can also return a value.
So the last expression that's evaluated by the block will be returned as the value of
the yield.
Let's look at that.
I'm just going to clean this code up.
I don't want to do that.
And I'm going to write a new iterator.
Let's call it compute.
Inside of that method, I'm going to yield, but then I want to print out the result of
calling yield.
So let's see what happens.
If we call compute, we're going to pass it a block, and I'm just going to return.
The block's just going to say hello like that.
So if I run it now, then I see that it prints hello.
Even though I'm not using a put s here, this string is getting returned by the block here,
and then the put s is then printing that string to the console.
So let's say we had multiple statements in here.
Maybe we had like 7, 3.14.
If I run this now, well, the result of that block is 3.14, and it gets printed because
it's the last expression that's evaluated inside of the block.
But what happens if we try to call the iterator method without a block?
Well, if we just take the block off of here and we call it, we get this method local jump
error.
Wow, that looks scary.
No block given to yield.
So if we've got a yield just like this, it expects there to be an associated block, and
it tries to call it.
But we can get around that a little bit by using a conditional.
We can say if block underscore given, that's a built-in Ruby method.
If a block is given, then go ahead and yield to it.
Else, we'll just print out something like does not compute.
So now if I run it, no blocks given, we get does not compute.
Otherwise, I can put a block in here.
Maybe the block just says the number 3.14, for example, and then the block runs.
So what happens when we combine iterator methods with blocks that return values?
Well, here's something kind of clever about Ruby.
I'm just going to clean up this file here, and let's start with something else.
Remember, let's just create an array.
Let's say it's 10 numbers, right?
And remember our old friend select.
We could call select on that array, and it gives us a value, and then we select out all
the ones that are even, for example.
So if we run that, we get all the even numbers.
And that select looks kind of special, but let's think about how we might actually implement
that in Ruby.
Let's define a method called mySelect, just to keep it separate from the select that's
built into array.
And it would take, let's say, an array as a parameter there.
So we're not going to put this on the array class.
We're just going to have our own method, mySelect, that takes an array.
How would we implement select?
Well, clearly, we want to be able to create an array and return it that just has the elements
that we've selected.
So let's just start with saying that the results is an empty array, and at the end, we want
to be able to return those results.
Then what we want to do is just loop through each of the elements in the array using a
built-in iterator.
It's going to give us each element in that array.
And now what we want to do is capture all the elements of that array that match some
criteria.
So we want to append to our results array that element that we're looping through, only
if the result of running some block says that we should include that element.
So what we can do is we can say, OK, go ahead and run the block, yield, and give the block
the element so it can decide whether it should include it or not.
Then we can just call our method.
We'll just use putS, mySelect.
We'll pass in our numbers array.
It's going to yield to a block.
So let's give it a block just like that.
It's also going to give us the element, in this case n.
The block looks just like it does with a regular select.
If I just take off the select up here, and we run this now, we get all the even numbers.
So that gives us some insight into how select, reject, partition, and some of these other
iterator methods we've used actually work.
So this is all kind of interesting, but what's a practical use of an iterator like this?
Well, I have an idea where this could be useful.In the game, you're currently printing this.
And it would be nice to be able to print each player's points on a per-treasure basis.
And in the same way, our playlist is currently printing something like this.
And we want to be able to print each movie's snacks on a per-snack basis.
So let's recap where things stand.
When a movie is played, a random snack is snagged from the snack bar.
In the movie object, we call 8 snack and then pass in the random snack object.
The movie then stores the snack's name and accumulated carbs in a hash.
So in this example, two popcorns were consumed for a total of 40 carbs, and one soda, or
pop, was consumed for 5 carbs.
We can then print out the grand total number of carbs consumed, 45 in this case.
Now we want to be able to print out each movie's snacks on a per-snack basis.
We don't want to expose the hash outside of the movie.
How it stores snacks is an implementation detail.
Instead, we'll write an each snack iterator method that hands out snack objects representing
what's in the hash.
Here's how we'll use that iterator method.
Each snack yields snack objects to an associated block.
So the first time through, the block will print 40 total popcorn carbs.
And then the second time through, it'll print 5 total soda carbs.
Yeah, and if we put this code in the printStats method of the playlist class, then we get
the output we're aiming for.
And the playlist class, which you can think of as a client to the movie class, will be
none the wiser about how snacks are stored in the movie.
So let's do that.
So we're over in our playlist class in this printStats method, and what we want to do
is right now we're just printing out the movie's grand total carbs.
And what we want to be able to do, the code we want to be able to write here is to take
the movie, this is the movie inside of this loop, and we want to be able to call an iterator
like each snack.
And then that iterator method should give us a snack object like that.
And then inside of the block, we want to be able to print out snack carbs total snack
name carbs.
Should be something like 15 total soda carbs, for example.
So the movie is going to vend out these snacks to us using this iterator method.
So let's go write that.
Go over to movie, and write our iterator.
It's going to be called each snack.
What's it going to do?
Well, we're going to take our snack carbs hash.
We're going to iterate through the keys and the values for it.
Let me set up my block here.
This is going to be the name.
This is going to be the carbs.
So remember, when we gave the snack object to the movie via the eight snack method, we
deconstructed it into name and carbs inside of our hash.
Now we want to put that back together again.
So we want to be able to yield an actual snack object.
So let's create a snack object.
Snack.new.
The name of the snack is the key of our hash, and the carbs of the snack is the value of
our hash, which came in in this second parameter here.
So we're just taking the key value pair out of the hash, and we're constructing a new
snack object.
Then we'll just yield that snack object over to the block.
We save that.
If we look back in our playlist method, we're expecting a snack object here, because we're
going to look at its carbs and its snack and its snack name.
So now we should be able to go over to flix.rb.
And if we run it and look at the bottom, now we've got our total carbs at the top.
And then for each movie, we've got a breakdown of the carbs for each particular snack, and
then the total at the bottom.
Now you're probably not going to write your own iterator method all that often, but in
certain situations, they come in really handy.
In this case, we made the movie class easier to use.
In the same way we can ask an array for all of its elements, we can ask a movie for all
of its snacks.
And in the exercise, you're going to do something very similar.
You're going to print out each player's points on a per-treasure basis.
Then when you come back, we'll look at taking some user input from the command line or the
console, and we'll also look at how to read and write to files.
We'll see you then.
You'll kind of be like...
I don't know how to fake laugh.
That's the problem.
It's hard.
It is really...
It would come out so cheesy.
I'd be like, haha!
Okay.
I could do that.
Well, I'll laugh at my own joke.
Should I do that, Matt?
No.
Can't find what you're looking for?
Get subtitles in any language from opensubtitles.com, and translate them here.