If you've never seen generators, there is this concept of generator functions and generator objects. A generator function is, looks like a function, it's just a little bit special. You have to put an asterisk there. And what we gain if we put that asterisk is that now we can use this keyword called yield.
And what yield does is kind of a special kind of return. It allows you to return a value, but rather than stopping the execution of the particular function, it's going to remember exactly where we stopped the last time. So it's more like a pause than actually stopping the function execution. And we can resume that execution from the point where it was left on the previous iteration.
But how do we use a generator function? Well, we can invoke it as a regular function, and that is going to return to us a generator object. And on a generator object, we can keep calling dot next dot next, and every time we receive back an object that contains the keys done and value that are basically telling us there is more data, yes or no, and this is the current value.
Now let me show you a more maybe interesting example where, sorry, where we have multiple yield points. This is fruit generator, so every instance we are returning a new fruit, or we are yielding a new fruit. And when we call dot next dot next, you can see that we get every single time a different item, so you can imagine the execution is pausing when we yield. And when we call dot next again, it's gonna continue from the line where we paused the last time.
The cool thing is that we can also use for of, and if we use for of on generator objects, we just get straight away access to the values. We don't have to think about that wrapper object anymore, and the loop is gonna stop automatically when we reach done equal true.
Now we can finally talk about iterators and iterables. Now these are two different concepts, very similar, very often mixed up together, so let's try to figure out what are the differences between the two. I like to think about iterator objects as a cursor on a collection of data. So you can imagine this yellow arrow there is an iterator object. The only thing we can do on it is just say, give me the current value or move to the next value.
While an iterable object is a slightly more generic concept, it's more the idea that you have a collection and this collection is somehow iterable, and the way you can iterate over it is to get an iterator from that collection. So again, you can imagine iterator as the cursor, iterable as the idea of a collection that you can get a cursor from.
Now just to show you a very quick example, here we have a factory function that allows us to create an iterator that represents a countdown, so if we call this function, for instance with 3, it's going to create an iterator that will give us the value 3, 2, 1, 0, right? So we want to implement this iterator, so we need to comply with the definition of the iteration protocol. So we need to have an object that has a next method, which returns objects with the keys done and value. So this is basically a factory function creating an iterator for us. And once we do that, we can instantiate it, count down is an iterator.