【问题标题】:How can I make slot to be filled with multiple same-type objects in R?如何在 R 中使用多个相同类型的对象填充插槽?
【发布时间】:2014-09-15 19:10:51
【问题描述】:

假设我想定义两个类,SentenceWord。每个词对象都有一个字符串和一个词性(pos)。每个句子都包含一定数量的单词,并有一个额外的数据槽。

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 类,它可以包含一些Words 和一些数字数据。

如果我这样定义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++ 中您可以拥有相同类型对象的向量。

【问题讨论】:

  • 我认为我之前的建议(已删除)很好。在句子中将其更改为单词向量而不是单词列表。我在 R 中没有做太多 OO 编程,但我认为这应该可行。
  • 它不会将它解释为一个向量,而是一个列表。使用words="vector"x &lt;- new("Sentence")x@words &lt;- c(Word(),Word(),3) 不会导致错误并使x@words 成为一个列表。
  • 可以理解吧?因为您有两个 Word 类型的元素和一个数字类型的元素?它甚至会在设置发生之前被强制执行。 3是否对应句子对象中的stats?
  • 在我看来,您希望通过 x@words
  • 一种变通方法是检查Sentence 构造函数中列表组件的类。例如,请参阅 sp 包的 Polygons 构造函数。然后您可以重新定义 @&lt;- 运算符以避免用户设置 word 插槽绕过您的约束。

标签: r class object slot


【解决方案1】:

记住 R 在向量上效果很好,第一步是考虑“单词”而不是“单词”

## constructor, accessors, subset (also need [[, [<-, [[<- methods)
.Words <- setClass("Words",
    representation(words="character", parts="character"))
words <- function(x) x@words
parts <- function(x) x@parts
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 在 Bioconductor 的 'devel' / 3.0 分支中),其中“SimpleList”采用“naive”要求列表的所有元素具有相同类的方法,而“CompressedList”具有相似的行为,但实际上是作为类似矢量的对象(具有长度()、[和[[方法]的对象)实现的,即“分区'(按末端或宽度)成组。

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) { x@word <- toupper(x@word); 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 时继续考虑向量是很有价值的——用词而不是词,人而不是人......

【讨论】:

  • 做我需要做的所有事情,而且比我预期的要多得多。非常清晰和信息丰富。给了我很多解决问题的想法!
【解决方案2】:

我建议仅解决此类问题。请记住,R 并不完全适合 OO 编程风格,并且每个解决方案都很难显示出 Java 或 C++ 等其他语言的稳固性。但是,您可以使用 words 插槽将 Sentence 类声明为列表。然后你定义你的构造函数:

   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)
   }

可以在 Polygons 类的 sp 包中找到此类构造函数的示例。您可以看到该函数的主体。

如果您想避免用户错误地设置words 槽,您可以重新定义@&lt;- 运算符,例如:

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

我不认为最后一步是必要的。无论你做什么,用户总是可以把事情搞砸。例如,他可以绕过您的构造函数直接调用new 函数。或者他可以将Word 类设置为任意对象,然后将其传递给Sentence。正如我所说,R 并不适合这种编程风格,因此您应该经常采用某种非最佳解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-10-19
    • 2018-10-31
    • 2015-05-31
    • 2020-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-17
    相关资源
    最近更新 更多