Skip to content

Instantly share code, notes, and snippets.

@nickfargo
Last active August 29, 2015 14:00
Show Gist options
  • Select an option

  • Save nickfargo/11277704 to your computer and use it in GitHub Desktop.

Select an option

Save nickfargo/11277704 to your computer and use it in GitHub Desktop.
operators = require './operators'



module.exports =
class Sequence

  { generatorOf } = operators

Constructor

  constructor: ( @generator ) ->

Static functions

  @from = ( iterable ) -> new Sequence generatorOf iterable

  for name, fn of operators['value -> value']
    @[name] = fn

  for name, fn of operators['value -> sequence']
    @[name] = do ( fn ) -> -> new Sequence fn.apply null, arguments

Methods

  for name, fn of operators['sequence -> sequence']
    @::[name] = do ( fn ) -> ( args... ) -> new Sequence fn args..., this

  __iterator__: -> @generator.apply this, arguments

  apply: ( context, args ) -> @generator.apply context, args
  call: ( context, args... ) -> @generator.call context, args

  evaluate: ( limit = Infinity ) ->
    out = []
    iterator = @generator()
    i = 0; while i++ < limit
      { value, done } = iterator.next()
      break if done
      out.push value
    out

Examples

  @examples = [
    ->
      Sequence
        .iterate Sequence.increment, 4
        .map (x) -> x * x
        .take 5
        .drop 2
        .evaluate()
  ]
{ throwIteratorFinished } = require './helpers'

ArrayIterator = require './array-iterator'



generatorOf = ( iterable ) -> ->
  iterable?() or
  iterable.__iterator__?() or
  ( iterable if typeof iterable.next is 'function' ) or
  new ArrayIterator iterable


negationOf = ( predicate ) -> -> not predicate.apply this, arguments


executable = ( object ) ->
  if typeof object.call is 'function'
    object
  else
    generatorOf object

value → value

identity  = (x) -> x
increment = (x) -> x + 1
decrement = (x) -> x - 1
isEven    = (x) -> x % 2 is 0
isOdd     = (x) -> y = x % 2; y is 1 or y is -1

sum = ->
  x = 0; i = 0; while i < arguments.length
    x += arguments[i++]
  x

multiply = ->
  x = 1; i = 0; while i < arguments.length
    x *= arguments[i++]
  x

value → sequence

iterate = ( fn, seed ) -> ->
  value = seed
  out = { value, done: no }
  next: ->
    out.value = value
    out.done = no
    value = fn value
    out


repeat = ( value ) -> ->
  out = { value, done: no }
  next: -> out

sequence → sequence

filter = ( predicate, sequence ) -> ->
  source = ( executable sequence ).call null
  value = undefined
  done = no
  out = { value, done }
  iterator =
    next: ->
      if done then do throwIteratorFinished else loop
        { value, done } = source.next()
        if out.done = done
          iterator.next = throwIteratorFinished
          out.value = value = undefined
        else if predicate value
          out.value = value
        else continue
        return out


remove = ( predicate, sequence ) ->
  filter ( negationOf predicate ), sequence


map = ( fn, sequence ) -> ->
  source = ( executable sequence ).call null
  out = value: undefined, done: no
  iterator =
    next: ->
      do throwIteratorFinished if done
      { value, done } = source.next()
      if out.done = done
        iterator.next = throwIteratorFinished
        out.value = value = undefined
      else
        out.value = fn value
      out


take = ( amount, sequence ) ->
  takeWhile ( ( v, i ) -> i < amount ), sequence


takeLast = ( amount, sequence ) ->
  takeUntil ( ( v, i ) -> i < amount ), sequence


takeWhile = ( predicate, sequence ) -> ->
  source = ( executable sequence ).call null
  count = 0
  value = undefined
  done = no
  out = { value, done }
  iterator =
    next: ->
      do throwIteratorFinished if done
      { value, done } = source.next()
      if not done and predicate value, count++
        out.value = value
        out.done = no
      else
        iterator.next = throwIteratorFinished
        out.value = value = undefined
        out.done = done = yes
      out


takeUntil = ( predicate, sequence ) ->
  takeWhile ( negationOf predicate ), sequence


drop = ( amount, sequence ) ->
  dropWhile ( ( v, i ) -> i < amount ), sequence


dropLast = ( amount, sequence ) ->
  dropUntil ( ( v, i ) -> i < amount ), sequence


dropWhile = ( predicate, sequence ) -> ->
  source = ( executable sequence ).call null
  count = 0
  value = undefined
  done = no

  while not done and predicate value, count++
    { value, done } = source.next()

  out = { value, done }
  iterator =
    next: ->
      do throwIteratorFinished if done
      { value, done } = source.next()
      if done
        iterator.next = throwIteratorFinished
        out.value = value = undefined
        out.done = done = yes
      else
        out.value = value
        out.done = no
      out


dropUntil = ( predicate, sequence ) ->
  dropWhile ( negationOf predicate ), sequence




module.exports = {
  negationOf
  generatorOf
  executable
}

categories =
  'value -> value': {
    identity
    increment
    decrement
    isEven
    isOdd
    sum
    multiply
  }
  'value -> sequence': {
    iterate
    repeat
  }
  'sequence -> sequence': {
    filter
    remove
    map
    take
    takeLast
    takeWhile
    takeUntil
    drop
    dropLast
    dropWhile
    dropUntil
  }

for categoryName, category of categories
  module.exports[ categoryName ] = category
  for name, fn of category
    module.exports[name] = fn
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment