Last active
November 24, 2021 20:10
-
-
Save briancavalier/abc713b6806f37c687a6 to your computer and use it in GitHub Desktop.
Infinite "lists" in es6 using generators and yield
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
// Requires traceur | |
import { iterate, take, map, foldl } from './list-out-of-yield'; | |
// Sum of squares of integers 1..100 | |
var values = take(100, iterate(x => x + 1, 1)); | |
var result = foldl((x, y) => x + y, 0, map(x => x * x, values)); | |
console.log(result); |
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
// empty list | |
export function* empty() {} | |
// list containing all arguments | |
export function* list(...xs) { | |
yield* xs; | |
} | |
// list with head x, tail list | |
export function* cons(x, list) { | |
yield x, yield* list; | |
} | |
// concat lists a and b | |
export function* concat(a, b) { | |
yield* a, yield* b; | |
} | |
// infinite list of x, f(x), f(f(x)), etc. | |
export function* iterate(f, x) { | |
yield* unfold(x => [x, f(x)], x); | |
} | |
// infinite list by unfolding f, starting with x | |
export function* unfold(f, x) { | |
// No tail recursion yet, will blow the stack, oh well. | |
// See https://github.com/google/traceur-compiler/issues/218 | |
var [a, b] = f(x); | |
yield a, yield* unfold(f, b); | |
} | |
// infinite list of xs | |
export function* repeat(x) { | |
yield x, yield* repeat(x); | |
} | |
// fold list from the left | |
export function foldl(f, z, list) { | |
// This "loop" is a hack just to get the head of the list | |
// Is there a better way without resorting to using iterator.next()? | |
for(var x of list) { | |
return foldl(f, f(z, x), list); | |
} | |
return z; | |
} | |
// fold list from the right | |
export function foldr(f, z, list) { | |
// This "loop" is a hack just to get the head of the list | |
// Is there a better way without resorting to using iterator.next()? | |
for(var x of list) { | |
return f(x, foldr(f, z, list)); | |
} | |
return z; | |
} | |
export function* scanl(f, z, list) { | |
for(var x of list) { // loop :( | |
yield z = f(z, x); | |
} | |
return z; | |
} | |
// list containing at most first n elements | |
export function* take(n, list) { | |
yield* takeWhile(() => --n >= 0, list); | |
} | |
export function* takeWhile(f, list) { | |
for(var x of list) { // loop :( | |
if(!f(x)) { | |
break; | |
} | |
yield x; | |
} | |
} | |
export function* drop(n, list) { | |
yield* dropWhile(() => --n > 0, list); | |
} | |
export function* dropWhile(f, list) { | |
for(var x of list) { // loop :( | |
if(!f(x)) { | |
break; | |
} | |
} | |
yield* list; | |
} | |
// list of items transformed by applying f(x) | |
export function* map(f, list) { | |
for(var x of list) { // loop :( | |
yield f(x); | |
} | |
} | |
// apply all the fs to all the xs | |
export function* ap(fs, xs) { | |
// WARNING: Doesn't work if xs is a generator iterable, because | |
// generator iterables are not restartable | |
yield* chain(function*(f) { | |
yield* map(f, xs); | |
}, fs); | |
} | |
// aka flatMap/bind | |
export function* chain(f, list) { | |
for(var x of list) { // loop :( | |
yield* f(x); | |
} | |
} | |
// list containing only items where f(x) is truthy | |
export function* filter(f, list) { | |
for(var x of list) { // loop :( | |
if(f(x)) { | |
yield x; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@rauschma thanks! the comma was mostly just playing around to see in what interesting ways I could lay out the code. kinda liked the readability of the "yield head, yield tail" style, like a prose list, e.g. break, milk :)