R Language Definition - 언어 객체의 직접 조작
R Language Definition - 언어 객체의 직접 조작
R에서 수정할 수 있는 언어 객체의 종류와 호출 객체를 직접 다루는 방법을 알아볼게요. 이 내용은 R Language Definition 6.1절을 옮긴 거예요.
출처: 문서
본문
6.1 언어 객체의 직접 조작
수정할 수 있는 언어 객체에는 세 종류가 있어요. 호출(call), 표현식(expression), 함수(function)이 그것이에요. 여기서는 호출 객체에 집중할게요. 호출 객체는 때때로 "평가되지 않은 표현식(unevaluated expressions)"이라고 불리기도 하는데, 이 용어는 조금 혼란스러울 수 있어요. 호출 객체를 얻는 가장 직접적인 방법은 quote에 표현식 인자를 넘기는 거예요.
> e1 <- quote(2 + 2)
> e2 <- quote(plot(x, y))
인자는 평가되지 않아요. 결과는 단순히 파싱된 인자일 뿐이에요. e1과 e2 객체는 나중에 eval로 평가하거나, 데이터처럼 다룰 수도 있어요. e2 객체가 mode "call"을 가진다는 사실은 가장 직관적으로 이해하기 쉬워요. plot 함수를 인자들과 함께 호출하는 구조니까요. 그런데 e1도 사실 이항 연산자 +를 두 인자로 호출하는 것과 정확히 같은 구조예요. 다음 코드가 그 사실을 분명하게 보여줘요.
> quote("+"(2, 2))
2 + 2
호출 객체의 구성요소는 리스트처럼 생긴 문법으로 접근할 수 있어요. 그리고 실제로는 as.list와 as.call을 사용해 리스트로 변환하거나, 리스트에서 다시 호출 객체로 변환할 수도 있어요.
> e2[[1]]
plot
> e2[[2]]
x
> e2[[3]]
y
키워드 인자 매칭을 사용하면, 키워드를 리스트 태그로 사용할 수 있어요.
> e3 <- quote(plot(x = age, y = weight))
> e3$x
age
> e3$y
weight
앞의 예들에서 호출 객체의 구성요소는 모두 mode "name"이에요. 호출 안의 식별자는 일반적으로 그렇죠. 하지만 호출의 구성요소는 상수일 수도 있어요. 상수는 어떤 타입이든 될 수 있는데, 호출을 성공적으로 평가하려면 첫 번째 구성요소는 함수가 되는 게 좋아요. 또는 구성요소가 하위 표현식에 해당하는 다른 호출 객체일 수도 있어요. mode가 name인 객체는 as.name을 이용해 문자열로 만들 수 있어요. 그래서 e2 객체를 다음과 같이 수정할 수 있답니다.
> e2[[1]] <- as.name("+")
> e2
x + y
하위 표현식은 그 자체가 호출인 하나의 구성요소일 뿐이라는 사실은 다음 예를 보면 확인할 수 있어요.
> e1[[2]] <- e2
> e1
x + y + 2
입력에 있는 모든 묶음 괄호는 파싱된 표현식에 그대로 보존돼요. 괄호는 인자를 하나 받는 함수 호출로 표현되므로, 4 - (2 - 2)는 접두 표기법에서 "-"(4, "(" ("-"(2, 2)))가 돼요. 평가 과정에서 ( 연산자는 인자를 그대로 돌려줄 뿐이에요.
조금 아쉽지만, 사용자 입력을 보존하면서도 최소 형태로 저장하고, deparse한 표현식을 다시 파싱했을 때 같은 표현식이 나오도록 보장하는 parser/deparser 조합을 만드는 건 쉬운 일이 아니에요.
실제로 R의 파서는 완벽하게 역변환할 수 없고, 디파서도 마찬가지예요. 다음 예제가 그걸 보여줘요.
> str(quote(c(1,2)))
language c(1, 2)
> str(c(1,2))
num [1:2] 1 2
> deparse(quote(c(1,2)))
[1] "c(1, 2)"
> deparse(c(1,2))
[1] "c(1, 2)"
> quote("-"(2, 2))
2 - 2
> quote(2 - 2)
2 - 2
다만 deparse한 표현식은 원래 표현식과 동등한 값으로 평가되어야 해요. 반올림 오차 범위 내에서요.
... 흐름 제어 구조의 내부 저장 방식 ... Splus 비호환성에 유의하세요 ...