Skip to content

Instantly share code, notes, and snippets.

@dionyziz
Created September 7, 2011 13:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dionyziz/1200612 to your computer and use it in GitHub Desktop.
Save dionyziz/1200612 to your computer and use it in GitHub Desktop.
A library for potentially infinite streams in Javascript
function sieve( s ) {
if ( s.empty() ) {
return s;
}
var h = s.head();
return new Stream( h, function () {
return sieve( s.tail().filter( function ( x ) {
return x % h != 0;
} ) );
} );
}
var s = sieve( Stream.range( 2 ) );
s.take( 10 ).print();
function Stream( head, tailPromise ) {
if ( typeof head == 'undefined' ) {
head = null;
}
else if ( typeof tailPromise == 'undefined' ) {
tailPromise = function () {
return new Stream();
};
}
this.headValue = head;
this.tailPromise = tailPromise;
}
Stream.prototype = {
empty: function() {
return this.headValue == null;
},
head: function() {
if ( this.empty() ) {
throw 'Cannot get the head of the empty stream.';
}
return this.headValue;
},
tail: function() {
if ( this.empty() ) {
throw 'Cannot get the tail of the empty stream.';
}
return this.tailPromise();
},
add: function( s ) {
return this.zip( function ( x, y ) {
return x + y;
}, s );
},
zip: function( f, s ) {
if ( this.empty() ) {
return s;
}
if ( s.empty() ) {
return this;
}
var self = this;
return new Stream( f( s.head(), this.head() ), function () {
return self.tail().zip( f, s.tail() );
} );
},
map: function( f ) {
if ( this.empty() ) {
return this;
}
var self = this;
return new Stream( f( this.head() ), function () {
return self.tail().map( f );
} );
},
walk: function( f ) {
this.map( function ( x ) {
f( x );
return x;
} ).force();
},
force: function() {
var stream = this;
while ( !stream.empty() ) {
stream = stream.tail();
}
},
scale: function( factor ) {
return this.map( function ( x ) {
return factor * x;
} );
},
filter: function( f ) {
if ( this.empty() ) {
return this;
}
var h = this.head();
var t = this.tail();
if ( f( h ) ) {
return new Stream( h, function () {
return t.filter( f );
} );
}
return t.filter( f );
},
take: function ( howmany ) {
if ( this.empty() ) {
return this;
}
if ( howmany == 0 ) {
return new Stream();
}
// TODO: optimize: iterate
var self = this;
return new Stream(
this.head(),
function () {
return self.tail().take( howmany - 1 );
}
);
},
print: function() {
this.walk( console.log );
},
toString: function() {
return '[stream head: ' + this.head() + '; tail: ' + this.tail() + ']';
}
};
Stream.makeOnes = function() {
return new Stream( 1, Stream.makeOnes );
};
Stream.makeNaturalNumbers = function() {
return new Stream( 1, function () {
return Stream.makeNaturalNumbers().add( Stream.makeOnes() );
} );
};
Stream.make = function( /* arguments */ ) {
if ( arguments.length == 0 ) {
return new Stream();
}
var restArguments = Array.prototype.slice.call( arguments, 1 );
return new Stream( arguments[ 0 ], function () {
return Stream.make.apply( null, restArguments );
} );
};
Stream.range = function ( low, high ) {
if ( typeof low == 'undefined' ) {
low = 1;
}
if ( low == high ) {
return Stream.make( low );
}
// if high is undefined, there won't be an upper bound
return new Stream( low, function () {
return Stream.range( low + 1, high );
} );
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment