Last active
November 20, 2019 09:01
-
-
Save odf/fe014d74ff1ce373e7a88f6e46f0af8a to your computer and use it in GitHub Desktop.
Flattening an infinite list of infinite lists in JavaScript via Cantor diagonalisation
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
class Seq { | |
toArray() { | |
const a = []; | |
for (const x of this) | |
a.push(x); | |
return a; | |
} | |
toString() { | |
return this.toArray().join(' -> '); | |
} | |
map(fn) { | |
return this.isNil ? nil : | |
cons(fn(this.first()), () => this.rest().map(fn)); | |
} | |
take(n) { | |
if (this.isNil || n <= 0) | |
return nil; | |
else | |
return cons(this.first(), () => this.rest().take(n - 1)); | |
} | |
split(fill=null) { | |
if (this.isNil) | |
return [this, fill]; | |
else | |
return [this.first(), this.rest()]; | |
} | |
}; | |
Seq.prototype[Symbol.iterator] = function*() { | |
let s = this; | |
while (!s.isNil) { | |
yield s.first(); | |
s = s.rest(); | |
} | |
}; | |
export const nil = new Seq(); | |
nil.isNil = true; | |
nil.first = () => { throw new Error('empty sequence has no first element'); }; | |
nil.rest = () => nil; | |
class Cons extends Seq { | |
constructor(firstVal, restFn) { | |
super(); | |
this.isNil = false; | |
this.first = () => firstVal; | |
this.rest = () => { | |
const val = restFn && restFn(); | |
this.rest = () => val; | |
return val; | |
} | |
} | |
} | |
export const cons = (firstVal, restFn) => new Cons(firstVal, restFn); | |
export const upFrom = start => cons(start, () => upFrom(start + 1)); | |
export function cantor(lists) { | |
let unused = lists; | |
let active = []; | |
let next = 0; | |
function _cantor() { | |
if (next >= active.length) { | |
const [xs, rest] = unused.split(nil); | |
active.push(xs); | |
unused = rest; | |
next = 0; | |
} | |
const [x, rest] = active[next].split(); | |
active[next] = rest; | |
next += 1; | |
return cons(x, () => _cantor()); | |
} | |
return _cantor(); | |
} | |
if (require.main == module) { | |
const test = t => { | |
const s = eval(t); | |
console.log(`${t}:\n ${s}`); | |
console.log(); | |
}; | |
test('nil'); | |
test('upFrom(10).take(5)'); | |
test('cantor(upFrom(1).map(x => upFrom(1).map(y => [x, y]))).take(25)'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment