Javascript Iterable

Written by Paul
https://helloworldjavascript.net/pages/260-iteration.html๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ์“ด ๊ธ€์ž…๋‹ˆ๋‹ค.

Iterable

iterable obect๋Š” forโ€ฆof์™€ ํ•จ๊ป˜ ES2015์—์„œ ๋„์ž…๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
๋ฐ˜๋ณต ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด๋ฅผ ๋‹ค๋ฅธ ๊ฐ์ฒด์™€ ๊ตฌ๋ถ„์ง“๋Š” ํŠน์ง•์€, ๊ฐ์ฒด์˜ย Symbol.iteratorย ์†์„ฑ์—ย ํŠน๋ณ„ํ•œ ํ˜•ํƒœ์˜ ํ•จ์ˆ˜๊ฐ€ ๋“ค์–ด์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
const str = 'hello'; str[Symbol.iterator]; // [Function]
๊ฐ์ฒด์˜ย Symbol.iteratorย ์†์„ฑ์— ํŠน์ • ํ˜•ํƒœ์˜ ํ•จ์ˆ˜๊ฐ€ ๋“ค์–ด์žˆ๋‹ค๋ฉด, ์ด๋ฅผ ๋ฐ˜๋ณต ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด(iterable object) ํ˜น์€ ์ค„์—ฌ์„œย iterable์ด๋ผ ๋ถ€๋ฅด๊ณ ,ย ํ•ด๋‹น ๊ฐ์ฒด๋Š” iterable protocol์„ ๋งŒ์กฑํ•œ๋‹ค๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ฐ์ฒด๋“ค์— ๋Œ€ํ•ด์„œ๋Š” ES2015์—์„œ ์ถ”๊ฐ€๋œ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋‚ด์žฅ๋œ ์ƒ์„ฑ์ž ์ค‘ iterable ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๋Š” ์ƒ์„ฑ์ž์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒƒ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • String
  • Array
  • TypedArray
  • Map
  • Set

Iterable์˜ ์‚ฌ์šฉ

์–ด๋–ค ๊ฐ์ฒด๊ฐ€ Iterable์ด๋ผ๋ฉด, ๊ทธ ๊ฐ์ฒด์— ๋Œ€ํ•ด์„œ ์•„๋ž˜์˜ ๊ธฐ๋Šฅ๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • for...ofย ๋ฃจํ”„
  • spread ์—ฐ์‚ฐ์ž (...)
  • ๋ถ„ํ•ด๋Œ€์ž…(destructuring assignment)
  • ๊ธฐํƒ€ iterable์„ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š” ํ•จ์ˆ˜
์ฆ‰,ย ๋ฌธ์ž์—ด์— ๋Œ€ํ•ด์„œ๋„ ์œ„ ๊ธฐ๋Šฅ๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ย ์•„๋ž˜์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ง์ ‘ ํ™•์ธํ•ด๋ณด์„ธ์š”.
// `for...of` for (let c of 'hello') { console.log(c); } // spread ์—ฐ์‚ฐ์ž const characters = [...'hello']; // ๋ถ„ํ•ด๋Œ€์ž… const [c1, c2] = 'hello'; // `Array.from`์€ iterable ํ˜น์€ array-like ๊ฐ์ฒด๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค. Array.from('hello');
ย 

Generator ํ•จ์ˆ˜

Iterable์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์€ ES2015์— ๋„์ž…๋œย generator ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
Generator ํ•จ์ˆ˜๋Š”ย iterable ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํŠน๋ณ„ํ•œ ํ˜•ํƒœ์˜ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
// ํ•จ์ˆ˜ ์„ ์–ธ function* gen1() {} // ํ•จ์ˆ˜ ํ‘œํ˜„์‹ const gen2 = function* () {} // ๋ฉ”์„œ๋“œ ๋ฌธ๋ฒ• const obj = { * gen3() {} }
Generator ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜๋Š”๋ฐ, ์ด ๊ฐ์ฒด๋Š” iterable protocol์„ ๋งŒ์กฑํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰,ย Symbol.iteratorย ์†์„ฑ์„ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
function* gen1() {} const iterable = gen1() iterable[Symbol.iterator] // [Function]
Generator ํ•จ์ˆ˜ ์•ˆ์—์„œ๋Š”ย yield๋ผ๋Š” ํŠน๋ณ„ํ•œ ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Generator ํ•จ์ˆ˜ ์•ˆ์—์„œย yieldย ํ‚ค์›Œ๋“œ๋Š”ย return๊ณผ ์œ ์‚ฌํ•œ ์—ญํ• ์„ ํ•˜๋ฉฐ, iterable์˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ๋•Œย yieldย ํ‚ค์›Œ๋“œ ๋’ค์— ์žˆ๋Š” ๊ฐ’๋“ค์„ ์ˆœ์„œ๋Œ€๋กœ ๋„˜๊ฒจ์ค๋‹ˆ๋‹ค.
function* numberGen() { yield 1; yield 2; yield 3; } // 1, 2, 3์ด ์ˆœ์„œ๋Œ€๋กœ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. for (let n of numberGen()) { console.log(n); }
yield*ย ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•˜๋ฉด, ๋‹ค๋ฅธ generator ํ•จ์ˆ˜์—์„œ ๋„˜๊ฒจ์ค€ ๊ฐ’์„ ๋Œ€์‹  ๋„˜๊ฒจ์ค„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
function* numberGen() { yield 1; yield 2; yield 3; } function* numberGen2() { yield* numberGen(); yield* numberGen(); } // 1, 2, 3, 1, 2, 3์ด ์ˆœ์„œ๋Œ€๋กœ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. for (let n of numberGen2()) { console.log(n); }
Generator ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ฃผ์˜ํ•  ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • Generator ํ•จ์ˆ˜๋กœ๋ถ€ํ„ฐ ์ƒ์„ฑ๋œ iterable์€ ํ•œ ๋ฒˆ๋งŒ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Generator ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์ •์˜๋œ ์ผ๋ฐ˜ ํ•จ์ˆ˜์—์„œ๋Š”ย yieldย ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
// Generator ํ•จ์ˆ˜๋กœ๋ถ€ํ„ฐ ์ƒ์„ฑ๋œ iterable์€ ํ•œ ๋ฒˆ๋งŒ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. function* gen() { yield 1; yield 2; yield 3; } const iter = gen(); for (let n of iter) { // ์ž˜ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. console.log(n); } for (let n of iter) { // `iter`๋Š” ํ•œ ๋ฒˆ ์‚ฌ์šฉ๋˜์—ˆ์œผ๋ฏ€๋กœ, ์ด ์ฝ”๋“œ๋Š” ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. console.log(n); }
// Generator ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์ •์˜๋œ ์ผ๋ฐ˜ ํ•จ์ˆ˜์—์„œ๋Š” `yield` ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. function* gen2() { // ์•„์˜ˆ ๋ฌธ๋ฒ• ์˜ค๋ฅ˜๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค. (Unexpected token) function fakeGen() { yield 1; yield 2; yield 3; } fakeGen(); }
ย 

Iterator Protocol

Iterable๊ณผ Iterator๋ฅผ ์ž˜ ๊ตฌ๋ถ„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
iterable ๊ฐ์ฒด๋Š” iterable protocol์„ ๋งŒ์ขํ•œ๋‹ค. ์ฆ‰, Symbol.Iterator ์†์„ฑ์— ํŠน๋ณ„ํ•œ ํ˜•ํƒœ์˜ ํ•จ์ˆ˜๊ฐ€ ์ €์žฅ๋˜์–ด ์žˆ๋‹ค.
Iterable Protocol์„ ๋งŒ์กฑํ•˜๋ ค๋ฉด, Symbol.iterator ์†์„ฑ์— ์ €์žฅ๋˜์–ด ์žˆ๋Š” ํ•จ์ˆ˜๋Š” iterator ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
iterator ๊ฐ์ฒด๋Š” ์•„๋ž˜์˜ ํŠน๋ณ„ํ•œ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.
  • iterator๋Š” next๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ–์Šต๋‹ˆ๋‹ค.
  • next ๋ฉ”์„œ๋“œ๋Š” ๋‹ค์Œ ๋‘ ์†์„ฑ์„ ๊ฐ–๋Š” ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
done - ๋ฐ˜๋ณต์ด ๋ชจ๋‘ ๋๋‚ฌ๋Š”์ง€๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
value - ํ˜„์žฌ ์ˆœ์„œ์˜ ๊ฐ’์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
์œ„ ์กฐ๊ฑด์„ iterator protocol ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.
const strIterator = 'go'[Symbol.iterator](); strIterator.next(); // { value: 'g', done: false } strIterator.next(); // { value: 'o', done: false } strIterator.next(); // { value: undefined, done: true } strIterator.next(); // { value: undefined, done: true } function* gen() { yield 1; yield 2; } const genIterator = gen()[Symbol.iterator](); genIterator.next(); // { value: 1, done: false } genIterator.next(); // { value: 2, done: false } genIterator.next(); // { value: undefined, done: true } genIterator.next(); // { value: undefined, done: true }
iterable์„ ๋งŒ๋“ค์–ด ๋ณด๊ธฐ.
function range(start = 0, end = Infinity, step = 1) { return { currentValue: start, [Symbol.iterator]() { return { next: () => { if (this.currentValue < end) { const value = this.currentValue; this.currentValue += step; return { done: false, value } } else { return { done: true } } } } } } }
ย 

Generator์™€ Iterator

Generator ํ•จ์ˆ˜๋กœ๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด์ง„ ๊ฐ์ฒด๋Š” ์ผ๋ฐ˜์ ์ธ iterable์ฒ˜๋Ÿผ ์“ธ ์ˆ˜ ์žˆ์ง€๋งŒ, iterator์™€ ๊ด€๋ จ๋œ ํŠน๋ณ„ํ•œ ์„ฑ์งˆ์„ ๊ฐ–๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
generator ํ•จ์ˆ˜๋กœ๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด์ง„ ๊ฐ์ฒด๋Š” iterable protocol๊ณผ iterator protocol์„ ๋™์‹œ์— ๋งŒ์กฑํ•ฉ๋‹ˆ๋‹ค.
function* gen() { // ... } const genObj = gen(); genObj[Symbol.iterator]().next === genObj.next; // true
Symbol.iterator๋ฅผ ํ†ตํ•ด iterator๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ ๋„ ๋ฐ”๋กœ next๋ฅผ ํ˜ธ์ถœ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
function* gen() { yield 1; yield 2; yield 3; } const iter = gen(); iter.next(); // { value: 1, done: false } iter.next(); // { value: 2, done: false } iter.next(); // { value: 3, done: false } iter.next(); // { value: undefined, done: true }
ย 
generator ํ•จ์ˆ˜ ์•ˆ์—์„œ return ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ˜๋ณต์ด ๋ฐ”๋กœ ๋๋‚˜๋ฉด์„œ next ๋ฉ”์„œ๋“œ์—์„œ ๋ฐ˜ํ™˜๋˜๋Š” ๊ฐ์ฒด์˜ ์†์„ฑ์— ์•ž์˜ ๋ฐ˜ํ™˜๊ฐ’์ด ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ, return์„ ํ†ตํ•ด ๋ฐ˜ํ™˜๋œ ๊ฐ’์ด ๋ฐ˜๋ณต ์ ˆ์ฐจ์— ํฌํ•จ๋˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.
function* gen() { yield 1; return 2; // generator ํ•จ์ˆ˜๋Š” ์—ฌ๊ธฐ์„œ ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค. yield 3; } const iter = gen(); iter.next(); // { value: 1, done: false } iter.next(); // { value: 2, done: true } iter.next(); // { value: undefined, done: true } // `1`๋งŒ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. for (let v of gen()) { console.log(v); }
ย 
generator ํ•จ์ˆ˜๋กœ๋ถ€ํ„ฐ ์ƒ์„ฑ๋œ ๊ฐ์ฒด์˜ next ๋ฉ”์„œ๋“œ์— ์ธ์ˆ˜๋ฅผ ์ฃผ์–ด์„œ ํ˜ธ์ถœํ•˜๋ฉด, generator ํ•จ์ˆ˜๊ฐ€ ๋ฉˆ์ท„๋˜ ๋ถ€๋ถ„์˜ yield ํ‘œํ˜„์‹์˜ ๊ฒฐ๊ณผ๊ฐ’์€ ์•ž์—์„œ ๋ฐ›์€ ์ธ์ˆ˜๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
function* gen() { const received = yield 1; console.log(received); } const iter = gen(); iter.next(); // { value: 1, done: false } // 'hello'๊ฐ€ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค. iter.next('hello'); // { value: undefined, done: true }
ย 

Generator Examples

// ๊ฐ ํ•ญ๋ชฉ์„ ๋ณ€ํ™˜ํ•œ ํ›„ ๋„˜๊ฒจ์ฃผ๊ธฐ function* map(iterable, mapper) { for (let item of iterable) { yield mapper(item); } } // ๊ฐ ์ˆœ์„œ๊นŒ์ง€์˜ ๋ˆ„์ ๊ฐ’์„ ๋„˜๊ฒจ์ฃผ๊ธฐ function* reduce(iterable, reducer, initial) { let acc = initial; for (let item of iterable) { acc = reducer(acc, item); yield acc; } } // ์กฐ๊ฑด์— ๋งŒ์กฑํ•˜๋Š” ํ•ญ๋ชฉ๋งŒ ๋„˜๊ฒจ์ฃผ๊ธฐ function* filter(iterable, predicate) { for (let item of iterable) { if (predicate(item)) { yield item; } } } // ์—ฌ๋Ÿฌ iterable์„ ์—ฐ๊ฒฐํ•˜๊ธฐ function* concat(iterables) { for (let iterable of iterables) { yield* iterable; } } // ์•ž์ชฝ ๋ช‡ ๊ฐœ์˜ ํ•ญ๋ชฉ๋งŒ ๋„˜๊ฒจ์ฃผ๊ธฐ function* take(iterable, count = Infinity) { const iterator = iterable[Symbol.iterator](); for (let i = 0; i < count; i++) { // `yield*`์™€๋Š” ๋‹ค๋ฅด๊ฒŒ, iterator์˜ `next` ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•˜๋ฉด iterable์˜ ์ผ๋ถ€๋งŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. const {value, done} = iterator.next(); if (done) break; yield value; } }
โ† Go home