R Language Definition: 5.1 정의
R Language Definition: 5.1 정의
R의 객체 지향 시스템과 클래스 디스패치의 핵심 개념을 살펴볼게요. UseMethod와 NextMethod가 어떻게 동작하는지 함께 알아봐요.
출처: 문서
본문
5.1 정의 (Definition)
R은 완전한 객체 지향 시스템을 갖추는 대신, 클래스 시스템과 객체의 클래스에 기반한 디스패치(dispatch) 메커니즘을 사용해요. 인터프리터 코드의 디스패치 메커니즘은 평가 프레임(evaluation frame)에 저장된 네 가지 특별한 객체에 의존해요. 이 특별한 객체들은 .Generic, .Class, .Method, .Group이에요. 내부 함수와 타입에 사용되는 별도의 디스패치 메커니즘은 다른 곳에서 다룰게요.
클래스 시스템은 class 속성을 통해 구현돼요. 이 속성은 클래스 이름들의 문자 벡터예요. 따라서 클래스 "foo"의 객체를 만들려면 문자열 "foo"가 들어 있는 class 속성을 붙이기만 하면 돼요. 그래서 거의 모든 것을 클래스 "foo"의 객체로 바꿀 수 있죠.
객체 시스템은 두 디스패치 함수 UseMethod와 NextMethod를 통해 제네릭 함수를 활용해요. 객체 시스템의 전형적인 사용 방식은 우선 제네릭 함수를 호출하는 것으로 시작해요. 이 제네릭 함수는 대개 아주 단순하고 코드 한 줄로 구성돼요. 시스템 함수 mean이 바로 그런 함수예요.
> mean
function (x, ...)
UseMethod("mean")
mean을 호출할 때는 인수를 몇 개든 전달할 수 있지만, 첫 번째 인수가 특별해요. 첫 번째 인수의 클래스로 어떤 메서드를 호출할지 결정해요. .Class 변수에는 x의 class 속성 값이 설정되고, .Generic에는 문자열 "mean"이 설정돼요. 그다음 호출할 올바른 메서드를 검색해요. mean의 다른 인수들의 class 속성은 무시돼요.
예를 들어 x의 class 속성에 "foo"와 "bar"가 순서대로 들어 있다고 해볼게요. 그러면 R은 먼저 mean.foo라는 함수를 찾아봐요. 없으면 mean.bar를 찾고, 그것도 없으면 마지막으로 mean.default를 찾아요. 마지막 검색까지 실패하면 R은 오류를 보고해요. 기본 메서드(default method)는 항상 작성해 두는 것이 좋아요. 이 맥락에서 mean.foo 같은 함수들을 메서드라고 부르는 점에 유의해요.
NextMethod는 디스패치를 위한 또 다른 메커니즘을 제공해요. 함수 안 어디에서든 NextMethod 호출을 넣을 수 있어요. 그다음 호출할 메서드는 주로 현재 .Class와 .Generic 값을 기준으로 결정돼요. 이 방식은 다소 문제가 있을 수 있어요. 메서드가 사실 일반 함수라서 사용자가 직접 호출할 수 있기 때문이에요. 직접 호출하면 .Generic이나 .Class 값이 없어요.
메서드가 직접 호출됐는데 NextMethod 호출을 포함하면, NextMethod의 첫 번째 인수로 제네릭 함수를 결정해요. 이 인수가 제공되지 않으면 오류를 알려줘요. 그러니 항상 이 인수를 넘겨주는 것이 좋아요.
메서드가 직접 호출되는 경우에는 메서드의 첫 번째 인자의 class 속성이 .Class 값으로 사용돼요.
메서드 자체도 일종의 상속을 제공하기 위해 NextMethod를 사용해요. 일반적으로 특정 메서드는 데이터를 준비하는 몇 가지 작업을 한 다음, NextMethod 호출을 통해 다음에 적합한 메서드를 호출해요.
다음 간단한 예를 살펴봐요. 2차원 유클리드 공간의 점은 직교 좌표(x-y) 또는 극좌표(r-theta)로 지정할 수 있어요. 따라서 점의 위치 정보를 저장하기 위해 "xypoint"와 "rthetapoint"라는 두 클래스를 정의할 수 있어요. 모든 xypoint 데이터 구조는 x 성분과 y 성분을 갖는 리스트예요. 모든 rthetapoint 객체는 r 성분과 theta 성분을 갖는 리스트예요.
이제 두 타입의 객체 중 어느 쪽에서든 x 위치를 얻고 싶다고 해볼게요. 이는 제네릭 함수를 통해 쉽게 구현할 수 있어요. 제네릭 함수 xpos를 다음과 같이 정의해요.
xpos <- function(x, ...)
UseMethod("xpos")
이제 메서드를 정의할 수 있어요.
xpos.xypoint <- function(x) x$x
xpos.rthetapoint <- function(x) x$r * cos(x$theta)
사용자는 두 표현 중 아무거나 인자로 xpos 함수를 호출하기만 하면 돼요. 내부 디스패치 방식이 객체의 클래스를 찾아 적절한 메서드를 호출해요.
다른 표현을 추가하는 것도 아주 쉬워요. 새 제네릭 함수를 작성할 필요 없이 메서드만 작성하면 되거든요. 기존 시스템에 추가하기도 쉬워요. 사용자는 기존 표현이 아니라 새 표현만 처리하면 되니까요.
이 방법론은 주로 다양한 타입의 객체에 대해 특화된 출력(printing)을 제공하는 데 사용돼요. print에는 메서드가 약 40개 있어요.