Skip to content

Instantly share code, notes, and snippets.

@CrossEye
Forked from buzzdecafe/cons, car, cdr
Last active July 8, 2016 06:26
Show Gist options
  • Save CrossEye/5307355 to your computer and use it in GitHub Desktop.
Save CrossEye/5307355 to your computer and use it in GitHub Desktop.
// clean and pure:
function cons(x, y) {
return function(pick) {
return pick(x, y);
}
}
// does more stuff:
function cons(x, y) {
var fn = function(pick) {
return pick(x, y);
};
fn.toString =function() {
if (isEmpty(this)) {return "()";}
var h = car(this), t = cdr(this), nested = arguments.length > 0;
return (nested ? "" : "(") + (pair(h) ? h.toString() : h) + (isEmpty(t) ? "" : " " + t.toString(nested)) + (nested ? "" : ")");
};
fn.pair = true; // to support atom? function
return fn;
}
function car(fn) {
return fn(function(x, y) { return x; });
}
function cdr(fn) {
return fn(function(x, y) { return y; });
}
function atom(x) {
return (x !== null) && (x !== undefined) && !x.pair;
}
function pair(x) {
return x && x.pair;
}
function isEmpty(lat) {
return lat == null || car(lat) == null;
}
function list() {
var args = [].slice.call(arguments);
return (args.length === 0) ? null : cons(args.shift(), list.apply(null, args));
}
function map(fn, lat) {
return (lat === null) ? null : cons(fn(car(lat)), map(fn, cdr(lat)));
}
function foldl(fn, acc, lat) {
return (lat === null) ? acc : foldl(fn, fn(acc, car(lat)), cdr(lat));
}
function foldr(fn, acc, lat) {
return (lat === null) ? acc : fn(car(lat), foldr(fn, acc, cdr(lat)));
}
function filter(pred, lat) {
return (lat === null) ? null : (pred(car(lat))) ? cons(car(lat), filter(pred, cdr(lat))) : filter(pred, cdr(lat));
}
function append(l, m) {
return (l === null) ? m : cons(car(l), append(cdr(l), m));
}
function reverse(l) {
return (l === null) ? null : append(reverse(cdr(l)), cons(car(l), null));
}
function not(fn) {return function() {return !fn.apply(this, arguments);};} // or one arg only?
function any(pred, lat) {
return (lat === null) ? false : pred(car(lat)) || any(pred, cdr(lat));
}
function every(pred, lat) {
return !any(not(pred), lat)
}
function asArray(list) {
return isEmpty(list) ? [] : [car(list)].concat(asArray(cdr(list)));
}
function asList(arr) {return list.apply(null, arr);}
function flip(fn) {
return function(a, b) { return fn(b, a); };
}
function lPartial(fn) {
var args = [].slice.call(arguments, 1);
return function() {
return fn.apply(this, args.concat([].slice.call(arguments)));
};
}
function rPartial(fn) {
var args = [].slice.call(arguments, 1);
return function() {
return fn.apply(this, [].slice.call(arguments).concat(args));
};
}
function eq(l, r) {
return (l === null && r === null) ||
(l === undefined && r === undefined) ||
(atom(l) && atom(r) && l == r) ||
(pair(l) && pair(r) && eq(car(l), car(r)) && eq(cdr(l), cdr(r)));
}
var list1 = cons(1, (cons (3, (cons(5, (cons(7, null)))))));
console.assert(1 === car(list1), "car");
console.assert(3 === car(cdr(list1)), "cdr");
console.assert(atom(42) && atom(true) && atom(false) && atom({}) && atom("abc") &&
atom(NaN) && atom([1, 2]) && atom([]), "atom");
console.assert(!atom(list1) && !atom(null) && !atom(undefined), "not atom");
console.assert(pair(list1), "pair");
console.assert(!pair(42) && !pair(true) && !pair(false) && !pair({}) && !pair("abc") &&
!pair(NaN) && !pair([1, 2]) && !pair([]), "not pair");
console.assert("(1 3 5 7)" === "" + list1, "toString");
console.assert("(3 5 7)" === "" + cdr(list1), "cdr toString");
var list2 = list(1, 3, 6, 7);
console.assert("(1 3 6 7)" === "" + list2, "list");
var times2 = function(x) {return 2 * x;};
console.assert("(2 6 10 14)" === "" + map(times2, list1), "map");
console.assert("hello" == foldl(function(a, b) {return a + b;}, "",
list("h", "e", "l", "l", "o")), "foldl");
console.assert(12 == foldr(function(x, y) {return (x + y)/ 2;}, 54, list(12, 4, 10, 6)), "foldr");
var odd = function(n) {return !!(n % 2);};
var even = function(n) {return !(n % 2);};
console.assert("(2 4)" === "" + filter(even, list(1, 2, 3, 4, 5)), "filter");
console.assert("(1 2 3 4)" === "" + append(list(1, 2), list(3, 4)), "append");
console.assert("(7 5 3 1)" === "" + reverse(list1), "reverse");
console.assert(any(even, list2), "any even");
console.assert(!any(even, list1), "not any even");
console.assert(every(odd, list1), "every odd");
console.assert(!every(odd, list2), "not every odd");
var arr1 = asArray(list1);
console.assert("[object Array]" === Object.prototype.toString.call(arr1), "asArray type");
console.assert("1,3,5,7" == arr1.toString(), "asArray");
console.assert(pair(asList(arr1)), "asList type");
console.assert("(1 3 5 7)" === "" + asList(arr1), "asList");
console.assert("ba" === flip(function(x, y) {return x + y;})("a", "b"), "flip");
var add = function(x, y) {return x + y};
var sumAll = lPartial(foldl, add, 0);
console.assert(15 === sumAll(list(1, 2, 3, 4, 5)), "lPartial");
var mult = function(x, y) {return x * y;};
var firstFive = rPartial(foldl, list(1, 2, 3, 4, 5));
console.assert(15 === firstFive(add, 0) && 120 === firstFive(mult, 1), "rPartial");
console.assert(eq(list(1, 2, 3), list(1, 2, 3)), "eq");
console.assert(!eq(list(1, 2, 3), list(1, 2, 3, 4)), "not eq");
@CrossEye
Copy link
Author

CrossEye commented Apr 4, 2013

Thanks Mike, for getting this going -- so much fun!

Still would like to deal with asList([1, 2, [3, 4], 5]) // => (1 2 (3 4) 5) and the reverse for asArray().

All sorts of possible extensions, of course.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment