well-formed 이터러블의 장점 (feat. 피보나치수열)
— Functional programming — 1 min read
프로그래머스에서 진행한 유인동님의 ES6로 알아보는 동시성 & 함수형 프로그래밍 강의를 들으며 질문/답변에 대한 추가학습
Q: well-formed iterable이 가지는 장점이 있을까요 ?
제너레이터로 생성한 이터러블만 자기자신을 반환하는 well-formed이고, 내장 iterator를 가지고 있는 "Array, String, NodeList..." 같은 애들은 well-formed가 아닌것 같은데...
well-formed iterable이 가지는 장점이 있을까요 ?
1var iterG = valuesIter({ a: 1, b: 2 });2var arr = [ 1, 2 ];3var str = "abc";4var node = document.querySelectorAll("div");5var map = new Map([["a", 1],["b", 2]]);6var set = new Set([1, 2, 3]);7
8iterG[Symbol.iterator]() == iterG // true9arr[Symbol.iterator]() == arr // false10str[Symbol.iterator]() == str // false11node[Symbol.iterator]() == node // false12map[Symbol.iterator]() == map // false13set[Symbol.iterator]() == set // false
A: 선생님 답변
- 일단, well-formed의 장점은 여기저기서 조합하기 좋다
- Array, String은 well-formed가 맞음. 위의 테스트가 잘못되었음
1// 올바른 Test Case2var arr = [1,2,3];3arr[Symbol.iterator]() == arr // false4
5var arrIter = [1,2,3][Symbol.iterator]();6arrIter[Symbol.iterator]() == arrIter; // true
-> Array의 [Symbol.iterator] 메서드를 한번 실행시켜줘야 이터레이터가 된다 !
-> 이터레이터가 된 후와 비교해야 올바른 테스트!
collIter()
함수의 코드가 이 원리와 같다
- coll이 [Symbol.iterator]를 가지고 있다면
- 이를 실행시켜줘야 -> 이터레이터가 된다
- 제너레이터는 호출하는 순간 실행 -> 이터레이터가 된다
1const collIter = coll =>2 hasIter(coll) ? // --> 13 coll[Symbol.iterator]() : // --> 24 valuesIter(coll); // --> 3
Well-formed Iterable
이 유용한 이유
Iterators that are Iterable
Why is it useful if an iterator is also an iterable? for-of only works for iterables, not for iterators. Because Array iterators are iterable, you can continue an iteration in another loop
- Iterator이면서 Iterable인 객체를 잘정의된(well-formed) Iterable이라고 부른다
- 제너레이터가 만들어주는 제너레이터객체는
well-formed Iterable
- This is Why ES6 generators are usually much more convenient !
예시) 피보나치 수열을 생성하는 이터러블
- non well-formed
이터러블이 진행된 지점을 기억하지 못해 처음부터 Restart - well-formed 이터러블이 진행된 지점을 기억하고 그 지점부터 Restart
1//1. non well-formed2
3var fibonacci = {4 [Symbol.iterator]() {5 let [prev, curr] = [0, 1];6 let step = 0;7 const maxStep = 5;8 return {9 next() {10 [prev, curr] = [curr, prev + curr];11 return { value: curr, done: step++ >= maxStep };12 }13 };14 }15};16
17// Test -----18
19for(const v of fibonacci) {20 console.log(v);21 break;22} // 123
24for(const v of fibonacci) {25 console.log(v);26} // 1 2 3 5 8 -> Restart iterator27
28
29
30//2. well-formed 31
32var fibonacciW = {33 prev: 0, curr: 1, step: 0, maxStep: 5,34 next() {35 [this.prev, this.curr] = [this.curr, this.prev + this.curr];36 return { value: this.curr, done: this.step++ >= this.maxStep };37 },38 [Symbol.iterator]() {39 console.log(this)40 return this;41 }42};43
44// Test -----45
46for(const v of fibonacciW) {47 console.log(v);48 break;49} // 150
51for(const v of fibonacciW) {52 console.log(v);53} // 2 3 5 8 -> Continue with same iterator
# 결론
- Array, String, Map, Set, NodeList는
[Symbol.iterator]
를 내장메서드로 가지고 있기때문에 기본적으로 Iterable하다 - 하지만 well-formed Iterable로 동작하기 위해선
[Symbol.iterator]()
를 한 번 실행시켜줘야한다
1var arr = [1,2,3]; //--> Iterable2var arrIt = arr[Symbol.iterator](); // well-formed Iterable3
4//1. Iterable5for(const v of arr) {6 console.log(v);7 break;8} // 19
10for(const v of arr) {11 console.log(v);12} // 1 2 3 --> 다시 시작13
14//2. well-formed Iterable15
16for(const v of arrIt) {17 console.log(v);18 break;19} // 1 --> 순회한 위치를 기억20
21undefined22for(const v of arrIt) {23 console.log(v);24} // 2 3 -> Continue with Same iterator