Sid Limaye

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(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), ...