https://github.com/Gozala/reducers
Tip revision: 4e743321440a426d44c6ce03c05339e73c4517a5 authored by Irakli Gozalishvili on 24 October 2012, 19:47:47 UTC
Version 0.1.2
Version 0.1.2
Tip revision: 4e74332
sequence.js
/* vim:set ts=2 sw=2 sts=2 expandtab */
/*jshint asi: true undef: true es5: true node: true browser: true devel: true
forin: true latedef: false globalstrict: true */
'use strict';
var Method = require('method')
var count = Method()
exports.count = count
var first = Method()
exports.first = first
var rest = Method()
exports.rest = rest
var isEmpty = Method()
exports.isEmpty = isEmpty
isEmpty.define(Array, function(array) { return array.length === 0 })
count.define(Array, function(array) { return array.length })
first.define(Array, function(array) { return array[0] })
rest.define(Array, function(array) { return array.slice(1) })
function expand(form) {
return form.operation.apply(form, form.body)
}
function Sequence(head, tail) {
this.head = head
this.tail = tail
}
Sequence.prototype.toString = function() {
var value = '', tail = this;
while (!isEmpty(tail)) {
value = value + ' ' + first(tail)
tail = rest(tail)
}
return '(' + value.substr(1) + ')'
}
Sequence.head = function head(sequence) {
return sequence.head
}
Sequence.tail = function tail(sequence) {
var form = sequence.tail
return form.isExpandable ? (sequence.tail = expand(form)) :
form.tail
}
first.define(Sequence, Sequence.head)
rest.define(Sequence, Sequence.tail)
isEmpty.define(Sequence, function() { return false })
count.define(Sequence, function(sequence) {
return count(rest(sequence)) + 1
})
function LazySequence(operation, body) {
this.operation = operation
this.body = body
this.isExpandable = true
}
LazySequence.prototype = Object.create(Sequence.prototype)
;[ first, rest, count, isEmpty ].forEach(function(method) {
method.define(LazySequence, function(form) {
var sequence = expand(form)
delete form.isExpandable
delete form.operation
delete form.body
if (isEmpty(sequence)) {
isEmpty.implement(form, function() { return true })
} else {
form.head = sequence.head
form.tail = sequence.tail
first.implement(form, Sequence.head)
rest.implement(form, Sequence.tail)
}
return method(sequence)
})
})
function cons(head, rest) {
return new Sequence(head, rest)
}
exports.cons = cons
function lazy(operation) {
return function transform() {
return new LazySequence(operation, arguments)
}
}
exports.lazy = lazy
function next(source) {
return rest(source)
}
exports.next = next
function second(source) {
return first(rest(source))
}
exports.second = second
function third(source) {
return first(rest(rest(source)))
}
exports.third = third
var filter = lazy(function(source, f) {
return isEmpty(source) ? source :
f(first(source)) ? cons(first(source), filter(rest(source), f)) :
filter(rest(source), f)
})
exports.filter = filter
var map = lazy(function(source, f) {
return isEmpty(source) ? source :
cons(f(first(source)), map(rest(source), f))
})
exports.map = map
var take = lazy(function(source, n) {
return isEmpty(source) ? source :
n <= 0 ? [] :
cons(first(source), take(rest(source), n - 1))
})
exports.take = take
var drop = lazy(function(source, n) {
return isEmpty(source) ? source :
n <= 0 ? source :
drop(rest(source), n - 1)
})
exports.drop = drop
var takeWhile = lazy(function(source, predicate) {
var head = first(source)
return isEmpty(source) ? source :
predicate(head) ? cons(head, takeWhile(rest(source), predicate)) :
[]
})
exports.takeWhile = takeWhile
var dropWhile = lazy(function dropWhile(source, predicate) {
var head = first(source)
return isEmpty(source) ? source :
predicate(head) ? dropWhile(rest(source), predicate) :
rest(source)
})
exports.dropWhile = dropWhile