Skip to content

Instantly share code, notes, and snippets.

@savelichalex
Last active December 26, 2015 17:09
Show Gist options
  • Save savelichalex/61527d8a5bf0bedfc966 to your computer and use it in GitHub Desktop.
Save savelichalex/61527d8a5bf0bedfc966 to your computer and use it in GitHub Desktop.
Lazy Sequence and functions to work with this
'use strict';
/**
* Dalay expressions evaluation
* @param expressionAsFunc {Function}
* @returns {Function} thunk
*/
function delay(expressionAsFunc) {
let result;
let isEvaluated = false;
return () => {
if (!isEvaluated) {
result = expressionAsFunc();
}
return result;
}
}
/**
* Force thunk to evaluate
* @param thunk {Function}
* @returns {*} evaluated expression
*/
function force(thunk) {
return thunk();
}
/**
* Create cons
* @param car {*}
* @param cdr {Function}
* @returns {*[]}
*/
function cons(car, cdr) {
return [car, cdr];
}
/**
* Create a sequence cons pair
* @param x {*} cons car
* @param y {*} cons cdr
*/
function seqCons(x, y) {
return cons(x, delay(y));
}
/**
* Return car of sequence
* @param seq {Sequence}
*/
function seqCar(seq) {
return seq[0];
}
/**
* Return cdr of sequence
* @param seq {Sequence}
*/
function seqCdr(seq) {
return force(seq[1]);
}
/**
* Empty seq
*/
const theEmptySeq = [];
/**
* Function to check that seq is empty
* @param seq {Sequence}
* @return {boolean}
*/
function seqNull(seq) {
return seq.length === 0;
}
/**
* Ref function
* @param seq {Sequence}
* @param n {number}
*/
function seqRef(seq, n) {
if (n === 0) {
return seqCar(seq);
} else {
return seqRef(seqCdr(seq), n - 1);
}
}
/**
* Return result of apply proc to each item in sequence s
* @param proc {Function}
* @param s {Sequence}
*/
function seqMap(proc, s) {
if (seqNull(s)) {
return theEmptySeq;
} else {
return seqCons(proc(seqCar(s)), () => seqMap(proc, seqCdr(s))
)
;
}
}
/**
* Apply procedure to each item in sequence s
* @param procedure {Function}
* @param s {Sequence}
*/
function seqForEach(procedure, s) {
if (seqNull(s)) {
return;
} else {
procedure(seqCar(s));
return seqForEach(procedure, seqCdr(s));
}
}
/**
* Folter sequence by predicate
* @param predicate {Function}
* @param seq {Sequence}
* @returns {*}
*/
function seqFilter(predicate, seq) {
if (seqNull(seq)) {
return theEmptySeq;
} else {
const car = seqCar(seq);
if (predicate(car)) {
return seqCons(car, () => seqFilter(predicate, seqCdr(seq))
)
;
} else {
return seqFilter(predicate, seqCdr(seq));
}
}
}
function seqTake(seq, n) {
if (n === 1) {
return seqCons(seqCar(seq), () => theEmptySeq);
} else {
return seqCons(seqCar(seq), () => seqTake(seqCdr(seq), n - 1));
}
}
/**
* Create sequence in range between low and high
* @param low {number}
* @param high {number}
* @returns {*}
*/
function range(low, high) {
if (low > high) {
return theEmptySeq;
} else {
return seqCons(low, () => range(low + 1, high));
}
}
console.log('Lazy evaluation');
var multiplyCount = 0;
function multiplyByTwo(val) {
multiplyCount++;
return val * 2;
}
var oddCount = 0;
function odd(val) {
oddCount++;
return val % 10 === 0;
}
var logCount = 0;
function log(val) {
logCount++;
console.log(val);
}
var startTime;
var endTime;
startTime = Date.now();
seqForEach(
log,
seqTake(
seqMap(
multiplyByTwo,
seqFilter(
odd,
range(1, 1000)
)
),
5
)
);
endTime = Date.now();
console.log(['Odd called ', oddCount, ' times\n',
'Multiply called ', multiplyCount, ' times\n',
'Log called ' + logCount + ' times\n',
'Time: ', endTime - startTime, '\n'].join(''));
console.log('Native evaluation');
multiplyCount = 0;
oddCount = 0;
logCount = 0;
var arr = [];
for(var i = 1; i < 1001; i++) {
arr.push(i);
}
startTime = Date.now();
arr.filter(odd).map(multiplyByTwo).slice(0, 5).forEach(log);
endTime = Date.now();
console.log(['Odd called ', oddCount, ' times\n',
'Multiply called ', multiplyCount, ' times\n',
'Log called ' + logCount + ' times\n',
'Time: ', endTime - startTime, '\n'].join(''));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment