๋ฐ๋ณต๊ธฐ ๋ฐ ์์ฑ๊ธฐ
์ปฌ๋ ์
๋ด ๊ฐ ํญ๋ชฉ ์ฒ๋ฆฌ๋ ๋งค์ฐ ํํ ์ฐ์ฐ์
๋๋ค. JavaScript๋ ๊ฐ๋จํ for ๋ฃจํ์์ map() ๋ฐ filter()์ ์ด๋ฅด๊ธฐ๊น์ง, ์ปฌ๋ ์
์ ๋ฐ๋ณตํ๋ ๋ง์ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. ๋ฐ๋ณต๊ธฐ(iterator) ๋ฐ ์์ฑ๊ธฐ(generator)๋ ๋ฐ๋ณต ๊ฐ๋
์ ํต์ฌ ์ธ์ด ๋ด๋ก ๋ฐ๋ก ๊ฐ์ ธ์ for...of ๋ฃจํ์ ๋์(behavior)์ ์ฌ์ฉ์ ์ ์ํ๋ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํฉ๋๋ค.
์์ธํ ๋ด์ฉ์, ๋ค์์ ์ฐธ์กฐํ์ธ์:
๋ฐ๋ณต์
JavaScript์์ ๋ฐ๋ณต์(Iterator)๋ ์ํ์ค๋ฅผ ์ ์ํ๊ณ ์ข
๋ฃ์์ ๋ฐํ๊ฐ์ ์ ์ฌ์ ์ผ๋ก ์ ์ํ๋ ๊ฐ์ฒด์
๋๋ค. ๋ ๊ตฌ์ฒด์ ์ผ๋ก ๋งํ์๋ฉด, ๋ฐ๋ณต์๋ ๋ ๊ฐ์ ์์ฑ( value, done)์ ๋ฐํํ๋ next() ๋ฉ์๋ ์ฌ์ฉํ์ฌ ๊ฐ์ฒด์ Iterator protocol์ ๊ตฌํํฉ๋๋ค. ์ํ์ค์ ๋ง์ง๋ง ๊ฐ์ด ์ด๋ฏธ ์ฐ์ถ๋์๋ค๋ฉด done ๊ฐ์ true ๊ฐ ๋ฉ๋๋ค. ๋ง์ฝ value๊ฐ์ด done ๊ณผ ํจ๊ป ์กด์ฌํ๋ค๋ฉด, ๊ทธ๊ฒ์ ๋ฐ๋ณต์์ ๋ฐํ๊ฐ์ด ๋ฉ๋๋ค.
๋ฐ๋ณต์๋ฅผ ์์ฑํ๋ฉด next() ๋ฉ์๋๋ฅผ ๋ฐ๋ณต์ ์ผ๋ก ํธ์ถํ์ฌ ๋ช
์์ ์ผ๋ก ๋ฐ๋ณต์ํฌ ์ ์์ต๋๋ค. ๋ฐ๋ณต์๋ฅผ ๋ฐ๋ณต์ํค๋ ๊ฒ์ ์ผ๋ฐ์ ์ผ๋ก ํ ๋ฒ์ฉ๋ง ํ ์ ์๊ธฐ ๋๋ฌธ์, ๋ฐ๋ณต์๋ฅผ ์๋ชจ์ํค๋ ๊ฒ์ด๋ผ๊ณ ํ ์ ์์ต๋๋ค. ๋ง์ง๋ง ๊ฐ์ ์ฐ์ถํ๊ณ ๋์ next()๋ฅผ ์ถ๊ฐ์ ์ผ๋ก ํธ์ถํ๋ฉด {done: true}. ๊ฐ ๋ฐํ๋ฉ๋๋ค.
JavaScript์์ ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ๋ฐ๋ณต์๋ ๋ฐฐ์ด ๋ฐ๋ณต์๋ก, ๋ฐฐ์ด์ ๊ฐ ๊ฐ์ ์์๋๋ก ๋ฐํํฉ๋๋ค. ๋ชจ๋ ๋ฐ๋ณต์๊ฐ ๋ฐฐ์ด๋ก ํํ๋ ์ ์๋ค๊ณ ์์ํ ์ ์์ง๋ง , ์ด๊ฒ์ ์ฌ์ค์ ์๋๋๋ค. ๋ฐฐ์ด์ ์์ ํ ํ ๋น๋์ด์ผ ํ์ง๋ง, ๋ฐ๋ณต์๋ ํ์ํ๋งํผ๋ง ์๋ชจ๋๋ฏ๋ก ๋ฌด์ ํ ์ํ์ค๋ก ํํํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํ ๋ฉด 0๋ถํฐ ๋ฌดํ๋์ฌ์ด์ ์ ์๋ฒ์์ฒ๋ผ ๋ง์ด์ฃ .
์ฌ๊ธฐ์ ์ค์ตํ ์ ์๋ ์์ ๊ฐ ์์ต๋๋ค. start์์ end๊น์ง step ์ ๋งํผ ๋์ด์ง ์ ์ ์ํ์ค๋ฅผ ์ ์ํ๋ ๊ฐ๋จํ ๋ฒ์ ๋ฐ๋ณต์๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. ์ต์ข
์ ์ผ๋ก ์ํ์ค์ ํฌ๊ธฐ๊ฐ ๋ฐํ๋ฉ๋๋ค.
function makeRangeIterator(start = 0, end = Infinity, step = 1) {
var nextIndex = start;
var n = 0;
var rangeIterator = {
next: function () {
var result;
if (nextIndex < end) {
result = { value: nextIndex, done: false };
} else if (nextIndex == end) {
result = { value: n, done: true };
} else {
result = { done: true };
}
nextIndex += step;
n++;
return result;
},
};
return rangeIterator;
}
์์ ๋ฐ๋ณต์๋ฅผ ์ฌ์ฉํ๋ฉด ์๋์ ๊ฐ์ต๋๋ค:
var it = makeRangeIterator(1, 4);
var result = it.next();
while (!result.done) {
console.log(result.value); // 1 2 3
result = it.next();
}
console.log("Iterated over sequence of size: ", result.value);
It is not possible to know reflectively whether a particular object is an iterator. If you need to do this, use Iterables.
Generator functions
์ ๋ง๋ค์ด์ง ๋ฐ๋ณต์(Iterator)๋ ์ ์ฉํ ๋๊ตฌ์ธ ๋ฐ๋ฉด, ์ด๊ฒ์ ์์ฑํ ๋๋ ์ฃผ์ํด์ ํ๋ก๊ทธ๋๋ฐ์ ํด์ผ ํ๋๋ฐ, ๋ฐ๋ณต์ ๋ด๋ถ์ ๋ช
์์ ์ผ๋ก ์ํ๋ฅผ ์ ์งํ ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์
๋๋ค. ์์ฑ์(Generator) ํจ์๋ ์ด์ ๋ํ ๊ฐ๋ ฅํ ๋์์ ์ ๊ณตํฉ๋๋ค: ์คํ์ด ์ฐ์์ ์ด์ง ์์ ํ๋์ ํจ์๋ฅผ ์์ฑํจ์ผ๋ก์ ๊ฐ๋ฐ์๊ฐ iterative algorithm์ ์ ์ํ ์ ์๊ฒ ํด์ค๋๋ค. ์์ฑ์ ํจ์๋ function* ๋ฌธ๋ฒ์ ์ฌ์ฉํ์ฌ ์์ฑ๋ฉ๋๋ค. ์์ฑ์ ํจ์๊ฐ ์ต์ด๋ก ํธ์ถ๋ ๋, ํจ์ ๋ด๋ถ์ ์ด๋ ํ ์ฝ๋๋ ์คํ๋์ง ์๊ณ , ๋์ ์์ฑ์๋ผ๊ณ ๋ถ๋ฆฌ๋ ๋ฐ๋ณต์ ํ์
์ ๋ฐํํฉ๋๋ค. ์์ฑ์์ next ๋ฉ์๋๋ฅผ ํธ์ถํจ์ผ๋ก์ ์ด๋ค ๊ฐ์ด ์๋น๋๋ฉด, ์์ฑ์ ํจ์๋ yield ํค์๋๋ฅผ ๋ง๋ ๋๊น์ง ์คํ๋ฉ๋๋ค.
์์ฑ์ ํจ์๋ ์ํ๋ ๋งํผ ํธ์ถ๋ ์ ์๊ณ , ๋งค๋ฒ ์๋ก์ด ์์ฑ์๋ฅผ ๋ฐํํฉ๋๋ค๋ค. ํ์ง๋ง, ๊ฐ ์์ฑ์๋ ๋จ ํ ๋ฒ๋ง ์ํ๋ ์ ์์ ๊ฒ์ ๋๋ค.
์์ ์์ ์ฝ๋์ ์์ฑ์๋ฅผ ์ ์ฉํ ๊ฒ์ ๋๋ค. ๋ ์ฝ๋์ ํ์๋ ๋์ผํ์ง๋ง, ์์ฑ์๋ฅผ ์ฌ์ฉํ ์ชฝ์ด ์ฐ๊ฑฐ๋ ์ฝ๊ธฐ๊ฐ ํจ์ฌ ์ฝ์ต๋๋ค.
function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
let n = 0;
for (let i = start; i < end; i += step) {
n++;
yield i;
}
return n;
}
Iterables
๊ฐ์ฒด๋ ๊ฐ์ด for..of ๊ตฌ์กฐ ๋ด์์ ๋ฐ๋ณต๋๋ ๊ฒ ๊ฐ์ ๊ทธ ๋ฐ๋ณต ๋์์ ์ ์ํ๋ ๊ฒฝ์ฐ ๋ฐ๋ณต์ด ๊ฐ๋ฅ(iterable)ํฉ๋๋ค. Array ๋๋ Map๊ณผ ๊ฐ์ ์ผ๋ถ ๋ด์ฅ ํ์ ๊ธฐ๋ณธ ๋ฐ๋ณต ๋์์ด ์์ง๋ง ๋ค๋ฅธ ํ(๊ฐ๋ น Object)์ ์์ต๋๋ค.
๋ฐ๋ณต๊ฐ๋ฅํ๊ธฐ ์ํด์, ๊ฐ์ฒด๋ @@iterator ๋ฉ์๋๋ฅผ ๊ตฌํํด์ผ ํฉ๋๋ค. ์ฆ, ๊ฐ์ฒด( ํน์ ๊ทธ ํ๋กํ ํ์
์ฒด์ธ์ ๋ฑ์ฅํ๋ ๊ฐ์ฒด ์ค ํ๋)๊ฐ Symbol.iterator ํค๋ฅผ ๊ฐ๋ ์์ฑ์ด ์์ด์ผ ํจ์ ๋ปํฉ๋๋ค.
ํ๋์ iterable์ ๋จ ํ ๋ฒ, ํน์ ์ฌ๋ฌ๋ฒ ๋ฐ๋ณต๊ฐ๋ฅํฉ๋๋ค. ์ด๋ค ์๊ฐ์ ์ด๋ป๊ฒ ์ฌ์ฉํ ์ง๋ ํ๋ก๊ทธ๋๋จธ์๊ฒ ๋ฌ๋ ค์์ต๋๋ค. ๋จ ํ ๋ฒ ๋ฐ๋ณต๊ฐ๋ฅํ iterable(e.g. Generator)์ ๊ด์ต์ ์ผ๋ก ์์ ์ @@iterator ๋ฉ์๋๋ก๋ถํฐ this๋ฅผ ๋ฐํํฉ๋๋ค. ๋ฐ๋ฉด, ์ฌ๋ฌ ๋ฒ ๋ฐ๋ณต ๊ฐ๋ฅํ iterables์ @@iterator ๋ฉ์๋๊ฐ ํธ์ถ๋๋ ๋งค ํ ์๋ก์ด iterator๋ฅผ ๋ฐ๋์ ๋ฐํํด์ผํฉ๋๋ค.
์ฌ์ฉ์ ์ ์ iterable
์ด์ ๊ฐ์ด ์์ ์ ๋ฐ๋ณต๊ฐ๋ฅ ๊ฐ์ฒด๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค:
var myIterable = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
}
}
for (let value of myIterable) {
console.log(value);
}
// 1
// 2
// 3
or
[...myIterable]; // [1, 2, 3]
๋ด์ฅ iterable
String, Array, TypedArray, Map ๋ฐ Set์ ๋ชจ๋ ๋ด์ฅ ๋ฐ๋ณต๊ฐ๋ฅ ๊ฐ์ฒด์
๋๋ค, ๊ทธ๋ค์ ํ๋กํ ํ์
๊ฐ์ฒด๊ฐ ๋ชจ๋ Symbol.iterator ๋ฉ์๋๊ฐ ์๊ธฐ ๋๋ฌธ์
๋๋ค.
iterable์ ๊ธฐ๋ํ๋ ๊ตฌ๋ฌธ
์ผ๋ถ ๋ฌธ(statement) ๋ฐ ์(expression)์ iterableํฉ๋๋ค, ๊ฐ๋ น for-of ๋ฃจํ, spread syntax, yield* ๋ฐ ํด์ฒด ํ ๋น.
for (let value of ["a", "b", "c"]) {
console.log(value);
}
// "a"
// "b"
// "c"
[..."abc"]; // ["a", "b", "c"]
function* gen() {
yield* ["a", "b", "c"];
}
gen().next(); // { value:"a", done:false }
[a, b, c] = new Set(["a", "b", "c"]);
a; // "a"
Generator ์ฌํ
์์ฑ์ ํจ์๋ ์์ฒญ์ ๋ฐ๋ผ ๊ทธ ์ฐ์ถ๋(yielded, yield ์์ผ๋ก ์ฐ์ถ๋) ๊ฐ์ ๊ณ์ฐํ๊ณ , ๊ณ์ฐํ๊ธฐ ๋น์ผ(ํ๋ ) ์์ด ๋๋ ์์ ์ค๋ช ํ ๋๋ก ๋ฌดํ ์์ด์ด๋ผ๋ ํจ์จ์ ์ผ๋ก ๋ํ๋ด๊ฒ ํฉ๋๋ค.
next() ๋ฉ์๋๋ ๋ํ ์์ฑ๊ธฐ์ ๋ด๋ถ ์ํ๋ฅผ ์์ ํ๋ ๋ฐ ์ฐ์ผ ์ ์๋ ๊ฐ์ ๋ฐ์ต๋๋ค. next()์ ์ ๋ฌ๋๋ ๊ฐ์ ์์ฑ๊ธฐ๊ฐ ์ค๋จ๋ ๋ง์ง๋ง yield ์์ ๊ฒฐ๊ณผ๋ก ์ฒ๋ฆฌ๋ฉ๋๋ค.
์ฌ๊ธฐ sequence(์์ด)์ ์ฌ์์ํ๊ธฐ ์ํด next(x)๋ฅผ ์ฌ์ฉํ๋ ํผ๋ณด๋์น ์์ฑ๊ธฐ๊ฐ ์์ต๋๋ค:
function* fibonacci() {
var fn1 = 0;
var fn2 = 1;
while (true) {
var current = fn1;
fn1 = fn2;
fn2 = current + fn1;
var reset = yield current;
if (reset) {
fn1 = 0;
fn2 = 1;
}
}
}
var sequence = fibonacci();
console.log(sequence.next().value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
console.log(sequence.next().value); // 3
console.log(sequence.next().value); // 5
console.log(sequence.next().value); // 8
console.log(sequence.next(true).value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
์ ๋๋ ์ดํฐ์ throw() ๋ฉ์๋๋ฅผ ํธ์ถํ๊ณ throwํด์ผ ํ๋ ์์ธ ๊ฐ์ ์ ๋ฌํ์ฌ ์์ฑ์๊ฐ ์์ธ๋ฅผ throwํ๋๋ก ํ ์ ์์ต๋๋ค. ์ด ์์ธ๋ ์์ฑ๊ธฐ์ ํ์ฌ ์ผ์ ์ค๋จ๋ ์ปจํ
์คํธ์์ throw๋ฉ๋๋ค. ๋ง์น ํ์ฌ ์ผ์ ์ค๋จ๋ yield ๊ฐ ๋์ throwvalue ๋ฌธ์ธ ๊ฒ์ฒ๋ผ ๋ง์
๋๋ค.
์์ธ๊ฐ ์์ฑ๊ธฐ ๋ด์์ ํฌ์ฐฉ๋์ง ์์ผ๋ฉด throw() ํธ์ถ์ ํตํด ์ ํ๋๊ณ ์ดํ์ next() ํธ์ถ์ done ์์ฑ์ด true ๊ฐ ๋ฉ๋๋ค.
์ ๋๋ ์ดํฐ์๋ ์ฃผ์ด์ง ๊ฐ์ ๋ฐํํ๊ณ ์ ๋๋ ์ดํฐ ์์ฒด๋ฅผ ์๋ฃํ๋ return(value) ๋ฉ์๋๊ฐ ์์ต๋๋ค.