부분집합 할당
부분집합 할당
R에서 구조의 부분집합에 값을 할당하는 방법을 설명할게요. 복잡한 할당의 일반 원리와 <<- 슈퍼할당까지 함께 살펴봐요.
본문
3.4.4 부분집합 할당
구조의 부분집합에 대한 할당은 복잡한 할당(complex assignment)을 위한 일반 메커니즘의 특별한 경우예요.
x[3:5] <- 13:15
이 명령의 결과는 다음 코드가 실행된 것과 같아요.
`*tmp*` <- x
x <- "[<-"(`*tmp*`, 3:5, value=13:15)
rm(`*tmp*`)
인덱스는 먼저 숫자 인덱스로 변환되고, 그다음 요소들이 숫자 인덱스를 따라 순차적으로 교체돼요. 마치 for 루프를 사용한 것처럼요. *tmp*라는 이름의 기존 변수가 있다면 덮어쓰여지고 삭제되므로, 이 변수 이름은 코드에서 사용하면 안 돼요.
같은 메커니즘은 [ 이외의 함수에도 적용할 수 있어요. 치환 함수(replacement function)의 이름은 원래 함수 이름에 <-를 붙인 형태예요. 그 마지막 인자는 반드시 value라는 이름이어야 하며, 새로 할당할 값이에요. 예를 들어,
names(x) <- c("a","b")
는 다음 코드와 동일해요.
`*tmp*` <- x
x <- "names<-"(`*tmp*`, value=c("a","b"))
rm(`*tmp*`)
복잡한 할당의 중첩은 재귀적으로 평가돼요.
names(x)[3] <- "Three"
는 다음 코드와 동일해요.
`*tmp*` <- x
x <- "names<-"(`*tmp*`, value="[<-"(names(`*tmp*`), 3, value="Three"))
rm(`*tmp*`)
둘러싼 환경(enclosing environment)에서의 복잡한 할당(<<- 사용)도 허용돼요:
names(x)[3] <<- "Three"
는 다음 코드와 동일해요.
`*tmp*` <- get("x", envir=parent.env(), inherits=TRUE)
names(`*tmp*`)[3] <- "Three"
x <<- `*tmp*`
rm(`*tmp*`)
그리고 다음 코드와도 동일해요.
`*tmp*` <- get("x", envir=parent.env(), inherits=TRUE)
x <<- "names<-"(`*tmp*`, value="[<-"(names(`*tmp*`), 3, value="Three"))
rm(`*tmp*`)
오직 대상 변수만 둘러싼 환경에서 평가돼요. 따라서
e<-c(a=1,b=2)
i<-1
local({
e <- c(A=10,B=11)
i <-2
e[i] <<- e[i]+1
})
이 코드는 LHS와 RHS 양쪽에서 지역 변수 i의 값을 사용하고, 슈퍼할당(superassignment) 문장의 RHS에서는 지역 변수 e의 값을 사용해요. 그 결과 바깥 환경의 e가 다음과 같이 설정돼요.
a b
1 12
즉, 슈퍼할당은 다음 네 줄과 동일해요.
`*tmp*` <- get("e", envir=parent.env(), inherits=TRUE)
`*tmp*`[i] <- e[i]+1
e <<- `*tmp*`
rm(`*tmp*`)
마찬가지로
x[is.na(x)] <<- 0
는 다음 코드와 동일해요.
`*tmp*` <- get("x", envir=parent.env(), inherits=TRUE)
`*tmp*`[is.na(x)] <- 0
x <<- `*tmp*`
rm(`*tmp*`)
그리고 다음 코드와는 동일하지 않아요.
`*tmp*` <- get("x", envir=parent.env(), inherits=TRUE)
`*tmp*`[is.na(`*tmp*`)] <- 0
x <<- `*tmp*`
rm(`*tmp*`)
이 두 후보 해석은 지역 변수 x도 존재할 때만 달라져요. 슈퍼할당의 대상 변수와 같은 이름을 가진 지역 변수를 두지 않는 것이 좋아요. 이 경우는 1.9.1 이하 버전에서 잘못 처리되었기 때문에, 그만큼 이런 코드에 대한 심각한 필요는 없었던 셈이에요.