Last active
April 29, 2024 04:07
-
-
Save LuckyKoala/5ddf8fddf50b2640166c1b75e2ba5934 to your computer and use it in GitHub Desktop.
Javascript implementation for SICP Chapter 2.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
sicp = (function() { | |
//-----Base implementation------ | |
//Primitive procedure | |
let cons = (x,y) => [x,y]; | |
let car = (pair) => pair[0]; | |
let cdr = (pair) => pair[1]; | |
//Nil definition | |
let nil = []; | |
let isNil = (obj) => obj==null || (Array.isArray(obj) && obj.length==0); | |
//Helper procedure to build, extract, compare and print list | |
let cadr = (pair) => car(cdr(pair)); | |
let list = (...args) => { | |
if(args.length > 1) return cons(args[0], list(...args.slice(1))); | |
else if(args.length == 0) return nil; | |
else return cons(args[0], nil); | |
}; | |
let isEqual = (seq1, seq2) => JSON.stringify(seq1)===JSON.stringify(seq2); | |
let print = (seq) => JSON.stringify(seq); | |
//List operations | |
let listRef = (seq, n) => { | |
if(n==0) return car(seq); | |
else return listRef(cdr(seq), n-1); | |
}; | |
let length = (seq) => { | |
let lengthIter = (s, count) => { | |
if(isNil(s)) return count; | |
else return lengthIter(cdr(s), count+1); | |
}; | |
return lengthIter(seq, 0); | |
}; | |
let append = (list1, list2) => { | |
if(isNil(list1)) return list2; | |
else return cons(car(list1), append(cdr(list1), list2)); | |
}; | |
//------Test------ | |
(function() { | |
//Test for usage of primitive procedure which is defined before | |
let test = cons(1,2); | |
console.log("can extract 1 and 2: " + (car(test)==1 && cdr(test)==2)); | |
let test1 = cons(1, cons(2,cons(3,4))); | |
console.log("can extract 1,2,3 and 4 from more complicate pair: " | |
+ (car(test1)==1 && car(cdr(test1))==2 && car(cdr(cdr(test1)))==3 | |
&& cdr(cdr(cdr(test1)))==4)); | |
//Test for usage of helper procedure | |
console.log("list() === nil: " + (list()===nil)); | |
test = cons(1, cons(2,cons(3,cons(4,nil)))); | |
test1 = list(1, 2, 3, 4); | |
console.log("list EQUAL recursive cons: " + isEqual(test, test1)); | |
//Test for usage of list operations | |
test = list(0,1,2,3,4,5); | |
refResult = true; | |
for(let i=0; i<=5; i++) { | |
refResult &= listRef(test, i)==i; | |
} | |
console.log("listRef can work: " + refResult); | |
console.log("length can work: " + (length(test)==6)); | |
test1 = list(6); | |
test2 = list(0,1,2,3,4,5,6); | |
console.log("append can work: " + isEqual(append(test,test1), test2)); | |
})(); | |
//------Solutions of ex2.17------ | |
let lastPair = (seq) => { | |
if(isNil(seq)) return nil; | |
let rest = cdr(seq); | |
if(isNil(rest) && isNil(car(rest))) return car(seq); | |
else return lastPair(rest); | |
}; | |
//------Solutions of ex2.18------ | |
let reverse = (seq) => { | |
let reverseIter = (ret, s) => { | |
if(isNil(s)) return ret; | |
let first = car(s); | |
let rest = cdr(s); | |
return reverseIter(cons(first,ret), rest); | |
}; | |
return reverseIter(nil, seq); | |
}; | |
//------Tree------ | |
let isPair = (seq) => Array.isArray(seq) && seq.length==2; | |
let countLeaves = (seq) => { | |
if(isNil(seq)) return 0; | |
else if(!isPair(seq)) return 1; | |
else return countLeaves(car(seq)) + countLeaves(cdr(seq)); | |
}; | |
//------Test------ | |
(function() { | |
let x = cons(list(1,2),list(3,4)); | |
let xx = list(x,x); | |
console.log("countLeaves can work: " + (length(x)==3 && countLeaves(x)==4 | |
&& length(xx)==2 && countLeaves(xx)==8)); | |
})(); | |
//------Higher-order procedure------ | |
let map = (proc, seq) => { | |
if(isNil(seq)) return nil; | |
else return cons(proc(car(seq)), map(proc, cdr(seq))); | |
}; | |
let forEach = (proc, seq) => { | |
if(isNil(seq)) return; | |
else { | |
proc(car(seq)); | |
forEach(proc, cdr(seq)); | |
} | |
}; | |
let filter = (predicate, seq) => { | |
if(isNil(seq)) return nil; | |
else if(predicate(car(seq))) return cons(car(seq), filter(predicate, cdr(seq))); | |
else return filter(predicate, cdr(seq)); | |
}; | |
let accumulate = (op, initial, seq) => { | |
if(isNil(seq)) return initial; | |
else return op(car(seq), accumulate(op, initial, cdr(seq))); | |
}; | |
let enumerateInterval = (low, high) => { | |
if(low>high) return nil; | |
else return cons(low, enumerateInterval(low+1, high)); | |
}; | |
let enumerateTree = (tree) => { | |
if(isNil(tree)) return nil; | |
else if(!isPair(tree)) return list(tree); | |
else return append(enumerateTree(car(tree)), enumerateTree(cdr(tree))); | |
}; | |
//------Test------ | |
(function() { | |
let isOdd = (n) => n%2==1; | |
let square = (n) => n*n; | |
let plus = (a,b) => a+b; | |
let sumOddSquares = (tree) => accumulate(plus, 0, map(square, filter(isOdd, enumerateTree(tree)))); | |
let result = sumOddSquares(list(1, 2, 4, 3, 5)); | |
console.log("High-order procedures can work: " + (result==35)); | |
})(); | |
//------Solutions of ex2.33------ | |
(function () { | |
let map = (proc, seq) => accumulate((operand, ret) => cons(proc(operand), ret), nil, seq); | |
let test = list(1,2,3); | |
let correctResult = list(1,4,9); | |
console.log("Map as accumulations can work: " + isEqual(map((x) => x*x, test), correctResult)); | |
let append = (seq1, seq2) => accumulate(cons, seq2, seq1); | |
let test1 = list(4,5,6); | |
correctResult = list(1,2,3,4,5,6); | |
console.log("Append as accumulations can work: " + isEqual(append(test, test1), correctResult)); | |
let length = (seq) => accumulate((opperand, ret) => ret+1, 0, seq); | |
console.log("Length as accumulations can work: " + (length(test) == 3)); | |
})(); | |
//Export object in case not to pollute global enviroment | |
return { | |
//Base | |
cons: cons, | |
car: car, | |
cdr: cdr, | |
nil: nil, | |
isNil: isNil, | |
//List | |
cadr: cadr, | |
list: list, | |
isEqual: isEqual, | |
print: print, | |
listRef: listRef, | |
length: length, | |
append: append, | |
//Other | |
lastPair: lastPair, | |
reverse: reverse, | |
//Tree | |
isPair: isPair, | |
countLeaves: countLeaves, | |
//Sequence - Signals Flow | |
map: map, | |
forEach: forEach, | |
filter: filter, | |
accumulate: accumulate, | |
enumerateInterval: enumerateInterval, | |
enumerateTree: enumerateTree, | |
}; | |
/* | |
//flatten(1); | |
let flat = (seq) => { | |
let flatIter = (ret, rest) => { | |
if(isNil(rest)) return ret; | |
else return flatIter(ret.concat(car(rest)), cdr(rest)); | |
} | |
return flatIter([], seq); | |
}; | |
*/ | |
})(); | |
//------Usage------ | |
/* | |
with(sicp) { | |
let seq = list(1,2,3,4); | |
print(seq); | |
...Code here can use enviroment which was exported to object sicp. | |
} | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment