Table of contents
Open Table of contents
1.1 객체지향 디자인 패턴의 반복자 패던과 일급 함수
- 요즘의 멀티 패러다임 언어에서는 함수형 패러다임을 적용하는 방법으로 반복자(iterator) 패턴을 활용함
- 반복자 패턴은 GoF의 객체지향 디자인 패턴 중 하나였음
1.1.1 GoF의 반복자 패턴
- 반복자 패턴은 객체지향 디자인 패턴중에 하나로, 컬렉션의 요소를 순차적으로 접근하는 규약을 제시
- 반복자 패턴은 컬렉션의 내부 구조를 노출하는 대신
next
같은public
메서드로 내부 요소에 접근할 수 있도록 설계됨
1.1.2 지연 평가되는 map 함수
- map은
Iterator<A>
와 A를 B로 변환하는transform
함수를 받아 지연된Iterator<B>
를 반환하는 함수 - 일급 함수: 함수를 값처럼 다루어 변수에 담거나 다른 함수의 인자로 전달하거나 함수의 반환값으로 사용할 수 있는 함수를 일급 함수라고 함
- 고차 함수: 하나 이상의 함수를 인자로 받거나 반환하는 함수를 말함
- 반복자 패턴의 지연성은 지연 평가가 가능한 객체를 생성할 수 있게 해주고 일급 함수는 고차함수를 정의할 수 있게함
1.2 명령형 프로그래밍으로 이터레이터를 만드는 제너레이터 함수
- 제너레이터는 반복자 패턴인 이터레이터를 명령형 코드로 구현하고 생성할 수 있는 도구
- 어떤 문제는 명령형 스타일로 해결하는 것이 더 효율적이면서 직관적임
1.2.1 제너레이터 기본 문법
- 제너레이터는 명령형 스타일로 이터레이터를 작성할 수 있게 해주는 문법
- 곧바로 실행되지 않고 이터레이터 객체를 반환함
- 이 객체를 통해 함수의 실행 흐름을 외부에서 제어할 수 있음
yield* 키워드는 제너레이터 함수 안에서 이터러블을 순회하며 해당 이터러블이 제공하는 요소들을 순차적으로 반환하도록 해줌
1.3 자바스크립트에서 반복자 패턴 사례: 이터레이션 프로토콜
- 이터레이션 프로토콜은 자바스크립트의 규약임
- 어떤 객체가 이터러블인지 여부를 나타내는 규칙과 해당 규칙을 따르는 문법들을 제공하는 언어 전반의 규약
1.3.1 이터레이터와 이터러블
-
어떤 객체가 이터레이터를 반환하는
[Symbol.iterator]() { return { next() {...} } }
메서드를 가지고 있다면 이터러블임 -
이터러블 객체는
for..of
문, 전개 연산자, 구조 분해 등 다양한 기능과 함께 사용할 수 있음 -
대표적으로
Array
,Map
,Set
등이 있고Web API
도 컬렉션 유형의 값들을 이터러블로 만들어 이터레이션 프로토콜을 따르고 있음 -
Iterator
: { value, done } 객체를 반환하는next()
메서드를 가진 값 -
Iterable
: 이터레이터를 반환하는[Symbol.Iterator()]
메서드를 가진 값 -
IterableIterator
: 이터레이터면서 이터러블인 값 -
이터레이션 프로토콜: 이터러블을 for..of문, 전개 연산자 등과 함께 동작하도록 한 규약
1.4 이터러블을 다루는 함수형 프로그래밍
1.4.1 forEach
function forEach<T>(f: (a: T) => void, iterable: Iterable<T>) {
for (const a of iterable) {
f(a);
}
}
forEach(console.log, naturals(3));
map
function map<T, U>(f: (a: T) => U, iterable: Iterable<T>): IterableIterator<U> {
function* iterator() {
for (const a of iterable) {
yield f(a);
}
}
return iterator();
}
forEach(
console.log,
map(x => x * x, naturals(3))
);
filter
function filter<T>(
predicate: (a: T) => boolean,
iterable: Iterable<T>
): IterableIterator<T> {
function* iterator() {
for (const a of iterable) {
if (predicate(a)) yield a;
}
}
return iterator();
}
forEach(
console.log,
filter(x => x % 2 === 0, naturals(10))
);
1.5 이터러블 프로토콜이 상속이 아닌 인터페이스로 설계된 이유
- 반복자 패턴과 이터레이터를 지원하는 헬퍼 함수들은 상속이 아닌 인터페이스로 설계되어 있음
Array
의map
,filter
를 사용하지 않고 이터러블을 사용하는 이유는 이터레이션 프로토콜은 ‘외부 구조의 다형성’을 해결하기 때문임- 인터페이스는 규약을 제시하여 다양한 클래스가 동일한 형식의 동작을 구현하도록 유도함
- 반면 상속은 공통 기능을 직접 구현한 뒤 이를 적절히 확장하는 데 초점을 둠
- 인터페이스는 언어나 표준 라이브러리 설계 단계에서 자주 사용되고 상속은 주로 SDK나 애플리케이션 레벨에서 사용됨