Отображение функции на генератор в JavaScript
У меня есть генератор под названием generateNumbers
в JavaScript и другой генератор generateLargerNumbers
который принимает каждое значение, сгенерированное generateNumbers
и применяет функцию addOne
к нему как таковой:
function addOne(value) {
return value + 1
}
function* generateNumbers() {
yield 1
yield 2
yield 3
}
function* generateLargerNumbers() {
for (const number of generateNumbers()) {
yield addOne(number)
}
}
Есть ли более краткий способ сделать это без создания массива из сгенерированных значений? Я думаю что-то вроде:
function* generateLargerNumbers() {
yield* generateNumbers().map(addOne) // obviously doesn't work
}
4 ответа
Там нет встроенного способа для отображения на Generator
объекты, но вы можете свернуть свою собственную функцию:
const Generator = Object.getPrototypeOf(function* () {});
Generator.prototype.map = function* (mapper, thisArg) {
for (const val of this) {
yield mapper.call(thisArg, val);
}
};
Теперь вы можете сделать:
function generateLargerNumbers() {
return generateNumbers().map(addOne);
}
const Generator = Object.getPrototypeOf(function* () {});
Generator.prototype.map = function* (mapper, thisArg) {
for (const val of this) {
yield mapper.call(thisArg, val);
}
};
function addOne(value) {
return value + 1
}
function* generateNumbers() {
yield 1
yield 2
yield 3
}
function generateLargerNumbers() {
return generateNumbers().map(addOne)
}
console.log(...generateLargerNumbers())
генераторы высшего порядка
Вы можете сами управлять функциями генератора
const Generator =
{
map: (f,g) => function* (...args)
{
for (const x of g (...args))
yield f (x)
},
filter: (f,g) => function* (...args)
{
for (const x of g (...args))
if (f (x))
yield x
}
}
// some functions !
const square = x =>
x * x
const isEven = x =>
(x & 1) === 0
// a generator !
const range = function* (x = 0, y = 1)
{
while (x < y)
yield x++
}
// higher order generator !
for (const x of Generator.map (square, Generator.filter (isEven, range)) (0,10))
console.log('evens squared', x)
итераторы высшего порядка
Или вы можете управлять итераторами
const Iterator =
{
map: (f, it) => function* ()
{
for (const x of it)
yield f (x)
} (),
filter: (f, it) => function* ()
{
for (const x of it)
if (f (x))
yield x
} ()
}
// some functions !
const square = x =>
x * x
const isEven = x =>
(x & 1) === 0
// a generator !
const range = function* (x = 0, y = 1)
{
while (x < y)
yield x++
}
// higher-order iterators !
for (const x of Iterator.map (square, Iterator.filter (isEven, range (0, 10))))
console.log('evens squared', x)
рекомендация
В большинстве случаев я считаю более практичным манипулировать итератором из-за его четко определенного (хотя и не очень) интерфейса. Это позволяет вам сделать что-то вроде
Iterator.map (square, Iterator.filter (isEven, [10,11,12,13]))
Тогда как другой подход
Generator.map (square, Generator.filter (isEven, Array.from)) ([10,11,12,13])
У обоих есть сценарий использования, но я считаю, что первое намного приятнее, чем второе
постоянные итераторы
Состоящие итераторы JavaScript раздражают меня - каждый последующий вызов .next
изменяет внутреннее состояние необратимо.
Но! ничто не мешает вам создавать свои собственные итераторы, а затем создавать адаптер для подключения к механизму безопасного создания стека в JavaScript
Если вас это интересует, вам могут понравиться другие сопровождающие примеры, найденные здесь: Цикл к структуре файловой системы в моем объекте, чтобы получить все файлы
Единственное преимущество не в том, что мы можем повторно использовать постоянный итератор, а в том, что в этой реализации последующее чтение даже быстрее, чем первое из-за запоминания - оценка: JavaScript 0, постоянные итераторы 2
// -------------------------------------------------------------------
const Memo = (f, memo) => () =>
memo === undefined
? (memo = f (), memo)
: memo
// -------------------------------------------------------------------
const Yield = (value, next = Return) =>
({ done: false, value, next: Memo (next) })
const Return = value =>
({ done: true, value })
// -------------------------------------------------------------------
const MappedIterator = (f, it = Return ()) =>
it.done
? Return ()
: Yield (f (it.value), () => MappedIterator (f, it.next ()))
const FilteredIterator = (f, it = Return ()) =>
it.done
? Return ()
: f (it.value)
? Yield (it.value, () => FilteredIterator (f, it.next ()))
: FilteredIterator (f, it.next ())
// -------------------------------------------------------------------
const Generator = function* (it = Return ())
{
while (it.done === false)
(yield it.value, it = it.next ())
return it.value
}
// -------------------------------------------------------------------
const Range = (x = 0, y = 1) =>
x < y
? Yield (x, () => Range (x + 1, y))
: Return ()
const square = x =>
x * x
const isEven = x =>
(x & 1) === 0
// -------------------------------------------------------------------
for (const x of Generator (MappedIterator (square, FilteredIterator (isEven, Range (0,10)))))
console.log ('evens squared', x)
Как насчет составления объекта итератора вместо использования вложенных генераторов?
function* generateNumbers(){
yield 1;
yield 2;
yield 3;
}
function generateGreaterNumbers(){
return { next(){ var r = this.gen.next(); r.value+=1; return r; }, gen: generateNumbers() };`
}
Если вам действительно нужно передать значения в ваш генератор, то вы не можете сделать это с помощью for...of, вы должны передать каждое значение через
const mapGenerator = (generatorFunc, mapper) =>
function*(...args) {
let gen = generatorFunc(...args),
i = 0,
value;
while (true) {
const it = gen.next(value);
if (it.done) return mapper(it.value, i);
value = yield mapper(it.value, i);
i++;
}
};
function* generator() {
console.log('generator received', yield 1);
console.log('generator received', yield 2);
console.log('generator received', yield 3);
return 4;
}
const mapGenerator = (generatorFunc, mapper) =>
function*(...args) {
let gen = generatorFunc(...args),
i = 0,
value;
while (true) {
const it = gen.next(value);
if (it.done) return mapper(it.value, i);
value = yield mapper(it.value, i);
i++;
}
};
const otherGenerator = mapGenerator(generator, x => x + 1)
const it = otherGenerator();
console.log(
it.next().value,
it.next('a').value,
it.next('b').value,
it.next('c').value
);