Skip to content

Instantly share code, notes, and snippets.

@lubien
Created June 24, 2017 18:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lubien/49171296c717ed694c2f63b170e4ba04 to your computer and use it in GitHub Desktop.
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
/*
* 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