Description:
Generators in Javascript are really exciting. For one, they're
functions which can stop mid-way, return a value, and then pick up
execution where they left off. This lets you do some interesting stuff
- for example, it's now possible to work with infinite sequences in
Javascript, because generators let you construct a term in the sequence
on-demand.
However, the current implementation of Generators in Javascript
leaves much to be desired. Given the linear "layout" of iterator
values, I would expect to be able to use most of the methods on Arrays
to deal with Generators. Sequences is a polyfill that lets you do just
that.
Usage:
Just include the js-utils library in your webpage or application and you're set:
<script type="text/javascript" src="https://raw.githubusercontent.com/eyamil/js-utils/master/dist/js_utils.js"/>
Sequences falls under the js_utils namespace.
To use Sequences on any Generator g, you'll
first have to wrap it:
let seq = js_utils.Sequence.wrap(g);
Overview:
Sequence.wrap- Wrap a generator to use all of Sequence's functions on itSequence.unwrap- Get a generator back out of a Sequence objectSequence.map- Compose generators with other functionsSequence.filter- Filter out elements from a generatorSequence.slice- Take only a contiguous subset of elements from a generatorSequence.concat- Concatenate one generator to anotherSequence.zip- Zip together two generators into oneSequence.integrate- LikeArray.reduce, but get one element for each of the generator's
Sequence.wrap(gen):
Wraps any generator gen or iterable object (arrays, maps, etc.), as below.
let fibonacci_seq = function * () {
let i = 0;
let j = 1;
while (true) {
let fib_num = i;
i = j;
j = j + fib_num;
yield fib_num;
}
}
let fib = js_utils.Sequence.wrap(fibonacci_seq);
let small_seq = js_utils.Sequence.wrap([1,2,3]);
Sequence.unwrap():
Unwrap a Sequence object (in the example, some_seq) to expose the inner generator.
// Assuming some_seq is a sequence object, print its elements:
let gen = some_seq.unwrap();
for (var num of gen()) {
console.log(num);
}
Sequence.map(mapping_func):
Compose a function with a generator, just like Array.map.
Takes in the function mapping_func, which will be passed elements of
the Sequence ai and indices i of the elements.
Converts the Sequence {a0, a1, a2, a3, a4, ...} to the
Sequence {mapping_func(a0, 0), mapping_func(a1, 1), mapping_func(a2, 2), mapping_func(a3, 3), mapping_func(a4, 4), ...}.
// Assuming fib is a Sequence of fibonacci numbers,
// compute the fibonacci numbers squared (don't need indices):
let sq = (x) => x**2;
let fib_sq = fib.map(sq);
Sequence.filter(condition_func):
Filter out elements of a generator, just like Array.filter
. Takes in a function condition_f that will be passed the Sequence
elements and respective indices. condition_func returns true
if the value is to be included in the output Sequence, and
false otherwise.
// Compute the even fibonacci numbers:
let is_even = (x) => (x % 2 == 0);
let fib_even = fib.filter(is_even);
Sequence.slice(start, end):
Slice a generator, just like Array.slice.
Takes in indices start (inclusive) and end
(exclusive), and keeps only the elements lying between those indices.
Observe that when starting with an infinite Sequence,
Sequence.slice will give a finite, terminating Sequence
when end < Infinity (unlike Sequence.filter).
// Compute the 45th to 60th fibonacci numbers only:
let fib_subset = fib.filter(45, 66);
Sequence.concat(seq2):
Concatenate a generator seq2 to the current one, just
like Array.concat. Takes in a Sequence.
Observe that the sequence returned by seq1.concat(seq2)
will only reach elements of seq2 if seq1
terminates.
// A sequence containing 1 through 5, then the fibonacci numbers:
let silly_seq = (Sequence.wrap([1,2,3,4,5])).concat(fib);
Sequence.zip(seq2, packager_func):
Zip a generator with another. Inspired by zip() in Python.
Takes in another Sequence seq2 and a packager
function packager_func (optional, will package elements into length 2
arrays if not provided) and puts the elements together using the
packager.
Terminates when the shorter of the Sequences terminates.
// Package the Lucas numbers with the Fibonacci numbers as arrays [L, F]:
let zipped_fib_seqs = fib.zip(lucas);
// Alternatively, package them into objects like {L: 2, F: 1}:
let packaged_fib_seqs = fib.zip(lucas, (a, b) => {L: a, F: b});
Sequence.integrate(reducer_func):
Reduce a generator, returning intermediate values. Inspired by
Array.reduce, but more general-purpose.
Takes in a reducer function reducer_func and a base case value,
and returns the Sequence of subsequent reductions of s.
// Calculate triangular numbers, assuming N is the sequence of positive integers:
let tri_nums = N.integrate((a, b) => (a + b), 0);
// Elements look like (0), (0 + 1), (0 + 1 + 2), ...