У меня возникли проблемы с определением правильного способа определения операторов подмножества [
, $
и [[
для класса S4.
Может ли кто-нибудь предоставить мне базовый пример определения этих трех для класса S4?
У меня возникли проблемы с определением правильного способа определения операторов подмножества [
, $
и [[
для класса S4.
Может ли кто-нибудь предоставить мне базовый пример определения этих трех для класса S4?
Обнаружьте общий, чтобы мы знали, к чему мы стремимся
> getGeneric("[")
standardGeneric for "[" defined from package "base"
function (x, i, j, ..., drop = TRUE)
standardGeneric("[", .Primitive("["))
<bytecode: 0x32e25c8>
<environment: 0x32d7a50>
Methods may be defined for arguments: x, i, j, drop
Use showMethods("[") for currently available ones.
Определите простой класс
setClass("A", representation=representation(slt="numeric"))
и реализовать метод
setMethod("[", c("A", "integer", "missing", "ANY"),
## we won't support subsetting on j; dispatching on 'drop' doesn't
## make sense (to me), so in rebellion we'll quietly ignore it.
function(x, i, j, ..., drop=TRUE)
{
## less clever: update slot, return instance
## [email protected] = [email protected][i]
## x
## clever: by default initialize is a copy constructor, too
initialize(x, [email protected][i])
})
В действии:
> a = new("A", slt=1:5)
> a[3:1]
An object of class "A"
Slot "slt":
[1] 3 2 1
Существуют различные стратегии поддержки (неявно) многих подписи, например, вы, вероятно, также захотите поддерживать логические и символьные значения индекса, возможно, для я и j. Наиболее прямолинейным является шаблон "фасад", где каждый метод делает некоторое предварительное принуждение к общему типу индекса подмножества, например, integer
, чтобы разрешить повторный порядок и повторение записей индекса, а затем использует callGeneric
to вызывается один метод, который выполняет работу подмножества класса.
Для [[
нет концептуальных отличий, кроме того, что нужно уважать семантику возврата содержимого, а не другого экземпляра объекта, что подразумевается под [
. Для $
имеем
> getGeneric("$")
standardGeneric for "$" defined from package "base"
function (x, name)
standardGeneric("$", .Primitive("$"))
<bytecode: 0x31fce40>
<environment: 0x31f12b8>
Methods may be defined for arguments: x
Use showMethods("$") for currently available ones.
и
setMethod("$", "A",
function(x, name)
{
## 'name' is a character(1)
slot(x, name)
})
с
> a$slt
[1] 1 2 3 4 5
Я бы сделал так, как предложил @Martin_Morgan для упомянутых вами операторов. Я бы добавил пару пунктов, хотя:
1) Я был бы осторожен в определении оператора $
для доступа к слоту S4 (если вы не хотите получить доступ к столбцу из фрейма данных, который хранится в определенном слоте?). Общее предложение состоит в том, чтобы написать функции доступа, такие как getMySlot()
и setMySlot()
, чтобы получить необходимую информацию. Вы можете использовать оператор @
для доступа к данным из этих слотов, хотя get и set лучше всего использовать в качестве пользовательского интерфейса. Использование $
может ввести в заблуждение для пользователя, который, вероятно, ожидал бы data.frame. См. this Учебник S4 от Christophe Genolini для углубленного обсуждения этих вопросов. Если вы не собираетесь использовать $
, пренебрегайте моим предложением (но учебник по-прежнему остается отличным ресурсом!).
2) Если вы определяете [
и [[
для наследования из другого класса, например вектора, вы также хотите определить el()
(эквивалентно [][[1L]]
), или первый элемент из подмножества []
) и length()
. В настоящее время я пишу класс для наследования из числа, а числовые методы будут автоматически пытаться использовать эти функции из вашего класса. Если класс предназначен для более ограниченного или личного использования, это может не быть проблемой.
Я прошу прощения, я бы оставил это в качестве комментария, но я новичок в SO, и у меня пока нет репутации!