The wikipedia article about coroutines makes an interesting read. In simple terms we can say that a coroutine is a function with multiple entry points, so that it can exit and start its execution later in a different point.
This should sound familiar. You could create a reentrant function by means of a closure, that would keep a state dictating what to do in the ensuing call. Both in C# and JavaScript the yield and await keywords allow us to create reentrant functions (well, await is a bit different, we could say that the function is reentring itself). Indeed, the terminology used in JavaScript, generator, is in accordance with the Computer Science nomenclature used to define a limited type of coroutines.
The thing is that invoking a generator function (javascript) or an iterator method (C#) return a generator/IEnumerable-IEnumerator object on which we will invoke the next or MoveNext methods. So we don't really have a reentrant function, but invocations on the same method of an object that maintains state. If we really want the effect of invoking a reentrant function we can easily leverage a generator/Iterator. I'm using C# iterators for my samples, it would be basically the same in JavaScript.
We can define a Factory method that will return a reentrant function (delegate). This factory method contains a local function defining an iterator and creates a closure that manages the iteration.
public static Func<string> CreateMessageProvider<T>() { IEnumerator<string> messageProvider(){ yield return "Bonjour"; yield return "Hola"; yield return "Hello"; } IEnumerator<string> enumerator = messageProvider(); return () => { if (!(enumerator.MoveNext())){ throw new Exception("Finished"); } return enumerator.Current; }; } //main coroutine = CreateMessageProvider<string>(); Console.WriteLine(coroutine()); Console.WriteLine(coroutine()); Console.WriteLine(coroutine());
We can generalize the above code to create a reentrant function from any provided IEnumerable:
public static Func<T> CreateCoroutine<T>(IEnumerable<T> enumerable) { IEnumerator<T> enumerator = enumerable.GetEnumerator(); return () => { if (!(enumerator.MoveNext())){ throw new Exception("Finished"); } return enumerator.Current; }; }
No comments:
Post a Comment