Created
June 24, 2017 18:08
-
-
Save lubien/49171296c717ed694c2f63b170e4ba04 to your computer and use it in GitHub Desktop.
Proof of concept of lazy evaluation of map and slices on JavaScript using generators, iterables and iterators. https://repl.it/JAbL/1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* imap lazily maps an interable. | |
* @param ref Either an iterable or an iterator | |
* @param fn Map function | |
* | |
* It works by always returning a new iterator so that | |
* you can chain other imaps without looping once more | |
* over the same array! | |
* | |
* Since iterators are returned, to start the real mapping | |
* you should do [...it] or use the function `list` I've made. | |
*/ | |
function * imap(ref, fn) { | |
for (const value of ref) { | |
console.log(fn) // Just so you can see what's going on | |
yield fn(value) | |
} | |
} | |
/* | |
* islice lazily slices an interable. | |
* @param ref Either an iterable or an iterator | |
* @param fn Map function | |
* | |
* islice ignore values at the start until it reaches | |
* the desired first value. The it keeps yielding values | |
* as long as it doesn't reach the end. | |
* | |
* Once again: since iterators are returned, to start the real slicing | |
* you should do [...it] or use the function `list` I've made. | |
*/ | |
function * islice(ref, start = 0, end = +Infinity) { | |
let i = 0 | |
const it = ref.next | |
// if we receive an iterator, we should just use it | |
// like when we chains imaps/islices | |
? ref | |
// otherwise we create an iterator from the iterable | |
: ref[Symbol.iterator]() | |
// ignore values until you find the start | |
while (i++ !== start && it.next()) | |
; // the null block muahahaha | |
for (const value of it) { | |
yield value | |
if (i++ === end) // stop as soon as we reach the desired end | |
break | |
} | |
} | |
const double = x => 2 * x | |
const square = x => x * x | |
const list = it => [...it] | |
// You SHOULD read from the innermost comment to the outermost | |
console.log( | |
list( | |
// whe you run the console will show you: | |
// [Function: double] | |
// [Function: square] | |
// proving that each iteration both functions | |
// work sequentially in only one loop | |
imap( | |
// but the outer map will run right after each iteration | |
// of the inner | |
imap( | |
// starts lazily mapping an array | |
[1, 2, 3, 4, 5] | |
, double) | |
, square) | |
) | |
) | |
console.log( | |
list( | |
imap( | |
// Since it's sliced, our outer map function | |
// will only reveive indexes 2..3. | |
// but the innet imap will receive ALL indexes so the console log | |
// starts with two: | |
// [Function: double] for index 0 | |
// [Function: double] for index 1 | |
// then the outer imap finnaly receives something | |
// [Function: double] for index 2 | |
// [Function: square] for index 2 | |
// [Function: double] for index 3 | |
// [Function: square] for index 3 | |
// note that islice will not run index 4 at all! | |
islice( | |
// but the thing is that islice will lazily | |
// ignore elements both from the start and from the end | |
// `2, 4` means "start from the index 2 up to but not including 4" | |
// that's basically a lazy array.slice(2, 4) | |
imap( | |
// Again we lazily map a list to doubles | |
[1, 2, 3, 4, 5] | |
, double), | |
2, 4 | |
) | |
, square) | |
) | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment