Lazy Evaluation을 알아보자
Lazy Evaluation이 무엇이고 장점이 뭔지 살펴 보겠습니다.
* 해석 Tip: Lazy(게으른, 느긋한, 지연된) Evaluation(평가, 연산, 계산)
compiler
는 일반적으로 expression
(값을 도출하는 코드, ex: 1+3)을 만나면 그 값을 계산(평가)합니다.
Evaluation
는 compiler
가 코드를 보고 최종 결과를 평가하는 과정으로, 예를 들면 code에서 5+3 을 만나면 8로 평가합니다.
대부분의 언어에서 expression
을 만나면 즉시 평가 하지만, 항상 효과적인건 아닙니다. 예를 들어 함수가 전달된 파라미터를 사용하지 않는 경우, compiler가 이를 평가할 필요는 없습니다.
def func(x, y):
return x + 1func(3, 3 + 2)
위 예제에서 3+2는 함수 내부에서 쓰이지 않으므로 평가할 필요가 없습니다.
이 때, 즉시 평가하지 않고, 필요한 것만 평가
하는 방법을 lazy evaluation,
즉시 평가
하는 방법을 strict evaluation
이라고 부릅니다.
lazy evaluation의 예시 하나를 보겠습니다.
JavaScript
는 왼쪽의 a()
호출 결과를 평가하고 값이 false
일 경우 필요없는 b
는 평가하지 않습니다. 위 처럼 필요없는 평가를 하지 않음으로서 성능의 이득을 취할 수 있습니다.
성능 관점
에서 lazy compiler를 좀 더 효율적으로 쓸 수 있는 방법으로 memoization
이 있습니다. 한 번 evaluated
된 값을 저장하여 재 평가하지 않는 방법으로 dictionary key에는 변수 이름을, value는 평가 결과를 저장하고, 이미 계산된 변수는 재 평가 없이 dictionary에서 바로 사용하여 성능을 향상합니다.
lazy evaluation
의 또다른 장점으로 무한 자료 구조
를 사용할 수 있습니다. 아래에 재귀적으로 무한으로 list를 생성하는 addOne 함수가 있습니다.
def addOne(n):
[n] + addOne(n + 1)
list = addOne(1) // [1, 2, 3, 4, 5, 6, …]
일반적인 compiler
는 해당 함수를 호출하면 메모리 부족으로 문제가 발생하지만 lazy evaluation
의 경우 재귀의 모든 depth를 계산하는게 아니라, 현재 실행되는 depth만 평가하기 때문에 문제가 발생하지 않습니다.
무한 자료구조를 선언할 수 있어도 쓸수가 없는데 왜 이게 이점인지 궁금하지 않나요? 비록 무한 자료구조 전체를 쓰지 않아도 일부는 사용할 수 있습니다.
oneToThree = list.takeFirst (3)
print (oneToThree) // [1, 2, 3]print (range (5, 10)) // [5, 6, 7, 8, 9]
위 처럼 인자로 몇 번째 데이터
or 범위 데이터
를 전달하여 무한의 목록 중 일부를 사용할 수 있습니다. 이를 이용하여 ranges, sequences, cycles, 연속 사전 키 등의 유용한 개념을 정의할 수 있습니다.