2014-09-15 1 views
5

Предположим, я хочу определить два класса классов, Sentence и Word. Каждый объект слова имеет строку символов и часть речи (pos). Каждое предложение содержит некоторое количество слов и имеет дополнительный слот для данных.Как сделать слот заполнен несколькими объектами одного и того же типа в R?

Категория Word не определена.

wordSlots <- list(word = "character", pos = "character") 
wordProto <- list(word = "", pos = "") 
setClass("Word", slots = wordSlots, prototype = wordProto)  
Word <- function(word, pos) new("Word", word=word, pos=pos) 

Теперь я хочу сделать Sentence класс, который может содержать некоторые Word с и некоторые числовые данные.

Если я определяю Sentence класс как так:

sentenceSlots <- list(words = "Word", stats = "numeric") 
sentenceProto <- list(words = Word(), stats = 0) 
setClass("Sentence", slots = sentenceSlots, prototype = sentenceProto) 

Тогда предложение может содержать только одно слово. Я мог бы определить его со многими слотами, по одному для каждого слова, но тогда он будет ограничен по длине.

Однако, если я определить Sentence класс вроде этого:

sentenceSlots <- list(words = "list", stats = "numeric") 
sentenceProto <- list(words = list(Word()), stats = 0) 
setClass("Sentence", slots = sentenceSlots, prototype = sentenceProto) 

может содержать столько слов, сколько я хочу, но слот words может содержать объекты, которые не являются класса Word.

Есть ли способ сделать это? Это будет похоже на C++, где вы можете иметь вектор объектов того же типа.

+0

Я думаю, что мое предыдущее предложение (которое я удалил), хорошо. В предложении измените его на вектор слов, а не на список слов. Я не делаю много программирования OO в R, но я думаю, что это должно сработать. – DMT

+0

Он не интерпретирует его как вектор, а список. С 'words =" vector "и' x <- new ("Sentence") ',' x @ words <- c (Word(), Word(), 3) 'не вызывает ошибок и делает' x @ words' список. –

+0

понятное право? Потому что у вас есть два элемента типа Word и один из чисел типа? Это будет принудительно до того, как настройка будет иметь место. Соответствует ли 3 критериям статистики в объекте предложения? – DMT

ответ

6

Вспоминая, что R хорошо работает на векторах, первый шаг должен думать о «Слова», а не «Слова»

## constructor, accessors, subset (also need [[, [<-, [[<- methods) 
.Words <- setClass("Words", 
    representation(words="character", parts="character")) 
words <- function(x) [email protected] 
parts <- function(x) [email protected] 
setMethod("length", "Words", function(x) length(words(x))) 
setMethod("[", c("Words", "ANY", "missing"), function(x, i, j, ...) { 
    initialize(x, words=words(x)[i], parts=parts(x)[i], ...) 
}) 

## validity 
setValidity("Words", function(object) { 
    if (length(words(object)) == length(parts(object))) 
     NULL 
    else 
     "'words()' and 'parts()' are not the same length" 
}) 

@ предложение Nicola о том, что один есть список слов был формализован в IRanges пакет (фактически, S4Vectors в 'devel'/3.0 в Bioconductor), где «SimpleList» использует «наивный» подход, требующий, чтобы все элементы списка имели один и тот же класс, тогда как «CompressedList» имеет подобное поведение, но фактически реализуется как векторный объект (один с помощью методов length(), [и [[методов], которые «разделены» (либо по контуру, либо по ширине) в группы.

library(IRanges) 
.Sentences = setClass("Sentences", 
    contains="CompressedList",  
    prototype=c(elementType="Words")) 

Можно было бы тогда написать более удобный конструктор, но основная функциональность

## 0 Sentences 
.Sentences() 
## 1 sentence of 0 words 
.Sentences(unlistData=.Words(), partitioning=PartitioningByEnd(0)) 
## 3 sentences of 2, 0, and 3 words 
s3 <- .Sentences(unlistData=.Words(words=letters[1:5], parts=LETTERS[1:5]), 
    partitioning=PartitioningByEnd(c(2, 2, 5))) 

приводит к

> s3[[1]] 
An object of class "Words" 
Slot "word": 
[1] "a" "b" 

Slot "part": 
[1] "A" "B" 

> s3[[2]] 
An object of class "Words" 
Slot "word": 
character(0) 

Slot "part": 
character(0) 

> s3[[3]] 
An object of class "Words" 
Slot "word": 
[1] "c" "d" "e" 

Slot "part": 
[1] "C" "D" "E" 

Обратите внимание, что некоторые типичные операции быстро, потому что они могут работать на «незарегистрированных» элементах без создания или уничтожения экземпляров S4, например, принуждение всех «слов» к верхнему регистру

setMethod(toupper, "Words", function(x) { [email protected] <- toupper([email protected]); x }) 
setMethod(toupper, "Sentences", function(x) relist(toupper(unlist(x)), x)) 

Это «быстрый» для больших коллекций предложений, потому что unlist/relist действительно находится в доступе к слоту и создании одного экземпляра «Слова». Scalable Genomics with R and Bioconductor описывает эту и другие стратегии.

В ответ @nicola говорит, что «R не идеально подходит для стиля программирования OO», но, вероятно, более полезно понять, что объектно-ориентированный стиль R S4 отличается от C++ и Java, так же, как R отличается от C. В частности, это действительно ценный для продолжения мышления в терминах векторов при работе с S4 - Слова, а не Word, люди, а не человек ...

+0

Делает все, что мне нужно, и больше, чем я ожидал, было легко. Очень ясный и информативный. Дает мне много идей по решению моей проблемы! –

4

Я предлагаю только обход для этого класса проблем. Имейте в виду, что R не идеально подходит для стиля программирования OO, и каждое решение вряд ли продемонстрирует прочность других языков, таких как Java или C++. Однако вы можете объявить свой класс Sentence слотом words в виде списка. Затем вы определяете конструктор, как, например:

Sentence<-function(words,stats) { 
    #check for the components' class of words argument 
    if (!is.list(words) || !all(sapply(words,function(x) class(x)=="Word"))) stop("Not valid words argument") 
    #create the object 
     new("Sentence", words=words, stats=stats) 
    } 

Примером такого конструктора можно найти в sp пакете для Polygons класса. Вы можете увидеть тело этой функции.

Если вы хотите, чтобы избежать этого, пользовательских наборов неверно слота words, вы можете переопределить @<- оператор такие как:

"@<-.Sentence"<-function(sentence,...) invisible(sentence) 

Я не думаю, что последний шаг необходим. Независимо от того, что вы делаете, пользователь всегда может повредить вещи. Например, он мог напрямую вызвать функцию new в обход вашего конструктора. Или он может установить класс Word произвольному объекту, а затем передать его Sentence. Как я уже сказал, R не идеален для этого стиля программирования, поэтому вы часто должны принимать какое-то неоптимальное решение.

Смежные вопросы