Welcome to this section on generators.
Let's consider a problem in which we want to get the explanations of numbers ranging from C zero to
C, 100,000 or a million.
That's one six.
Or we could even start with a smaller number like, say, even ten.
So if we have to go from 0 to 10, we could define E, which is simply, uh, for Iron Range ten.
This way.
That's fine.
Now we're putting out E, and there we go.
We have this list of values ranging from 0 to 9.
And then if we want to get an explanation, we have simply to define this function exponential, which
takes an E, and then we return a list wherein we have I, we have let's let's consider our exponent
to be, say, a 1.01.
So we have 1.01 to the power of E.
All right.
It's suppose I for I e run that and then here we go.
We have exponent of e, we see we have 11.011.02 and so on and so forth.
We could increase this range to say thousand or even 10,000 that would go.
Let's take off the screens.
Right?
Yeah.
Okay.
Now we can go ahead again to increase this.
We have an overflow because this number is quite large.
Okay.
So we'll see.
Yeah.
Everybody's opinion that's giving a reason.
It's actually still loading.
That's interrupt this channel that's on this.
Run again.
See, it takes a lot of time to load all this in memory and is still loading the data in memory.
Let's interrupt the game and take a smaller number from that.
Wrong again.
This time around, it took less time and that took some time.
There's overflow.
So this reduced number again and again takes a while and that's what we obtain.
Now, as you could see, there is a problem with the way we are approaching this.
The problem is that we always have to load all this data in memory before computing this exponent for
each element in this list.
And given that each of those elements are actually independent, it means we could look for a more efficient
way of dealing with this data.
By fishing, it would mean maybe storing just the part of the list which will need for a given time
to compute exponents and then only compute in the exponents whenever need it.
Now, apart from this memory problem, the next problem it faces the fact that in some systems, like
in real time systems, where data isn't already packed somewhere.
So like if you have data in some database and that you are doing some.
Computation on this data then is possible that you could load it up and then go this way.
But then in some real life scenarios where this data is not necessarily located in the database but
maybe is coming in as input streams.
In that case, we can't actually preload this data because at a given time t the data at this one is
inexistent.
Now that said, oh, come on, the solution to this problem is working with generators.
Let's see how they work.
Now we have this list right here, which has been defined.
Now, we could actually let's let's reduce this.
Let's go back to it, then run this again.
This fine would take as long as we maintain it.
That's fine.
We now redefine gen exponential, which takes in the number of elements we have.
That's ten.
So we're taking the maximum number of elements.
And then in year we're actually going to return the exponential physical is this.
So we're going return this.
Yeah.
But now, instead of doing this return, we are going to yield.
So we generate we have this yield keyword.
And the way it works is very simple.
Instead of returning the whole array or the whole list, what we have is that we have this yield such
that for every element in my list.
So let's take this.
Let's say this is in.
So for every element in my list, I'm going to yield the output or I'm going to generate the output.
So yeah, instead of coming to like define the size that we have the whole list getting into this function
and then this function returns, also this whole list.
Now we define in this function differently in the sense that we have this maximum number of inputs.
And then for each and every one of them, or as they are passed in, we just simply yield the outputs.
So run that.
And then the way we take this values or the way we obtain the outputs from this kind of generate a function
is by simply using the next keyword.
So we have next gen exponential.
What we pass is we could take, for example, ten.
Now that's, that's okay.
We have to specify G, for example.
So we have this exponential, which takes M ten.
So now we would say that the maximum number of inputs we're working with is ten, and then we'll define
that as such.
And then we now look for the next G.
We have this error into the rebel.
We arrange run again.
That's fine.
So we'll see.
We have this farce elements.
And then if we repeat this, those repeat this.
We'll have the next element.
Let's do some prints.
Prints?
See, we have this elements, this next and so on and so forth.
Notice that because we had done oh, we had caught on this next previously we ended up having this first
value.
Instead we having this value here and this one.
So let's run.
We run this again.
We have one zero.
Next one.
Let's take all this.
One, two, three, four.
Yeah.
Run again.
And we see that we have this same output right here.
But what the difference that this time around the outputs are and just return as a whole, but instead
they are being generated in batches of one.
We can now go ahead and increases RN.
So let's take for example that there we go.
We see that unlike with this, let's take this back here.
Run it again.
You see, before you has to load all this in memory after loading this, we have to get into this.
We have to get into this.
See, that's already taking a lot of time.
Now, let's put this a bit.
Rob.
So it takes some takes a while to load this in memory whereas here.
It doesn't take that much time to load in memory.
So you practically takes no time.
And the job is done.
So clearly working with generators is memory efficient.
And also in some cases, as we said, like when we have continuous flow of data, we can actually work
with data which doesn't exist yet.
And in such cases we have to make use of generators like this.