[Functional Programming] 4장: 고급 이터러블 함수 구현

2025. 4. 24. 08:00프로그래밍 공부/javascript & typescript

반응형
SMALL

property get length

class Fx<T> implements Iterable<T> {
  /**
   * @description evaluates the iterable and returns the length of the array (O(n))
   * @returns {number} the length of the array
   */
  get length(): number {
    return this.toArray().length;
  }
}

Fx 클레스에 length 프로퍼티를 추가합니다. length 프로퍼티에 접근하게 되면 평가가 진행되어 O(n)의 시간 복잡도를 가지게 됩니다.

왜 O(n)의 시간복잡도를 유지하는가?

github copilot의 코드리뷰입니다.

src/Fx.ts:62

Using toArray() to calculate the length may lead to performance issues for large iterables. Consider an alternative approach if the underlying size can be determined more efficiently.

get length(): number { return this.toArray().length; }


toArray()함수를 호출하기 때문에 성능 이슈를 가져올 수 있다고 우려하고 있습니다. 그러나 Fx의 핵심 철학인 지연 평가를 달성하기 위하여 length는 평가의 결과이기 때문에 명확히 비용이 발생하는 함수로 유지합니다. 즉, length함수 호출 시 지연 평가가 종료되고 평가가 시작됩니다.

public method

Fx 클래스에 다음과 같은 고급 이터러블 연산자들을 추가했습니다

함수명 설명
flatMap 요소를 이터러블로 매핑 후 평탄화
zip 여러 이터러블을 병렬로 순회하며 배열 생성
reduce 누적값 계산 (fold)
scan 누적 흐름 자체를 이터러블로 생성
chunk 일정 크기 단위로 데이터를 그룹화

1. flatMap

flatMap<U>(fn: (item: T) => Iterable<U>): Fx<U>
  • 각 요소를 fn(item)으로 변환 → Iterable<U>
  • 내부 이터러블을 순회하며 모든 값을 펼쳐서 yield

2. zip

zip<U>(...others: Iterable<U>[]): Fx<(T | U)[]>
  • 내부적으로 모든 이터러블의 iterator를 병렬로 생성
  • some(r.done) 조건을 만나면 zip 종료

3. reduce

reduce<U>(fn: (acc: U, cur: T) => U, initial: U): U
  • 이터러블을 순회하며 누적 값을 계산하여 단일 값을 리턴
  • Array.prototype.reduce와 동일한 방식
  • 지연 평가 종료 메서드

4. scan

scan<U>(fn: (acc: U, cur: T) => U, initial: U): Fx<U>
  • reduce와 달리 누적값의 흐름을 모두 이터러블로 출력
  • 이터러블의 스냅샷을 얻을 수 있음
// Usage
Fx.of([1, 2, 3])
  .scan((acc, x) => acc + x, 0)
  .toArray(); // [0, 1, 3, 6]

5. chunk

chunk(size: number): Fx<T[]>
  • size 크기만큼 요소를 묶어 T[]로 반환
  • 마지막 그룹은 크기가 부족할 수도 있음
// Usage
Fx.of([1, 2, 3, 4, 5])
  .chunk(2)
  .toArray(); // [[1, 2], [3, 4], [5]]

해당 작업 PR: https://github.com/99mini/fx/pull/2

반응형
LIST