Избегание учета окружающих кадров при получении значения поля эталонного класса S4

Я большой поклонник эталонных классов S4, поскольку они допускают гибридный стиль программирования (функциональность / передача по значению по сравнению с oop / передача по ссылке; пример) и, таким образом, значительно повышают гибкость.

Тем не менее, я думаю, что я только что столкнулся с нежелательным поведением в отношении способа сканирования R через окружения / кадры, когда вы просите его извлечь определенное значение поля с помощью метода. $field() (см. страницу помощи). Проблема в том, что R также, кажется, смотрит в окружающих средах / кадрах, если нужное поле не найдено в фактической локальной / целевой среде (которая была бы средой, составляющей эталонный класс S4), то есть это похоже на работу get(<objname>, inherits=TRUE) (см. страницу помощи).

Актуальный вопрос

Чтобы R просто посмотрел на локальную / целевую среду, я подумал что-то вроде $field(name="<fieldname>", inherits=FALSE) но $field() не имеет ... аргумент, который позволил бы мне пройти inherits=FALSE вместе с get() (который, я думаю, называется где-то по пути). Есть ли обходной путь к этому?


Пример кода

Для тех, кто интересуется более подробной информацией: вот небольшой пример кода, иллюстрирующий поведение

setRefClass("A", fields=list(a="character"))

x <- getRefClass("A")$new(a="a")

Есть поле a в классе A, так что он найден в целевой среде и возвращается значение:

> x$field("a")
[1] "a"

Все выглядит иначе, если мы пытаемся получить доступ к полю, которое не является полем ссылочного класса, но имеет имя, идентичное имени некоторого другого объекта в рабочей области / поисковом пути (в этом случае "lm"):

require("MASS")
> x$field("lm")

function (formula, data, subset, weights, na.action, method = "qr", 
    model = TRUE, x = FALSE, y = FALSE, qr = TRUE, singular.ok = TRUE, 
    contrasts = NULL, offset, ...) 
{
    ret.x <- x
    ret.y <- y

    [omitted]

    if (!qr) 
        z$qr <- NULL
    z
}
<bytecode: 0x02e6b654>
<environment: namespace:stats>

Не совсем то, что я ожидал бы на этом этапе. ИМХО ошибка или хотя бы предупреждение было бы намного лучше. Или метод открытия $field() для аргументов, которые могут быть переданы другим функциям через ..., Я предполагаю, что где-то по пути get() вызывается при звонке $field()Таким образом, что-то подобное может предотвратить возникновение описанного выше поведения:

x$field("digest", inherits=FALSE)

Обходной путь: собственное предложение

Это должно сработать, но, может быть, есть что-то более элегантное, что не требует спецификации нового метода поверх $field():

setRefClass("A", fields=list(a="character"),
    methods=list(
        myField=function(name, ...) {
            # VALIDATE NAME //
            if (!name %in% names(getRefClass(class(.self))$fields())) {
                stop(paste0("Invalid field name: '", name, "'"))
            }
            # //
            .self$field(name=name)
        }
    )
)
x <- getRefClass("A")$new(a="a")

> x$myField("a")
[1] "a"
> x$myField("lm")
Error in x$myField("lm") : Invalid field name: 'lm'

1 ответ

Решение

По умолчанию field() метод можно заменить на свой. Так что добавление inherits Аргумент, чтобы избежать включения рамок, это просто вопрос захвата существующих x$field определение и добавление его...

setRefClass( Class="B",
             fields= list( a="character" ),
             methods= list(
               field = function(name, value, inherits=TRUE ) {
                 if( missing(value) ) {
                   get( name, envir=.self, inherits=inherits )
                 } else {
                   if( is.na( match( name, names( .refClassDef@fieldClasses ) ) ) ) {
                     stop(gettextf("%s is not a field in this class", sQuote(name)), domain = NA)
                   }
                   assign(name, value, envir = .self)
                 }
               }
             ),
)

Или вы можете получить хорошее сообщение об ошибке с небольшой перестановкой

setRefClass( Class="C",
             fields= list( a="character" ),
             methods= list(
               field = function(name, value, inherits=TRUE ) {
                if( is.na( match( name, names( .refClassDef@fieldClasses ) ) ) &&
                      ( !missing(value) || inherits==FALSE) ) {
                  stop(gettextf("%s is not a field in this class", sQuote(name)), domain = NA)
                }

                if( missing(value) ) {
                    get( name, envir=.self, inherits=inherits )
                } else {
                  assign(name, value, envir = .self)
                }
               }
             ),
)

Поскольку вы можете определить любой из ваших собственных методов для замены значений по умолчанию, практически любая логика, которую вы хотите, может быть реализована для ваших рефклассов. Возможно, ошибка, если переменная получена с использованием наследования, но режим соответствует c ("выражение", "имя", "символ", "функция") и предупреждение, если оно не совпадает напрямую с именами локальных полей refClass?

Другие вопросы по тегам