R에서의 <<- 사용법

오늘은 재미있는 코드를 들고왔습니다. 이번 포스팅은 요즘 제가 보고있는 Rcpp 책을 보다가 나온 코드임을 밝힙니다.

피보나치 수열

피보나치 수열은 수학에서 유명한 수열이죠. 이 수열은 다음과 같은 점화식을 써서 나타낼 수 있습니다.

\[ F_1 = 1, F_2 = 1, F_{n+2} = F_{n+1} + F_n \]

즉, 임의의 항을 만들때 그 전과 그 전전 항을 더해서 만들어 나가는 꼴입니다. 이 수열은 컴공 알고리즘 시간에도 많이 나오는데, 다음과 같은 재귀 함수를 써써 짜는 것을 배웁니다.

fibonacci <- function(seq) {
    if (seq == 1) return(1);
    if (seq == 2) return(1);
    return (fibonacci(seq - 1) + fibonacci(seq - 2));
}
fibonacci(12)
## [1] 144
sapply(1:12, fibonacci)
##  [1]   1   1   2   3   5   8  13  21  34  55  89 144

하지만 이러한 방식의 이전의 항들을 다 구해야하므로, 너무 비효율 적이죠. 이것을 우회적으로 돌아가는 방식의 코드를 보시죠.

<<- 연산자를 사용한 피보나치 수열

fibonacci2 <- local({
    memo <- c(1, 1, rep(NA, 100))
    f <- function(x) {
        if(x == 0) return(1)
        if(x < 0) return(NA)
        if(x > length(memo))
        stop("’x’ too big for implementation")
        if(!is.na(memo[x])) return(memo[x])
        ans <- f(x-2) + f(x-1)
        memo[x] <<- ans
        ans
    }
})
fibonacci2(12)
## [1] 144
sapply(1:12, fibonacci2)
##  [1]   1   1   2   3   5   8  13  21  34  55  89 144

위의 코드는 Stackoverflow에 올라온 코드입니다.

요점은 피보나치 수열 계산할때, 예전에 한 번 계산을 했던 결과값을 저장해놓습니다. 그리고, 그 함수의 값을 다시 불러올 경우 이미 계산을 한 숫자에 대해서는 계산을 하지 않게 만들어 함수가 훨씬 빨라지게 됩니다.

코드에서 사용된 <<- 연산자의 경우, 일반적인 변수에 값을 부여할 때 사용되는 <- 연산자와 달리, 현재 속해있는 변수환경 하나 위에 있는 환경에 있는 변수에 값을 부여할 때에 사용됩니다. 중요한 것은 만약 하나위의 환경에 그 변수가 없을 경우, 하나 더 위의 환경으로 올라가서 같은 이름의 변수를 찾기를 반복하다가 결국 최상위 환경에도 변수가 없을 경우, 최상위 환경(Global environment)에 변수를 생성하고 값을 부여합니다.

위에 R코드에서는 <<- 연산자가 속한 환경은 함수 f 안에 이라고 생각할 수 있죠. 그렇다면 <<- 연산자는 현재 속한 환경인 함수 f의 안 보다 하나 위인 함수 밖에서 memo라는 이름을 가진 변수를 찾아 값을 부여하려고 할 것 입니다. memo 변수를 우리가 함수 f를 정의 하기 전에 선언하였으므로 이미 선언된 memo 변수에 값을 저장하게 됩니다.

local() 함수에 대하여

피보나치 함수 효율화 과정처럼 특정 변수를 함수와 연결하여 사용하고 싶은 경우, local() 함수를 이용하여 함수를 정의할 때 그에 대응되는 변수를 따로 새로운 환경을 만들어서 같이 정의해 줄 수 수 있습니다. 이렇게 생성해주면 함수와 변수가 함께 묶여다니는 효과를 누릴 수 있게 됩니다.

즉, 함수가 정의될 때 변수가 같이 선언이 되고, 함수가 최상위 환경에서 제거되면 그에 상응하는 변수 역시도 없어지게 됩니다. 다음의 코드를 실행해 보시면서 이해하시면 쉬울 것이라 생각합니다.

x <- 3
x
## [1] 3
together <- local({
  x <- 1
  f <- function(y){
    print(x + y)
  }
})
together(4)
## [1] 5
x
## [1] 3
x <- 8

together(4)
## [1] 5
x
## [1] 8

  

댓글(0)

Designed by JB FACTORY