[JS] iterator | 출발지 ~ 도착지 까지만을 반환하는 2호선 클래스를 작성하시오.

준리·2022년 9월 7일
0
post-thumbnail
//다음의 지하철 노선 중에서, 출발지 ~ 도착지 까지만을 반환하는 클래스를 작성하시오.
//(단, 단방향만 가능)
const routes = new Subway('문래', '신림');
const it1 = routes[Symbol.iterator]();
console.log("routes1 ==>", [...routes]); // [ '문래', '대림', '구로디지털단지', '신대방', '신림' ]
console.log(it1.next()); // { value: '문래', done: false }
console.log(it1.next()); // { value: '대림', done: false }
console.log(it1.next()); // { value: '구로디지털단지', done: false }
console.log(it1.next()); // { value: '신대방', done: false }
console.log(it1.next()); // { value: '신림', done: false }
console.log(it1.next()); // { value: undefined, done: true }

const routes2 = new Subway('구로디지털단지', '성수');
console.log("routes2 ==>", [...routes2]); // ['구로디지털단지', '신대방', ..., '성수']
const it2 = routes2[Symbol.iterator]();
while (true) {
  const x = it2.next();
  console.log(x);
  if (x.done) break;
}

이터러블 참고 : [JS] stack과 queue를 iterator로 작성하시오.

iterator : 반복하지 못하는 객체에 대해 반복가능한 프로토콜을 만들어주는 것이다.
데이터가 무한대에 가까울 떄 사용하기 위함으로 순환이 되야한다. (next())

one-way subway 🚟

#1 index를 사용해서 slice 해버리기

const LINE2 = [
... 2호선 역 리스트입니다.
  ]

class Subway {
    constructor(start, end) {
        this.start = start;
        this.end = end;
    }

    [Symbol.iterator]() {
        let startIdx = LINE2.indexOf(this.start);
        let endIdx = LINE2.indexOf(this.end) + 1;
        return LINE2.slice(startIdx, endIdx).values();
    }

  const routes = new Subway("문래", "신림");

----------------------------
[ '문래', '대림', '구로디지털단지', '신대방', '신림' ]
{ value: '문래', done: false }
{ value: '대림', done: false }
{ value: '구로디지털단지', done: false }
{ value: '신대방', done: false }
{ value: '신림', done: false }
{ value: undefined, done: true }
routes1 ==> [ '문래', '대림', '구로디지털단지', '신 
대방', '신림' ]
문래
대림
구로디지털단지
신대방
신림
-----------------------------
routes2 ==> []
{ value: undefined, done: true }

Subway Class를 만든다.
Constructor로 2개의 매개변수를 받는다.
새로 인스턴스를 만들면, 출발역과 도착역을 가진 인스턴스가 된다.

[Symbol.iterator]() 를 사용해서 iterator를 만들어준다. 왜냐? iterable 하게 만들어주기 위해서!

    [Symbol.iterator]() {
        let startIdx = LINE2.indexOf(this.start);
        let endIdx = LINE2.indexOf(this.end) + 1;
        return LINE2.slice(startIdx, endIdx).values();
    }

indexOf로 start와 end 값을 찾아서 LINE2 값을 slice해와서 사용했다.
생각보다 잘 적용되었고 이게 맞나 싶었는데, iterable은 '반복되는'이라는 의미와 맞지는 않았다.

const routes2 = new Subway("구로디지털단지", "성수");
console.log("routes2 ==>", [...routes2]); // ['구로디지털단지', '신대방', ..., '성수']

///
routes2 ==> [] // 왜안나와

구디에서 성수를 찍었는데 이건 결과 값이 나오지 않았다.

slice의 index 값을 사용하면 해당 값을 자를 수 없기 때문이었다.

#2

 class Subway {
    constructor(start, end) {
        // startIdx 등으로 변화해서 쓰는건 안좋음
        this.start = start;
        this.end = end;
    }

    [Symbol.iterator]() {

        let idx = LINE2.indexOf(this.start) - 1;
        let done = false;
        return {
            next: () => {
                // 화살표함수를 사용한 이유는 done에 this.end를 쓰기 위해서
                idx = idx === LINE2.length - 1 ? 0 : idx + 1;
                done = done || LINE2[idx - 1] === this.end;
                return { value: done ? undefined : LINE2[idx], done };
            },
        };
    }
}

iterator : 데이터가 무한대에 가까울 때 사용하기 위함임 (순환)
slice : slice를 쓰지 않는 건 새로운 array를 만드는 것을 방지한다. 새로운 배열을 생성하는 순수함수이므로!

        let idx = LINE2.indexOf(this.start) - 1;
        let done = false;

start의 idx 값은 index 값의 -1을 주어준다. done은 기본적으로 false를 준다.
return 값으로 객체를 내보내고, 그 안에 next 함수를 선언해서 보내준다. idx과 done을 사용하는 클로저 함수다.

        return {
            next: () => {
                // 화살표함수를 사용한 이유는 done에 상위스코프의 this인 this.end를 쓰기 위해서
                idx = idx === LINE2.length - 1 ? 0 : idx + 1;
                done = done || LINE2[idx - 1] === this.end;
                return { value: done ? undefined : LINE2[idx], done };
            },
        };

화살표 함수는 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정된다. 동적으로 결정되는 일반 함수와는 달리 화살표 함수의 this 언제나 상위 스코프의 this를 가리킨다. 이를 Lexical this라 한다.

idx 변수를 재할당하는데 idx 값과 LINE2.length -1 값이 같으면 0으로 idx를 초기화해준다. 이는 순환을 위한 장치로 활용된다. 아니라면 idx +1 해주는 방식으로 next() 를 구현한다.
done변수를 재할당하는데 done(false) 이거나 LINE2[IDX - 1] 값이 this.end (문자열이겠죠) 값과 같아지면 true로 할당이 된다.

그 값들을 return 하면

	return { value: done ? undefined : LINE2[idx], done };

///
{ value: '구로디지털단지', done: false }
...
{ value: '성수', done: false }
{ value: undefined, done: true }

객체를 내보내는데 valye는 done이 true일 땐 undefined를 주고 아닐 땐 LINE2[idx] 값을 출력해준다. 그리고 done은 done 값을 출력해준다. done이 true로 바뀌면 value는 undefined가 되는 것이다.

이는 idx값으로 순환되는 iterable 프로토콜이 되었다.

결론

이는 메모리 차원에서도 큰 이 점이 있을 것 같다. 사용자가 호출하지 않은 값을 가지고 있는 것만으로도 효율이 떨어지기 때문이다. 실무에서 실제로 iterator를 짜볼 일은 없다고 하지만, generator나 promise, async&await를 이해하기 위해선 필수적으로 iterator를 알아야 한다고 했다. 우리가 쉽게 쓰는 지하철 노선도도 이렇게 구현되어있으려나.

출처

SSAC 영등포 교육기관에서 풀스택 실무 프로젝트 과정을 수강하고 있다. JS전반을 깊이 있게 배우고 실무에 사용되는 프로젝트를 다룬다. 앞으로 그 과정 중의 내용을 블로그에 다루고자 한다.

profile
트렌디 풀스택 개발자

0개의 댓글