16.4 R 환경과 범위

이 절에서는 R 프로그래밍의 환경 (전역 환경, 단계적 환경 등)에 대해 알아 보겠습니다. 또한 변수의 적용 범위에 대해 예제를 통해 살펴 보겠습니다.

함수를 적절하게 작성하고 비정상적인 오류를 방지하려면 R에서의 환경(environment)범위(range)의 개념을 알아야합니다.

16.4.1 R 프로그래밍 환경

환경(environment)은 객체(함수, 변수 등)의 모음으로 생각할 수 있습니다. R 인터프리터를 처음 시작할 때 하나의 환경이 생성됩니다. 우리가 정의하는 모든 변수는 이제 이 환경에 존재하게 됩니다.

R 명령 프롬프트에서 사용할 수 있는 최상위 환경은R_GlobalEnv라는 전역 환경입니다. 전역 환경은 R 코드에서도.GlobalEnv로 참조될 수 있습니다.

ls() 함수를 사용하여 현재 환경에서 정의된 변수와 함수를 표시할 수 있습니다. 또한environment() 함수를 사용하여 현재 환경을 가져올 수 있습니다.

a <- 2                                      # 개체 a에 2를 할당합니다.
b <- 5                                      # 개체 b에 5를 할당합니다.
f <- function(x) x <- 0                     # 함수 f를 정의합니다.
ls()                                        # 현재의 환경을 확인합니다.
##  [1] "a"            "add"          "b"            "check"        "choice"      
##  [6] "coin"         "counter"      "divide"       "f"            "i"           
## [11] "multi_return" "multiply"     "my.mat"       "mydata"       "num1"        
## [16] "num2"         "operator"     "output"       "pow"          "result"      
## [21] "subtract"     "x"            "y"
environment()
## <environment: R_GlobalEnv>
.GlobalEnv
## <environment: R_GlobalEnv>

위의 예에서a,b,fR_GlobalEnv 환경에 있음을 알 수 있습니다.

그런데 (f 함수의 형식 인수인) x 는 이 전역 환경에 존재하지 않습니다. 이는 함수를 정의하면 새로운 환경이 생성되기 때문입니다. 위의 예에서 함수f는 전역 환경 내에 새로운 환경을 만듭니다.

실제로 하나의 환경은 모든 정의된 개체를 담고 있는 프레임(frame)과 둘러싸고 있는 (상위) 환경에 대한 포인터(pointer)가 있습니다.

따라서x는 함수f에 의해 생성된 새로운 환경의 프레임 안에 있습니다. 이 환경은 이 환경의 상위 환경인 R_GlobalEnv에 대한 포인터를 가지고 있을 것입니다.

16.4.2 단계적 환경의 예

글로벌 환경 내에 상이한 환경이 어떻게 단계적으로 중첩될 수 있는지 다음의 예를 통해 확인해 보겠습니다.

f <- function(f_x){                          # 함수 f를 정의합니다.
     g <- function(g_x){                     # 함수 f내에서 함수 g를 정의합니다.
          print("Inside g")                  
          print(environment())                 # g 함수 내의 환경을 반환합니다.
          print(ls())                          # g 함수 내의 개체를 반환합니다.
     }
     g(5)                                    # f 환경에서 g함수를 호출합니다.
     print("Inside f")    
     print(environment())                    # f 함수 내의 환경을 반환합니다
     print(ls())                             # f 함수 내의 개체를 반환합니다.
}

이제 명령 프롬프트에서 이 함수를 실행하면 다음과 같은 결과를 얻게 될 것입니다.

f(6)                                         # 함수 f를 호출합니다.
                                             # g 함수 내의 환경과 개체를 반환하고, 
                                             # 또한 f 함수 내의 환경과 개쳬를 반환합니다.

environment()                                # 최상위의 환경을 반환합니다.
## <environment: R_GlobalEnv>
ls()                                         # 최상위의 개체를 반환합니다.
##  [1] "a"            "add"          "b"            "check"        "choice"      
##  [6] "coin"         "counter"      "divide"       "f"            "i"           
## [11] "multi_return" "multiply"     "my.mat"       "mydata"       "num1"        
## [16] "num2"         "operator"     "output"       "pow"          "result"      
## [21] "subtract"     "x"            "y"

여기에서 우리는 함수 f 안에서 함수 g를 정의하였다. 두 함수 모두 각각의 프레임 내에서 서로 다른 객체를 갖는 다른 환경을 가지고 있음을 분명히 알 수 있습니다.

16.4.3 R 프로그래밍 범위

변수가 프로그램 내에서 적용되는 범위에 대한 개념을 이해하기 위해서 다음 예를 살펴 보겠습니다.

outer_func <- function(){
                 b <- 20
                 inner_func <- function(){
                               c <- 30
                 }
               }
a <- 10

16.4.3.1 전역 변수

전역 변수(global variables)는 프로그램이 실행되는 동안 계속 존재하고 있는 변수입니다. 이 전역 변수는 프로그램의 모든 부분에서 액세스할 수 있고 변경도 가능합니다. 그러나 전역 변수는 함수의 관점에 의존하기도 합니다.

예를 들어 위의 예에서inner_func()의 관점에서 보면ab는 모두 전역 변수입니다. 그러나outer_func()의 관점에서 보면b지역 변수(local variables)이고a전역 변수입니다. 변수couter_func()의 입장에서는 전혀 알 수 없습니다.

16.4.3.2 지역 변수

반면에 지역 변수(local variables)는 함수와 같은 프로그램의 특정 부분에만 존재하며 함수 호출이 종료되면 사라지는 변수입니다.

위의 프로그램에서 보면 변수 c는 지역 변수에 해당합니다.

inner_func() 함수를 사용하여 변수에 값을 할당하면 변경 사항은 그 함수 내에서만 적용될 뿐이며 inner_func() 함수 외부에서는 이 변수에 접근할 수 없습니다.

이는 전역 변수와 지역 변수의 이름이 같더라도 마찬가지 입니다.

예를 들어 아래와 같은 함수가 있다면.

outer_func <- function(){
                 a <- 20
                 inner_func <- function(){
                                  a <- 30
                                  print(a)
              }
              inner_func()
              print(a)
}

이제 이 함수들을 호출해 보겠습니다.

a <- 10                                 # 전역 변수 a에 10을 할당합니다.
outer_func()                            # outer_func() 함수를 호출합니다.
## [1] 30
## [1] 20
print(a)                                # a를 출력합니다.
## [1] 10

변수 aouter_func() 함수와 inner_func() 함수 등의 각각의 환경 프레임 내에서 지역 변수로 생성이 되었으며, 글로벌 환경 프레임의 변수 a와는 다릅니다.

16.4.3.3 전역 변수 접근

지역의 환경에서 전역 변수의 값을 접근할 수 있지만, 그 전역 변수에 값을 할당하려고하면 새 지역 변수가 생성됩니다. 이럴 때 전역 변수에 값을 할당하고자 한다면 수퍼 할당 연산자 <<-를 사용합니다.

함수 내에서 이 연산자를 사용하면, 상위 환경 프레임에 있는 변수를 검색하고, 찾을 수 없는 경우 전역 환경에 도달할 때까지 상위의 레벨을 계속 검색하게 됩니다.

변수가 여전히 발견되지 않으면 글로벌 환경 수준에서 그 변수를 생성하고 할당하게 됩니다.

outer_func <- function(){
                 inner_func <- function(){
                 a <<- 30                       # 수퍼 할당 연산자(<<-)를 사용하여 "전역 변수"에 할당합니다.
                 print(a)
              }
              inner_func()
              print(a)
}

이 함수를 실행해 보면 다음과 같은 결과를 확인할 수 있습니다.

outer_func()                  # inner_func() 함수 내의 a와 outer_func() 함수 내의 a가 출력됩니다.
## [1] 30
## [1] 30
print(a)                      # 전역 변수 a 값이 출력됩니다.
## [1] 30

inner_func) 함수 내에 a <<- 30 할당문이 있기 때문에 상위의 환경인 outer_func() 환경에서 변수a를 찾습니다. 그러나 검색이 실패하여 다시 그 상위 환경인 전역 환경 즉, R_GlobalEnv에서 검색합니다.

이 전역 환경에서도a가 발견되지 않기 때문에 inner_func() 내에서 변수 a가 전역 변수로 생성되고 할당됩니다. 따라서 이 a 변수는 inner_func() 함수 내에서 뿐 아니라 out_func() 함수 내에서도 참조되어 출력이 되는 것입니다.