new-generator
Version:
This new Generator is general purpose iterable generator.
320 lines (267 loc) • 8.14 kB
JavaScript
// new-generator.js
(function () {
;
var util = require('util');
var iterator = (typeof Symbol === 'function' &&
typeof Symbol.iterator === 'symbol') ? Symbol.iterator : undefined;
/**
* Generator. ジェネレータ
*
* @param generator
* - Generator Function
* - generator object that has 'next' function
* - Array, String, Arguments, and so on
* - Number or Range arguments
*/
function Generator(generator, to, step, boundary) {
if (!(this instanceof Generator))
return new Generator(generator, to, step, boundary);
var self = this;
// Generator Function
if (typeof generator === 'function' &&
generator.constructor.name === 'GeneratorFunction') {
this.generator = generator();
return;
}
if (iterator && generator && generator[iterator]) {
this.generator = generator[iterator]();
return;
}
// Number or Range arguments
if (generator instanceof Number || typeof generator === 'number') {
this.generator = range(Number(generator), to, step, boundary);
return;
}
// Array, String, Arguments, and so on...
var index, length;
if (generator instanceof Array ||
generator instanceof String ||
typeof generator === 'string' ||
typeof generator === 'object' &&
typeof generator.length === 'number') {
length = generator.length;
if (length < 0) throw new Error('length must be plus value');
index = 0;
this.generator = {
next: function next() {
if (index >= length)
return {done: true};
return {done: false, value: generator[index++]};
}
};
if (iterator)
this.generator[iterator] = function () { return self.generator; };
return;
}
// generator object that has 'next' function
if (typeof generator.next === 'function') {
this.generator = generator;
if (iterator && !this.generator[iterator])
this.generator[iterator] = function () { return self.generator; };
return;
}
throw new TypeError('Unsupported generator: ' +
typeof generator + ' ' + util.inspect(generator));
}
if (iterator) {
Object.defineProperty(Generator.prototype, iterator, {
configurable: true,
get: function () {
var self = this;
return function () { return this.generator; };
}
});
}
// next. ネクスト
Generator.prototype.next = function next() {
return this.generator.next();
}
// toArray. 配列に変換する
Generator.prototype.toArray = function toArray() {
var values = [];
//for (var value of this)
for (var n = this.next(); !n.done; n = this.next())
values.push(n.value);
return values;
}
// forEach.
Generator.prototype.forEach = function forEach(fn, ctx) {
if (typeof fn !== 'function')
throw new TypeError('must be a function');
//for (var value of this)
for (var n = this.next(); !n.done; n = this.next())
fn.call(ctx, n.value);
return;
}
Generator.prototype.each = Generator.prototype.forEach;
// every. 全て真であるかどうか
Generator.prototype.every = function every(fn, ctx) {
if (typeof fn !== 'function')
throw new TypeError('must be a function');
//for (var value of this)
for (var n = this.next(); !n.done; n = this.next()) {
if (!fn.call(ctx, n.value)) return false;
}
return true;
}
// some. どれかひとつでも真かどうか
Generator.prototype.some = function some(fn, ctx) {
if (typeof fn !== 'function')
throw new TypeError('must be a function');
//for (var value of this)
for (var n = this.next(); !n.done; n = this.next()) {
if (fn.call(ctx, n.value)) return true;
}
return false;
}
// range. 範囲
Generator.range = function range(from, to, step, boundary) {
return new Generator(range(from, to, step, boundary));
}
// count. カウント
Generator.count = function count(start, step) {
return new Generator(range(start, Infinity, step));
}
// chain. 連鎖
Generator.chain = function chain(/*...generators*/) {
var generators = Array.prototype.slice.call(arguments);
var length = generators.length;
for (var i = 0; i < length; ++i) {
if (!(generators[i] instanceof Generator))
generators[i] = new Generator(generators[i]);
}
var index = 0;
return new Generator({
next: function next() {
for (;;) {
if (index >= length)
return {done: true};
var n = generators[index].next();
if (!n.done) return n;
index++;
}
}
});
}
// filter. フィルタ
Generator.prototype.filter = function filter(fn, ctx) {
if (typeof fn !== 'function')
throw new TypeError('must be a function');
var self = this;
return new Generator({
next : function next() {
for (;;) {
var n = self.next();
if (n.done) return n;
if (fn.call(ctx, n.value)) return n;
}
}
})
}
// map. マップ
Generator.prototype.map = function map(fn, ctx) {
if (typeof fn !== 'function')
throw new TypeError('must be a function');
var self = this;
return new Generator({
next: function next() {
var n = self.next();
if (n.done) return n;
return {done: false, value: fn.call(ctx, n.value)};
}
});
}
// take. テイク
Generator.prototype.take = function take(n) {
if (typeof n !== 'number')
throw new TypeError('must be a number');
if (n < 0) throw new TypeError('must be plus value');
var self = this;
return new Generator({
next: function next() {
if (n <= 0) return {done: true};
n--;
return self.next();
}
});
}
// skip. スキップ
Generator.prototype.skip = function skip(n) {
if (typeof n !== 'number')
throw new TypeError('must be a number');
if (n < 0) throw new TypeError('must be plus value');
var self = this;
return new Generator({
next: function next() {
while (n > 0) {
self.next();
n--;
}
return self.next();
}
});
}
// reduce. リデュース(集約)
Generator.prototype.reduce = function reduce(fn, initial, ctx) {
if (typeof fn !== 'function')
throw new TypeError('must be a function');
var n = this.next();
var result;
if (typeof initial !== 'undefined' || arguments.length > 1)
result = fn.call(ctx, initial, n.value);
else
result = n.value;
if (n.done) return result;
for (var n = this.next(); !n.done; n = this.next())
result = fn.call(ctx, result, n.value);
return result;
}
// range. 範囲
// range(from, to, step, boundary)
// 5 -> 0..4
// 0, 5 -> 0..4
// 0, 5, 1 -> 0..4
// 5, 0 -> 5..1
// 5, 0, -1 -> 5..1
// 5, true -> 1..5
// 1, 5, true -> 1..5
// 1, 5, 1, true -> 1..5
// 5, 1, true -> 5..1
// 5, 1, -1, true -> 5..1
function range(from, to, step, boundary) {
if (typeof to === 'boolean')
boundary = to, to = step = undefined;
if (typeof step === 'boolean')
boundary = step, step = undefined;
if (typeof boundary !== 'boolean')
boundary = false;
if (typeof to === 'undefined')
to = from, from = boundary ? 1 : 0;
if (typeof step === 'undefined')
step = from <= to ? 1 : -1;
if (step === 0) throw new Error('step must not be zero');
if (from <= to) {
if (step <= 0) throw new Error('step must be plus value');
}
else {
if (step >= 0) throw new Error('step must be minus value');
}
var value = from;
var index = 0;
var length = Math.floor((to - from + (boundary? step: 0))/ step);
var generator = {
next: function next() {
if (index >= length)
return {done: true};
var result = {done: false, value: value};
value += step;
index++;
return result;
}
};
if (iterator)
generator[iterator] = function () { return generator; };
return generator;
}
exports = module.exports = Generator;
})();