Страницы

вторник, 22 декабря 2015 г.

ES6 Lazy vs Eager

Небольшой этюд на тему работы c объектами, реализующими протоколы iterable и iterator, принятые стандартом ECMAScript 6 (он же 2015). Попробуем реализовать "горячий" и "ленивый" варианты итерируемой коллекции, с итераторами (само собой), генераторами, символами (куда же без них) и прочими свистелками (нововведениями стандарта), а также добавим "немного экстрима" в финале, не пытайтесь повторить это дома :).


"Горячий" вариант:
var Eager = {
  map(fn) {
    var self = this;
    return Array.from(
      (function* () {
        for (var n of self) {
          yield fn(n);
        }
      })()
    );    
  },
  filter(fn) {    
    var self = this;    
    return Array.from(
      (function* () {
        for (var n of self) {
          if (fn(n)) yield n;
        }
      })()
    );
  },
  take(num) {
    var self = this;    
    return Array.from(
      (function* () {
        for (var n of self) {
          if (--num < 0) break;
          yield n;
        }
      })()
    );
  },
  toArray() {
    return Array.from(this);
  }
};
 
var digits = Object.assign({
  [Symbol.iterator]: () => {
    var n = 9, done;
    return {
      next: function() {
        done = done || n === 0;
        return {
          done, value: done ? undefined : n-- 
        };
      }
    };
  }
}, Eager);


"Ленивый" вариант:
var Lazy = {
  map(fn) {
    return Object.assign({
      [Symbol.iterator]: () => {
        var iterator = this[Symbol.iterator]();
        return {
          next: () => {
            var it = iterator.next(), done = it.done;
            return {
              done, value: done ? undefined : fn(it.value)
            };
          }
        }
      }
    }, Lazy);
  },  
  filter(fn) {
    return Object.assign({
      [Symbol.iterator]: () => {
        var iterator = this[Symbol.iterator]();
        return {
          next: () => {
            var it, done, value;
            do {
              it = iterator.next(), done = it.done, value = it.value;              
            } while (!done && !fn(value));
            return {
              done, value
            };
          }
        }
      }
    }, Lazy)
  },
  take(num) {
    return Object.assign({
      [Symbol.iterator]: () => {
        var iterator = this[Symbol.iterator]();
        return {
          next: () => {
            var it = iterator.next(), done = it.done || --num < 0;            
            return {
              done, value: done ? undefined : it.value
            };
          }
        }
      }
    }, Lazy);
  },  
  toArray() {
    return Array.from(this);
  }
};
 
var digits = Object.assign({
  [Symbol.iterator]: () => {
    var n = 9, done;
    return {
      next: function() {
        done = done || n === 0;
        return {
          done, value: done ? undefined : n-- 
        };
      }
    };
  }
}, Lazy);


"Немного экстрима":
var numbers = Object.assign({
  [Symbol.iterator]: () => {
    var n = 0, done = false;
    return {
      next: function() {        
        return {
          done, value: ++n
        };
      }
    };
  }
}, Lazy);

... или с помощью генератора:
var numbers = Object.assign({
  [Symbol.iterator]: function*() {
    var n = 0;
    while (true) {
      yield ++n;
    }
  }
}, Lazy);


Вот как-то так.