Would you like to inspect the original subtitles? These are the user uploaded subtitles that are being translated:
1
00:00:00,000 --> 00:00:08,620
So we've already seen some built-in iterators, like each, times, and up to, and folks sometimes
2
00:00:08,620 --> 00:00:11,200
think those are the only iterators you can use.
3
00:00:11,200 --> 00:00:14,180
But it turns out you can actually write your own iterator methods.
4
00:00:14,180 --> 00:00:17,660
So let's look at writing a couple basic iterators ourselves.
5
00:00:17,660 --> 00:00:21,700
So let's suppose we wanted to write an iterator method called once, and we want to pass it
6
00:00:21,700 --> 00:00:26,320
a block, or associate a block with that method call, and the block's just going to say,
7
00:00:26,320 --> 00:00:29,340
running your block, just like that.
8
00:00:29,340 --> 00:00:31,000
How would we implement this method?
9
00:00:31,000 --> 00:00:34,640
Well, to find the method, it's just a normal method.
10
00:00:34,640 --> 00:00:39,860
Inside of that method, to pass control over to the block, we call yield.
11
00:00:39,860 --> 00:00:45,200
So before we do that, I'm just going to print out before yield, and then after it, I'm going
12
00:00:45,200 --> 00:00:47,700
to print out after yield.
13
00:00:47,700 --> 00:00:51,280
And let's run it and see what happens.
14
00:00:51,280 --> 00:00:55,560
So it prints before yield, then it prints running your block, then it prints after yield.
15
00:00:55,560 --> 00:00:58,440
So what happened was we ran the method once.
16
00:00:58,440 --> 00:01:04,280
It ran its first statement here, before yield, then yield, pass control over to this block,
17
00:01:04,280 --> 00:01:06,140
and it printed running your block.
18
00:01:06,140 --> 00:01:08,280
Then the block exited, it was done.
19
00:01:08,280 --> 00:01:13,320
So then control passes back into the method, it finishes by printing after yield.
20
00:01:13,320 --> 00:01:14,720
Let's say we want a different iterator.
21
00:01:14,720 --> 00:01:16,840
Maybe we want the iterator to be called twice.
22
00:01:16,840 --> 00:01:18,280
We want to yield to the block twice.
23
00:01:18,280 --> 00:01:24,160
Well, we can just call yield multiple times inside of the method, change this over to
24
00:01:24,160 --> 00:01:25,160
twice.
25
00:01:25,160 --> 00:01:29,640
Now, if we run it, sure enough, we get before yield, it runs the block twice, and then we
26
00:01:29,640 --> 00:01:30,640
get after yield.
27
00:01:30,640 --> 00:01:35,200
How about we want the iterator to be called three times, but in this case, we want to
28
00:01:35,200 --> 00:01:37,060
do something slightly different.
29
00:01:37,060 --> 00:01:41,960
We want the iterator to pass the current iteration, like we've seen before with other iterator
30
00:01:41,960 --> 00:01:42,960
methods.
31
00:01:42,960 --> 00:01:46,580
So we want it to pass us the current number, and then inside of there, we're just going
32
00:01:46,580 --> 00:01:49,460
to print out the number that it gives us.
33
00:01:49,460 --> 00:01:51,179
Number like that.
34
00:01:51,180 --> 00:01:58,080
So the method's going to be called three times, and then instead of before yield, I'm just
35
00:01:58,080 --> 00:01:59,080
going to print here.
36
00:01:59,080 --> 00:02:01,040
I'm going to say ready.
37
00:02:01,040 --> 00:02:02,160
We're going to yield.
38
00:02:02,160 --> 00:02:07,140
Now, any parameters that we pass to yield are going to get passed off to the associated
39
00:02:07,140 --> 00:02:08,140
block.
40
00:02:08,140 --> 00:02:10,720
So we could call yield and pass in the number one, right?
41
00:02:10,720 --> 00:02:15,200
And then I'm going to print, put as set, and then we could call yield again.
42
00:02:15,200 --> 00:02:16,560
Yield is a method here.
43
00:02:16,560 --> 00:02:22,320
I can use the actual parentheses for two, or I could just say yield two because parentheses
44
00:02:22,320 --> 00:02:23,320
in Ruby are optional.
45
00:02:23,320 --> 00:02:29,320
So then I can say yield two, this would be go, and then I could say yield three, just
46
00:02:29,320 --> 00:02:30,320
like that.
47
00:02:30,320 --> 00:02:34,560
So now if we run that, we see it yielded to our block three times, and it gave it the
48
00:02:34,560 --> 00:02:37,120
numbers one, two, and three.
49
00:02:37,120 --> 00:02:38,720
Now that's pretty cool.
50
00:02:38,720 --> 00:02:40,840
Let's look at that in slow motion.
51
00:02:40,840 --> 00:02:44,360
We call the three times method with an associated block.
52
00:02:44,360 --> 00:02:48,840
The method begins to execute, the first thing it does is print ready.
53
00:02:48,840 --> 00:02:52,340
Then the method calls yield, passing the number one.
54
00:02:52,340 --> 00:02:55,560
At that point, the associated block is invoked.
55
00:02:55,560 --> 00:03:01,100
The value we pass to yield, the number one, is assigned to the block parameter number.
56
00:03:01,100 --> 00:03:02,940
The block prints one.
57
00:03:02,940 --> 00:03:06,040
Then the block returns control back over to the method.
58
00:03:06,040 --> 00:03:07,960
The method prints set.
59
00:03:07,960 --> 00:03:12,520
Then it yields to the block again, passing in two, and the block prints two.
60
00:03:12,520 --> 00:03:18,080
Then we go back to the method, it prints go, and then we yield to the block a third time,
61
00:03:18,080 --> 00:03:21,560
passing three, and the block prints three.
62
00:03:21,560 --> 00:03:24,740
And then we come back to the method, and it ends.
63
00:03:24,740 --> 00:03:29,120
So the cool thing about these iterator methods is they decouple the actual looping from what
64
00:03:29,120 --> 00:03:30,560
happens during each iteration.
65
00:03:30,560 --> 00:03:32,160
Here, let me show you.
66
00:03:32,160 --> 00:03:33,920
So we could change this inside of here.
67
00:03:33,920 --> 00:03:38,360
Instead of just printing out the number, we could say maybe it's the number times two,
68
00:03:38,360 --> 00:03:39,360
for example.
69
00:03:39,360 --> 00:03:43,720
If we run this code, the iterator does exactly the same thing, but when it turns control
70
00:03:43,720 --> 00:03:47,560
over to our block, now we're multiplying the number by two.
71
00:03:47,560 --> 00:03:52,360
Or maybe we want to just take number times number, one, four, and nine.
72
00:03:52,360 --> 00:03:54,400
So our iterator method stays the same.
73
00:03:54,400 --> 00:03:58,660
We don't have to change it at all, but we can change what happens during each iteration
74
00:03:58,660 --> 00:04:01,880
simply by changing what's inside of the block.
75
00:04:01,880 --> 00:04:04,040
Now blocks can also return a value.
76
00:04:04,040 --> 00:04:08,920
So the last expression that's evaluated by the block will be returned as the value of
77
00:04:08,920 --> 00:04:09,920
the yield.
78
00:04:09,920 --> 00:04:10,920
Let's look at that.
79
00:04:10,920 --> 00:04:12,119
I'm just going to clean this code up.
80
00:04:12,119 --> 00:04:13,559
I don't want to do that.
81
00:04:13,559 --> 00:04:14,839
And I'm going to write a new iterator.
82
00:04:14,839 --> 00:04:17,700
Let's call it compute.
83
00:04:17,700 --> 00:04:22,960
Inside of that method, I'm going to yield, but then I want to print out the result of
84
00:04:22,960 --> 00:04:24,560
calling yield.
85
00:04:24,560 --> 00:04:25,560
So let's see what happens.
86
00:04:25,560 --> 00:04:30,120
If we call compute, we're going to pass it a block, and I'm just going to return.
87
00:04:30,120 --> 00:04:32,200
The block's just going to say hello like that.
88
00:04:32,200 --> 00:04:36,000
So if I run it now, then I see that it prints hello.
89
00:04:36,000 --> 00:04:41,680
Even though I'm not using a put s here, this string is getting returned by the block here,
90
00:04:41,680 --> 00:04:44,620
and then the put s is then printing that string to the console.
91
00:04:44,620 --> 00:04:46,240
So let's say we had multiple statements in here.
92
00:04:46,240 --> 00:04:49,740
Maybe we had like 7, 3.14.
93
00:04:49,740 --> 00:04:54,560
If I run this now, well, the result of that block is 3.14, and it gets printed because
94
00:04:54,560 --> 00:04:58,500
it's the last expression that's evaluated inside of the block.
95
00:04:58,500 --> 00:05:02,000
But what happens if we try to call the iterator method without a block?
96
00:05:02,000 --> 00:05:07,480
Well, if we just take the block off of here and we call it, we get this method local jump
97
00:05:07,480 --> 00:05:08,480
error.
98
00:05:08,480 --> 00:05:09,480
Wow, that looks scary.
99
00:05:09,480 --> 00:05:11,680
No block given to yield.
100
00:05:11,680 --> 00:05:16,200
So if we've got a yield just like this, it expects there to be an associated block, and
101
00:05:16,200 --> 00:05:17,540
it tries to call it.
102
00:05:17,540 --> 00:05:21,840
But we can get around that a little bit by using a conditional.
103
00:05:21,840 --> 00:05:26,140
We can say if block underscore given, that's a built-in Ruby method.
104
00:05:26,140 --> 00:05:29,120
If a block is given, then go ahead and yield to it.
105
00:05:29,120 --> 00:05:35,640
Else, we'll just print out something like does not compute.
106
00:05:35,640 --> 00:05:38,720
So now if I run it, no blocks given, we get does not compute.
107
00:05:38,720 --> 00:05:41,120
Otherwise, I can put a block in here.
108
00:05:41,120 --> 00:05:46,840
Maybe the block just says the number 3.14, for example, and then the block runs.
109
00:05:46,840 --> 00:05:50,640
So what happens when we combine iterator methods with blocks that return values?
110
00:05:50,640 --> 00:05:52,840
Well, here's something kind of clever about Ruby.
111
00:05:52,840 --> 00:05:57,120
I'm just going to clean up this file here, and let's start with something else.
112
00:05:57,120 --> 00:06:00,400
Remember, let's just create an array.
113
00:06:00,400 --> 00:06:04,400
Let's say it's 10 numbers, right?
114
00:06:04,400 --> 00:06:07,080
And remember our old friend select.
115
00:06:07,080 --> 00:06:12,880
We could call select on that array, and it gives us a value, and then we select out all
116
00:06:12,880 --> 00:06:14,920
the ones that are even, for example.
117
00:06:14,920 --> 00:06:19,240
So if we run that, we get all the even numbers.
118
00:06:19,240 --> 00:06:24,200
And that select looks kind of special, but let's think about how we might actually implement
119
00:06:24,200 --> 00:06:27,400
that in Ruby.
120
00:06:27,400 --> 00:06:32,640
Let's define a method called mySelect, just to keep it separate from the select that's
121
00:06:32,640 --> 00:06:34,120
built into array.
122
00:06:34,120 --> 00:06:37,620
And it would take, let's say, an array as a parameter there.
123
00:06:37,620 --> 00:06:39,560
So we're not going to put this on the array class.
124
00:06:39,560 --> 00:06:42,840
We're just going to have our own method, mySelect, that takes an array.
125
00:06:42,840 --> 00:06:44,400
How would we implement select?
126
00:06:44,400 --> 00:06:49,360
Well, clearly, we want to be able to create an array and return it that just has the elements
127
00:06:49,360 --> 00:06:50,360
that we've selected.
128
00:06:50,360 --> 00:06:55,040
So let's just start with saying that the results is an empty array, and at the end, we want
129
00:06:55,040 --> 00:06:57,840
to be able to return those results.
130
00:06:57,840 --> 00:07:02,120
Then what we want to do is just loop through each of the elements in the array using a
131
00:07:02,120 --> 00:07:03,700
built-in iterator.
132
00:07:03,700 --> 00:07:06,760
It's going to give us each element in that array.
133
00:07:06,760 --> 00:07:12,320
And now what we want to do is capture all the elements of that array that match some
134
00:07:12,320 --> 00:07:13,540
criteria.
135
00:07:13,540 --> 00:07:20,820
So we want to append to our results array that element that we're looping through, only
136
00:07:20,820 --> 00:07:26,000
if the result of running some block says that we should include that element.
137
00:07:26,000 --> 00:07:30,520
So what we can do is we can say, OK, go ahead and run the block, yield, and give the block
138
00:07:30,520 --> 00:07:34,840
the element so it can decide whether it should include it or not.
139
00:07:34,840 --> 00:07:36,760
Then we can just call our method.
140
00:07:36,760 --> 00:07:39,560
We'll just use putS, mySelect.
141
00:07:39,560 --> 00:07:41,240
We'll pass in our numbers array.
142
00:07:41,240 --> 00:07:42,680
It's going to yield to a block.
143
00:07:42,680 --> 00:07:44,640
So let's give it a block just like that.
144
00:07:44,640 --> 00:07:47,320
It's also going to give us the element, in this case n.
145
00:07:47,320 --> 00:07:50,280
The block looks just like it does with a regular select.
146
00:07:50,280 --> 00:07:57,160
If I just take off the select up here, and we run this now, we get all the even numbers.
147
00:07:57,160 --> 00:08:01,560
So that gives us some insight into how select, reject, partition, and some of these other
148
00:08:01,560 --> 00:08:05,100
iterator methods we've used actually work.
149
00:08:05,100 --> 00:08:09,080
So this is all kind of interesting, but what's a practical use of an iterator like this?
150
00:08:09,080 --> 00:08:15,560
Well, I have an idea where this could be useful.In the game, you're currently printing this.
151
00:08:15,560 --> 00:08:21,140
And it would be nice to be able to print each player's points on a per-treasure basis.
152
00:08:21,140 --> 00:08:25,960
And in the same way, our playlist is currently printing something like this.
153
00:08:25,960 --> 00:08:31,400
And we want to be able to print each movie's snacks on a per-snack basis.
154
00:08:31,400 --> 00:08:33,539
So let's recap where things stand.
155
00:08:33,539 --> 00:08:38,199
When a movie is played, a random snack is snagged from the snack bar.
156
00:08:38,200 --> 00:08:43,680
In the movie object, we call 8 snack and then pass in the random snack object.
157
00:08:43,680 --> 00:08:48,720
The movie then stores the snack's name and accumulated carbs in a hash.
158
00:08:48,720 --> 00:08:54,600
So in this example, two popcorns were consumed for a total of 40 carbs, and one soda, or
159
00:08:54,600 --> 00:08:58,160
pop, was consumed for 5 carbs.
160
00:08:58,160 --> 00:09:03,880
We can then print out the grand total number of carbs consumed, 45 in this case.
161
00:09:03,880 --> 00:09:07,880
Now we want to be able to print out each movie's snacks on a per-snack basis.
162
00:09:07,880 --> 00:09:10,780
We don't want to expose the hash outside of the movie.
163
00:09:10,780 --> 00:09:13,520
How it stores snacks is an implementation detail.
164
00:09:13,520 --> 00:09:19,280
Instead, we'll write an each snack iterator method that hands out snack objects representing
165
00:09:19,280 --> 00:09:21,160
what's in the hash.
166
00:09:21,160 --> 00:09:23,700
Here's how we'll use that iterator method.
167
00:09:23,700 --> 00:09:28,360
Each snack yields snack objects to an associated block.
168
00:09:28,360 --> 00:09:33,500
So the first time through, the block will print 40 total popcorn carbs.
169
00:09:33,500 --> 00:09:38,280
And then the second time through, it'll print 5 total soda carbs.
170
00:09:38,280 --> 00:09:42,800
Yeah, and if we put this code in the printStats method of the playlist class, then we get
171
00:09:42,800 --> 00:09:44,440
the output we're aiming for.
172
00:09:44,440 --> 00:09:48,140
And the playlist class, which you can think of as a client to the movie class, will be
173
00:09:48,140 --> 00:09:51,500
none the wiser about how snacks are stored in the movie.
174
00:09:51,500 --> 00:09:53,240
So let's do that.
175
00:09:53,240 --> 00:09:56,940
So we're over in our playlist class in this printStats method, and what we want to do
176
00:09:56,940 --> 00:10:00,760
is right now we're just printing out the movie's grand total carbs.
177
00:10:00,760 --> 00:10:05,200
And what we want to be able to do, the code we want to be able to write here is to take
178
00:10:05,200 --> 00:10:09,060
the movie, this is the movie inside of this loop, and we want to be able to call an iterator
179
00:10:09,060 --> 00:10:11,800
like each snack.
180
00:10:11,800 --> 00:10:15,840
And then that iterator method should give us a snack object like that.
181
00:10:15,840 --> 00:10:25,700
And then inside of the block, we want to be able to print out snack carbs total snack
182
00:10:25,700 --> 00:10:28,000
name carbs.
183
00:10:28,000 --> 00:10:32,480
Should be something like 15 total soda carbs, for example.
184
00:10:32,480 --> 00:10:35,920
So the movie is going to vend out these snacks to us using this iterator method.
185
00:10:35,920 --> 00:10:36,920
So let's go write that.
186
00:10:36,920 --> 00:10:40,840
Go over to movie, and write our iterator.
187
00:10:40,840 --> 00:10:44,880
It's going to be called each snack.
188
00:10:44,880 --> 00:10:45,880
What's it going to do?
189
00:10:45,880 --> 00:10:48,960
Well, we're going to take our snack carbs hash.
190
00:10:48,960 --> 00:10:52,080
We're going to iterate through the keys and the values for it.
191
00:10:52,080 --> 00:10:54,280
Let me set up my block here.
192
00:10:54,280 --> 00:10:55,600
This is going to be the name.
193
00:10:55,600 --> 00:10:57,440
This is going to be the carbs.
194
00:10:57,440 --> 00:11:02,960
So remember, when we gave the snack object to the movie via the eight snack method, we
195
00:11:02,960 --> 00:11:06,120
deconstructed it into name and carbs inside of our hash.
196
00:11:06,120 --> 00:11:08,080
Now we want to put that back together again.
197
00:11:08,080 --> 00:11:11,200
So we want to be able to yield an actual snack object.
198
00:11:11,200 --> 00:11:13,200
So let's create a snack object.
199
00:11:13,200 --> 00:11:15,200
Snack.new.
200
00:11:15,200 --> 00:11:23,140
The name of the snack is the key of our hash, and the carbs of the snack is the value of
201
00:11:23,140 --> 00:11:26,560
our hash, which came in in this second parameter here.
202
00:11:26,560 --> 00:11:29,959
So we're just taking the key value pair out of the hash, and we're constructing a new
203
00:11:29,959 --> 00:11:31,819
snack object.
204
00:11:31,819 --> 00:11:36,839
Then we'll just yield that snack object over to the block.
205
00:11:36,839 --> 00:11:37,839
We save that.
206
00:11:37,839 --> 00:11:41,680
If we look back in our playlist method, we're expecting a snack object here, because we're
207
00:11:41,680 --> 00:11:45,160
going to look at its carbs and its snack and its snack name.
208
00:11:45,160 --> 00:11:48,540
So now we should be able to go over to flix.rb.
209
00:11:48,540 --> 00:11:54,319
And if we run it and look at the bottom, now we've got our total carbs at the top.
210
00:11:54,320 --> 00:12:00,840
And then for each movie, we've got a breakdown of the carbs for each particular snack, and
211
00:12:00,840 --> 00:12:02,800
then the total at the bottom.
212
00:12:02,800 --> 00:12:05,720
Now you're probably not going to write your own iterator method all that often, but in
213
00:12:05,720 --> 00:12:08,320
certain situations, they come in really handy.
214
00:12:08,320 --> 00:12:11,280
In this case, we made the movie class easier to use.
215
00:12:11,280 --> 00:12:15,320
In the same way we can ask an array for all of its elements, we can ask a movie for all
216
00:12:15,320 --> 00:12:16,800
of its snacks.
217
00:12:16,800 --> 00:12:19,800
And in the exercise, you're going to do something very similar.
218
00:12:19,800 --> 00:12:24,439
You're going to print out each player's points on a per-treasure basis.
219
00:12:24,439 --> 00:12:28,040
Then when you come back, we'll look at taking some user input from the command line or the
220
00:12:28,040 --> 00:12:31,520
console, and we'll also look at how to read and write to files.
221
00:12:31,520 --> 00:12:34,319
We'll see you then.
222
00:12:34,319 --> 00:12:37,359
You'll kind of be like...
223
00:12:37,359 --> 00:12:38,920
I don't know how to fake laugh.
224
00:12:38,920 --> 00:12:39,920
That's the problem.
225
00:12:39,920 --> 00:12:40,920
It's hard.
226
00:12:40,920 --> 00:12:41,920
It is really...
227
00:12:41,920 --> 00:12:42,920
It would come out so cheesy.
228
00:12:42,920 --> 00:12:43,920
I'd be like, haha!
229
00:12:43,920 --> 00:12:44,920
Okay.
230
00:12:44,920 --> 00:12:45,920
I could do that.
231
00:12:45,920 --> 00:12:46,920
Well, I'll laugh at my own joke.
232
00:12:46,920 --> 00:12:47,920
Should I do that, Matt?
233
00:12:47,920 --> 00:12:49,920
No.
20804
Can't find what you're looking for?
Get subtitles in any language from opensubtitles.com, and translate them here.