Skip to content

Instantly share code, notes, and snippets.

@benji6
Last active April 28, 2016 22:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benji6/ce2fd2c058ec5853f8d43cd6b94b3b10 to your computer and use it in GitHub Desktop.
Save benji6/ce2fd2c058ec5853f8d43cd6b94b3b10 to your computer and use it in GitHub Desktop.
Lazy iterables in JS
// imlazy show and tell
// utils
const compose = (...fns) => x => fns.reduceRight((acc, f) => f(acc), x)
const log = (x, ...xs) => (console.log(...[x + ':', '\n', ...xs, '\n']))
// Generator expression
const oneTwoThreeGenerator = function * () {
yield 1
yield 2
yield 3
}
// Invoke generator to create iterator
const oneTwoThreeIterator = oneTwoThreeGenerator()
// Iterator has next method which returns an Object
log(
'naturalNumbersIterator',
oneTwoThreeIterator.next(),
oneTwoThreeIterator.next(),
oneTwoThreeIterator.next(),
oneTwoThreeIterator.next(),
oneTwoThreeIterator.next()
)
log('oneTwoThreeIterator can only be iterated once', [...oneTwoThreeIterator])
log('oneTwoThreeIterator newly generated', [...oneTwoThreeGenerator()])
// Iterables
const oneTwoThreeIterable = {[Symbol.iterator]: oneTwoThreeGenerator}
log('oneTwoThreeIterable', [...oneTwoThreeIterable])
log('oneTwoThreeIterable can be used multiple times', [...oneTwoThreeIterable])
// range
const range = x => y => ({[Symbol.iterator]: function * () {
var n = x
if (n < y) while (n <= y) yield n++; else while (n >= y) yield n--
}})
const oneTwoThree = range(1)(3)
log('oneTwoThree created by range', [...oneTwoThree])
const naturalNumbers = range(1)(Infinity)
const take = n => xs => ({[Symbol.iterator]: function * () {
let i = n
for (const x of xs) if (i-- === 0) return; else yield x
}})
const take8 = take(8)
const take16 = take(16)
const first8NaturalNumbers = take8(naturalNumbers)
log('first8NaturalNumbers', [...first8NaturalNumbers])
// first 8 even
const even = x => x % 2 === 0
const filter = f => xs => ({[Symbol.iterator]: function * () {
for (const x of xs) if (f(x)) yield x
}})
const filterEven = filter(even)
const first8Even = compose(take8, filterEven)
const first8EvenNaturalNumbers = first8Even(naturalNumbers)
log('first8EvenNaturalNumbers', [...first8EvenNaturalNumbers])
// square
const squared = x => Math.pow(x, 2)
const map = f => xs => ({[Symbol.iterator]: function * () {
for (const x of xs) yield f(x)
}})
const mapSquared = map(squared)
const first8EvenSquared = compose(mapSquared, first8Even)
log('first8EvenSquared', [...first8EvenSquared(naturalNumbers)])
// less than
const lt = x => y => x > y
const takeWhile = f => xs => ({[Symbol.iterator]: function * () {
for (const x of xs) if (f(x)) yield x; else return
}})
const takeWhileLt100 = takeWhile(lt(100))
const evenSquaredLt100 = compose(takeWhileLt100, mapSquared, filterEven)
log('evenSquaredLt100', [...evenSquaredLt100(naturalNumbers)])
// iterate
const double = x => 2 * x
const iterate = f => n => ({[Symbol.iterator]: function * () {
let x = n
yield x
while (true) yield x = f(x)
}})
const iterateDouble = iterate(double)
const powersOf2Lt100 = takeWhileLt100(iterateDouble(2))
log('powersOf2Lt100', [...powersOf2Lt100])
const cycle = xs => ({[Symbol.iterator]: function * () {
while (true) yield* xs
}})
const cycledPowersOf2Lt100 = cycle(powersOf2Lt100)
log('cycledPowersOf2Lt100', [...take16(cycledPowersOf2Lt100)])
// fibonacci numbers
const fibonacciNumbers = ({[Symbol.iterator]: function * () {
let [x, y] = [1, 1]
while (true) {
yield x
;[x, y] = [y, x + y]
}
}})
const first16FibonacciNumbers = take16(fibonacciNumbers)
log('first16FibonacciNumbers', [...first16FibonacciNumbers])
const sum = xs => {
let total = 0
for (const x of xs) total += x
return total
}
// sum
const sumFirst16FibonacciNumbers = sum(first16FibonacciNumbers)
log('sumFirst16FibonacciNumbers', sumFirst16FibonacciNumbers)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment