[Dart] 지연 평가 (Lazy Evaluation)

Jinwook Kim·2024년 8월 12일

Dart

목록 보기
5/7
post-thumbnail

지연 평가(Lazy Evaluation)란 어떤 연산이나 계산을 즉시 하지 않고, 뒤로 지연시키는 것이다. 이를 통해 효율적인 메모리 사용과 최적화된 성능의 실현이 가능하다.

다트에서는 흔히 이터러블(Iterable) 데이터를 처리할 때에 이 지연 평가 현상이 일어난다. 대표적인 메서드인 where(), map(), expand()에서 이러한 현상을 관찰할 수 있다.

대표적으로, map()은 변형된 전체 리스트를 바로 반환하지 않는다. 그 대신, 변환된 요소를 필요할 때마다 하나씩 계산하여 반환한다.

const List<int> numbers = [1, 2, 3, 4, 5];
  
// map을 사용해 각 요소를 제곱하는 Iterable 생성 (하지만 아직 계산되지 않음)
Iterable<int> squares = numbers.map((number) {
    print("Computing square of $number");
    return number * number;
});
  
print("Squares created, but not computed.");

// Iterable을 순회하면서 실제로 값을 계산함
print(squares.toList());

위의 예에서 실제로 계산되는 시점은 toList()로, 리스트로 바뀔 때이다. 즉, map()이 호출될 때에는 결괏값이 아니라, 결괏값에 대한 일종의 설계도(Blueprint)가 반환되는 것이다.

Squares created, but not computed.
Computing square of 1
Computing square of 2
Computing square of 3
Computing square of 4
Computing square of 5
[1, 4, 9, 16, 25]

map()이나 where()에서 리스트를 바로 반환하지 않고, 이터러블을 반환하는 것이 이러한 이유의 연장선 상에 있다고 할 수 있다. 즉, 이터러블을 통해 지연 평가를 적용한 뒤, 실제 계산은 리스트로 캐스팅될 때로 지연시키는 것이다. 이를 통해 성능을 최적화할 수 있는 것이다.

지연 평가의 개념은 무한한 자연수 시퀸스를 다룰 때 진가를 발휘한다. 아래의 코드는 0부터 무한대의 자연수를 생성하는 이터러블에 대한 코드이다.

Iterable<int> naturalsToInfinity() sync* {
    int number = 0;
    while (true) {
        yield number++;
    }
}

Iterable<int> naturals = naturalsToInfinity();
print(naturals.take(5).toList()); // [0, 1, 2, 3, 4]

아무리 성능이 좋은 컴퓨터라도 무한대의 자연수를 즉시 계산할 수는 없을 것이다. 다트에서는 지연 평가의 원리를 이용하여 손쉽게 0부터 4까지의 자연수만을 뽑아낼 수 있다. 무한이지만, 유한처럼 연산을 하는 것이다.

지연 평가 방식의 장점으로 첫번째, 전체 컬렉션을 미리 생성하지 않기 때문에, 메모리 사용량이 최적화된다. 이는 특히, 대용량 데이터나, 앞서 설명한 무한 컬렉션을 다룰 때 더욱 유용하다.

두번째로, 모든 요소를 한꺼번에 계산하지 않고, 필요한 시점에만 계산하기 때문에, 불필요한 연산을 피할 수 있다. 특히, 일부 요소만 활용하는 경우에는 성능을 크게 향상시키는 결과를 가져온다.

물론, 장점만 있는 것은 아니다. 단점으로는, 디버깅이 어렵다는 점이 있다. 예상치 못한 시점에 실제 연산이 실행되다 보니 코드의 실행 순서를 예측하기 어렵다.

지연 평가 개념은 다른 언어에도 있다. 파이썬에서는 제너레이터(Generator)를 통해, 자바에서는 스트림(Stream)이 지연 평가의 원리로 동작한다. C#에서도, LINQ(Language Integrated Query)가 지연 평가 개념을 지원한다. 자바스크립트에서 또한 제너레이터(Generator) 수준에서 지연 평가를 활용한다. 내가 개인적으로 애정하는 언어인 Swift에서도 lazy 키워드를 통해 지연 평가를 활용할 수 있다.

지연 평가는, 이렇듯 다양한 프로그래밍 언어에서 사용되며, 메모리 절약과 성능 최적화를 위해 값을 필요할 때만 계산하는 중요한 개념이다. 이를 통해 코드의 효율성과 유연성을 크게 향상시킬 수 있다.

profile
200 Everything Okay.

0개의 댓글