필자의 경우 컴퓨터 언어와 관련해서 공부할 때 가장 어려운 부분 중 하나가 어떤 단어의 의미가 해당 언어에서만 쓰는 특이한 것인지, 아니면 일반적인 단어로 사용하는 것인지 분간되지 않을 때다. R에서 환경(enviroment)이라는 개념이 그랬다. R Studio에 'Enviroment' 라는 창을 보고 나서야 그 의미를 이해할 수 있었다.
R에서 환경은 리스트 못지 않게 중요한 데이터 타입 중 하나다. R에서는 환경이 일반 단어가 아니고, 아토믹 벡터 또는 리스트와 같은 R 객체의 한 종류이다. 환경이라는 개념을 이해하지 못하면 클로저(closer), 함수 호출, R패키지의 작동 원리도 항상 마법처럼 느껴질 것이다. 심지어 ls(), rm() 함수도 제대로 이해하지 못한 것일 수 있다.
다음 글들에는 R 환경에 관한 설명이 잘 정리되어 있다. 여기서는 이런 글들을 이해하기 위한 기초적인 내용만 설명한다.
How R Searches and Finds Stuff : http://blog.obeautifulcode.com/R/How-R-Searches-And-Finds-Stuff/
환경과 이름 찾기(resolution)
R콘솔에서 다음과 같이 실행하면, 글로벌 환경(Global enviroment)에서 x라는 객체와 그 객체의 값(1:3이라는 정수형 벡터)에 대한 바인딩을 만드는 셈이다. R 콘솔에서 실행되는 모든 계산은 이런 글로벌 환경에서 이루어지며 R에서는 이를 .GlobalEnv 라고 이름 지어 부른다.
> x <- 1:3
다음과 같이 x를 입력 하였을 때 그 값이 출력되는 것은 글로벌 환경에 x 가 있기 때문이다. 그리고 y라는 객체는 찾을 수 없기 때문에 이것을 출력하라고 하면 오류(error)가 발생한다.
> x
[1] 1 2 3
> y
에러: 객체 'y'를 찾을 수 없습니다.
R콘솔에서도 다음과 같은 계산도 문제없이 실행된다.
> sum(1:10)
[1] 55
R에서는 함수도 하나의 객체다. 현재 글로벌 환경에는 sum이라는 객체가 존재하지 않는데, 이렇게 R은 이 함수를 사용하여 계산하는 것일까? R에서 어떤 이름을 주고 객체를 찾을 때는 find() 함수를 사용할 수 있다.
> find("x")
[1] ".GlobalEnv"
> find("y")
character(0)
> find("sum")
[1] "package:base"
이 의미는 x라는 이름을 가진 객체는 .GlobalEnv 에 존재하고 y라는 이름의 객체는 존재하지 않으며, sum이라는 객체는 package:base 에 존재한다는 것이다. 이것을 보더라도 R이 뭔가를 찾을 때는 현재 있는 공간, 즉 현재의 환경만을 뒤지는 것이 아님을 알 수 있다. 실제로 현재의 환경에서 탐색한 뒤 있으면 그 값을 출력하고, 없으면 다음 단계의 환경을 탐색한다. 마치 인형 속에 인형이 들어가 있는 러시아 인형마트료시카의 구조와 비슷하다. 즉 환경들이 계속 이어진 구조를 지닌 것이다.
이렇게 이어진 구조를 탐색 경로(Search Path)라고 하는데 , search()라는 함수를 사용하여 알 수 있다. 다음과 같이 처음 .GlobalEnv 에서 시작하여 마지막 package:base 까지 환경을 이동하면서 어떤 객체의 위치를 찾는다. 없으면 오류가 발생하는 것이다.
> search()
[1] ".GlobalEnv" "package:stats" "package:graphics"
[4] "package:grDevices" "package:utils" "package:datasets"
[7] "package:methods" "Autoloads" "package:base"
환경은 구조적으로 프레임(frame)과 인클로저(enclosure)로 구성된다. 프레임이란 이름과 값에 대한 바인딩이고, 인클로저란 이 환경에서 뭔가를 찾지 못할 때 다음에 어느 환경으로 이동할지를 지목해주는 포인터이다. 환경은 이름과 값에 대한 바인딩의 집합이라 정의할 수도 있는데, 이는 환경의 프레임에 대한 부분을 말하는 것이다. 이처럼 R을 사용하는 순간, 우리는 이미 많은 환경을 만들고 사용하게 되는 것이다.
특별한 용도의 객체로서 환경의 사용
함수와 연관된 환경과 같이 눈에 보이지 않게 작동하는 환경도 있지만, 사용자가 직접 환경을 만들 수도 있다. new.env()라는 함수를 만들어 본다.
> e1 <- new.env()
> typeof(e1)
[1] "environment"
이렇게 만든 e1이라는 환경에 x라는 객체를 만들고 그 값 1:3을 할당해본다.
> e1$x <- 1:3
> e1$x
[1] 1 2 3
환경이라는 객체는 아토믹 벡터, 데이터 프레임, 리스트 등 다른 R 객체와 근본적으로 다른 점이 있다.
프로그램 용어로는 mutable 하다고 표현하는데, 있는 그 자체로 값이 수정될 수 있다는 특징을 가진다. 대부분의 R 객체는 immutable하다. 객체를 다른 객체로 복사하고 값을 좀 바꿔보면 그 차이를 알 수 있다.
> a <- 1:3
> b <- a
> a
[1] 1 2 3
> b
[1] 1 2 3
> a[2] <- 5
> a
[1] 1 5 3
> b
[1] 1 2 3
이것을 보면 a의 값을 b에 할당하고, 원래의 a의 두 번째 요소의 값을 수정해도 b의 값은 바뀌지 않는 것을 알 수 있다. 다음에는 환경을 만들고 비슷한 과정을 진행해본다.
> e_a <- new.env()
> e_b <- e_a
> e_a
<environment: 0x00000000046cee50>
> e_b
<environment: 0x00000000046cee50>
> e_a$j <- 1:3
> e_b$j
[1] 1 2 3
> identical(e_a,e_b)
[1] TRUE
환경 e_a를 e_b로 할당하고, e_a의 값을 일부 수정해보았다. 코드를 보면 e_b는 어떤 할당 등을 하지 않았음에도 불구하고 j라는 객체를 가지게 되었음을 알 수 있다. 특히 이러한 특징은 어떤 상태(state)를 저장하기 좋기 때문에 환경 객체를 사용하면 구현하기 편리하다. 여기서 상태란 프로그램이 실행되고 있을 때 특정 시점에서 프로그램 변수들의 값을 말한다.
'전공 > R프로그래밍' 카테고리의 다른 글
R프로그래밍 18. 표현식의 사용 (0) | 2019.08.07 |
---|---|
R프로그래밍 17. 느긋한 평가 조급한 평가 (0) | 2019.08.06 |
R프로그래밍 15. R 함수 (0) | 2019.07.31 |
R프로그래밍 14. $,[[,[ 의 차이점 (0) | 2019.07.22 |
R 프로그래밍 13. 데이터 프레임 서브 세팅 (0) | 2019.07.10 |