[Scala] evaluation strategies in scala
이번 글에서는 scala의 evaluation strategy에 대해서 정리한다.
해당 글은 Evaluation strategies in scala 글의 내용을 토대로 샘플 코드를 달리해서 작성했다.
먼저 evaluation strategy의 개념부터 살펴보고 종류별로 구분해 보자.
evaluation strategy
evaluation strategy(이하 평가전략)이란 함수에 전달한 인자값을 함수가 실행될 때, 언제 어떻게 결정하는지 평가함을 말한다.
Java에 익숙했던 필자로서는 당연히 함수에 값을 전달해 주는거 아닌가?라고 생각이 들었지만, 개념상 크게 2가지가 존재하며 큰 차이가 있다.
평가 전략의 2가지는 다음과 같다.
-
Strict evaluation
-
함수에 인자값을 전달하기 전에 인자값을 평가하고 값을 전달한다.
-
-
Non-strict evalutaion
-
인자값이 함수에서 실제로 사용될 때, 값을 평가한다.
-
call by value, call by name
scala에서는 각각의 평가전략을 call by value, call by name으로서 구현할 수 있게 한다.
아래 코드로 동일한 기능을 call by value, call by name으로 각각 구현해서 비교해 보자.
아래 함수를 인자값으로 전달할 때 결과값에 어떤 차이가 있는지 보자.
def something() = {
println("calling something")
1 // return value
}
아래는 함수의 이름처럼 call by value, call by name 순으로 구현되었다.
def callByValue(x: Int) = {
println("start")
println("x1=" + x)
println("x2=" + x)
}
def callByName(x: => Int) = {
println("start")
println("x1=" + x)
println("x2=" + x)
}
각 함수를 각각 실행해서 비교해보자.
scala> callByValue(something())
calling something
start
x1=1
x2=1
scala> callByName(something())
start
calling something
x1=1
calling something
x2=1
비교한 결과를 보면 side-effect로 볼 수 있는 "calling something"을 출력하는 println의 결과 순서와 개수 차이가 난다.
call by value는 처음 한번 시작하고 더 이상 출력하지 않으나 call by name은 2번 출력됐다. 차이는 무엇일까?
call by value의 경우, 함수가 호출되기 전에 평가를 진행하고 값을 저장하고 있기 때문에 함수를 호출하기 전에 평가결과로서 먼저 println을 한다.
그리고 함수 호출 할 때는 평가된 결과값만 표시하기 때문에 더 이상 "calling something"이 나타나지 않는다.
그러나 call by name의 경우는 인자값을 사용할 시점에 값을 평가하기 때문에 "calling something"이 2번 나온다.
callByName 함수에서 x 값을 2번 호출했는데, 그 시점 각각 println이 수행되었기 때문이다.
call by need
그럼 한걸음 더 들어가서 call by name의 인자값을 메모이제이션 하는 방법으로서 call by need를 살펴보자.
메모이제이션은 동일한 계산을 해야할 때, 계산 값을 메모리에 저장해서 반복적인 계산 과정을 줄이는 방법이다.
call by name에서 인자값을 사용할 때마다 평가를 한다고 했는데 그럼 불필요한 계산 과정이 늘어난다.
다음과 같이 call by name을 수정해보자.
def callByNeed(x: => Int) = {
println("start")
lazy val xx = x
println("x1=" + xx)
println("x2=" + xx)
}
코드를 보면 인자값인 x를 lazy 키워드를 붙인 변수로 저장했다.
scala에서의 lazy 키워드는 변수를 사용할 때, 값을 초기화 한다. 그리고 다시 호출할 때 해당값을 다시 활용한다.
그래서 결과값을 보면 처음 인자값을 호출했을 때는 "calling something"이 출력되지만 다음 호출에는 "calling something"이 출력되지 않는다.
scala> callByNeed(something())
start
calling something
x1=1
x2=1
참고 문서