Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
`Reduced` можно же реализовать `take/limit` на подобии `filter`. Или я что-то упустил? `([[CurrentCtx]],[[Item]], [[Accessor]],[[RootCtx]], )`, где `Accessor` это индекс (в случае итерации по массиву).take(5), который выполняет полный перебор по коллекции из миллиона элементов — это очень грустно.Странно, вместо конструкции `Reduced` можно же реализовать `take/limit` на подобии `filter`. Или я что-то упустил?
var array = [1, 2, 3, 4, 5, 6, 7];
(function() {
var i = 0;
function reduce(coll, fn, seed) {
var result = seed;
for (; i < coll.length; i++) {
result = fn(result, coll[i]);
if (Reduced.isReduced(result)) {
return result.unwrap();
}
}
return result;
}
function Reduced(wrapped) {
this._wrapped = wrapped;
}
Reduced.prototype.unwrap = function() {
return this._wrapped;
}
Reduced.isReduced = function(obj) {
return (obj instanceof Reduced);
}
function take(n) {
return function(step) {
var count = 0;
return function(result, item) {
if (count++ < n) {
return step(result, item);
} else {
return new Reduced(result);
}
}
}
}
var first5T = take(5);
var result = reduce(array, first5T(append), []);
console.log('> Reduced. Loops: %d Result: ', ++i, result);
}());
(function() {
var i = 0;
function reduce(coll, fn, ctx) {
var prev = ctx;
for (; i < coll.length; i++) {
ctx = fn(ctx, coll[i]);
if (ctx === void 0)
return prev;
prev = ctx;
}
return ctx;
}
function take(n) {
return function(step) {
var count = 0;
return function(result, item) {
if (count++ < n)
return step(result, item);
}
}
}
var first5T = take(5);
var result = reduce(array, first5T(append), []);
console.log('> Void 0. Loops: %d Result: ', ++i, result);
}());
function append(ctx, x) {
return ctx.concat([x]);
}
take неоптимален. Попробуйте вот этот: function take(n) {
if (n==0)
return function(step) {
return function(result, item) {
return new Result(result);
}
}
else
return function(step) {
var count = 0;
return function(result, item) {
if (++count < n)
return step(result, item);
else
return new Reduced(step(result, item));
}
}
}
и добейтесь того же самого поведения с `void 0`> Reduced. Loops: 5 Result: [1, 2, 3, 4, 5]> Void 0. Loops: 6 Result: [1, 2, 3, 4, 5] function take(n) {
return function(step) {
var count = 0;
return function(result, item) {
if (++count < n)
return step(result, item);
else if (count === n)
step(result, item);
}
}
}
function append(ctx, x) {
ctx.push(x);
return ctx;
}
function * gen1(collection, limit = NaN) {
let counter = 0;
for ( let item of collection ) {
yield `item is ${ item }`;
if ( ++counter >= limit ) break;
}
}
function * gen2(...collections) {
for ( let collection of collections ) {
yield * collection;
if ( (yield 'end collection') == true ) break;
}
}
let test = gen2( gen1([1, 2, 3, 4, 5, 6, 7], 3), [5, 6, 7], gen1(['a', 'b', 'c']), new Array(999).join('1|').split('|') );
let counter = 0, sendValue = void 0;
do {
let {value, done} = test.next(sendValue);
if ( value == 'end collection' ) {
if ( ++counter > 2 ) sendValue = true;
}
else console.log(value);
}
while( done != true );
function * gen1(collection, limit = NaN) {
let counter = 0;
for ( let item of collection ) {
yield `item is ${ item }`;
if ( ++counter >= limit ) break;
}
}
function * gen2(...collections) {
for ( let collection of collections ) {
yield * collection;
if ( (yield 'end collection') == true ) break;
}
}
let test = gen2( gen1([1, 2, 3, 4, 5, 6, 7], 3), [5, 6, 7], gen1(['a', 'b', 'c']), new Array(999).join('1|').split('|') );
let counter = 0, sendValue = void 0;
let value, done;
do {
({value, done}) = test.next(sendValue);
if ( value == 'end collection' ) {
if ( ++counter > 2 ) sendValue = true;
}
else console.log(value);
}
while( done != true );
function * gen1(collection, limit = NaN) {
let counter = 0;
for ( let item of collection ) {
yield 'item is ' + item;
if ( ++counter >= limit ) break;
}
}
function * gen2(...collections) {
for ( let collection of collections ) {
yield * collection;
if ( (yield 'end collection') == true ) break;
}
}
let test = gen2( gen1([1, 2, 3, 4, 5, 6, 7], 3), [5, 6, 7], gen1(['a', 'b', 'c']), new Array(999).join('1|').split('|') );
let counter = 0, sendValue = void 0;
let value, done;
do {
({value, done}) = test.next(sendValue);
if ( value == 'end collection' ) {
if ( ++counter > 2 ) sendValue = true;
}
else console.log(value);
}
while( done != true );
reduce([1, 2, 3, 4, 5, 6, 7], first5T(append), []); // => [1, 2, 3, 4, 5]
function Stream(transducer, subscriber) {
this._step = transducer(function(result, item) {
if (arguments.length === 2) { subscriber(item) }
});
}
Stream.prototype.push = function(x) {
this._step(null, x);
}
stream = new Stream(first5T, function(x) { console.log('>>', x) });
stream.push(1);
stream.push(2);
stream.push(3);
stream.push(4);
stream.push(5);
stream.push(6);
stream.push(7);
step() без параметров можно вызывать только один раз — в ответ на аналогичный вызов. Если разрешить обратное, то область применения трансдьюсеров сильно сократится.flatten является обратным к partition — что логично. Но если разрешить трансдьюсеру создавать любое число новых коллекций, то это свойство перестанет выполняться для коллекци общего вида, ведь partition будет знать, как создать произвольную коллекцию благодаря step() — а flatten не будет знать, как ее итерировать!partition к потоку — то он насоздает новых потоков, что совершенно бессмысленно, поскольку все элементы в этих потоках появятся раньше, чем возможность на поток подписаться.partition, который умеет создавать только массивы, для потоков данных будет очень даже применим.Тоже самое и для step() с одним параметром, его можно, и нужно, вызывать только если ваш step вызвали с одним параметром.Стоп. А как же раннее прерывание?
return new Reduced(result) или return new Reduced(step(result, item)) писать нельзя, надо писать return new Reduced(step(result)) и return new Reduced(step(step(result, item))) соответственно.console.log(reduceList([1,2,3,4,5], take(3))); // [1,2,3]Что произошло? Take досрочно прервал цикл — и потому не append не дождался конца коллекции.
console.log(reduceList([1,2,3], append(6,7))); // [1,2,3,6,7]
console.log(reduceList([1,2,3,4,5], take(3), append(6,7))); // [1,2,3] — Может, мы перепутали аргументы местами?..
console.log(reduceList([1,2,3,4,5], append(6,7), take(3))); // [1,2,3] — WTF?!
console.log(reduceList([1,2,3,4,5], append(), take(3))); // [1,2,3]Что произошло? Take прервал цикл — но append этого не понял. Получается, что любые трансдьюсеры, которые вставляют данные в середину потока, обязаны тоже уметь обрабатывать досрочное окончание этого самого потока. Что-то мне от протокола плохо…
console.log(reduceList([1,2,3,4], append(5), take(3))); // [1,2,3]
console.log(reduceList([1,2,3], append(4,5), take(3))); // [1,2,3]
console.log(reduceList([1,2], append(3,4,5), take(3))); // Uncaught TypeError: undefined is not a function — WTF?!
Еще придется добавлять проверку Reduced.isReduced(result) в трансдьюсеры, которые несколько раз вызывают step (прим. flatten).
function Stream(transducer, onValue, onEnd) {
this._onEnd = onEnd;
this._closed = false;
this._step = transducer(function(result, item) {
if (arguments.length === 2) { onValue(item) }
});
this._step();
}
Stream.prototype.push = function(x) {
if (!this._closed) {
if (isReduced(this._step(null, x)) {
this._closed = true;
this._onEnd();
}
}
}
Stream.prototype.close = function() {
if (!this._closed) {
this._step(null);
this._closed = true;
this._onEnd();
}
}
if (result instanceof Reduced)
return result.wrapped;
else
return step(result);
partition как пример функции добавляющей в конец. Всё работатет. Походу, все наши примеры использования Reduced были некорректнымиНаверно вы правы. Спасибо!
transduce([1, 2, 3, 4, 5, 6, 7, 2, 3, 1], [], [
filter(function (x) { return x < 4}),
map(function (x) {return -x},
take(5),
append
]);
Трансдьюсеры в JavaScript. Часть вторая