Получение фактического исходного выражения, определяющего ссылочный класс S4, из связанного с ним объекта

Короче (актуальный вопрос)

Как я могу получить доступ к фактическому исходному коду/выражению, определяющему ссылочный класс S4 (см. ?setRefClass), из объекта, возвращаемого либо getClass("MyClass"), либо getRefClass("MyClass") (поэтому после< /strong> он был получен, а не путем изучения фактического исходного файла)?

Домашнее задание, которое я сделал

Поскольку в R все является объектом, я могу получить исходный код/выражение

1) Обычная функция, просто исследующая соответствующий объект:

foo <- function(x) print(x)

> foo
function(x) print(x)

2) Формальный метод путем получения объекта метода определенного метода через selectMethod:

setGeneric(name="myMethod", signature=c("x"),
    def=function(x) standardGeneric("myMethod")       
)
setMethod(
    f="myMethod", 
    signature=signature(x="numeric"), 
    definition=function(x) print(x)
)
def <- selectMethod(f="myMethod", signature=c(x="numeric"))

# Get actual source code/expression
> attributes(def)$srcref
function(x) print(x)

Но для эталонных классов S4 все выглядит иначе:

setRefClass(Class="MyClass", fields=list(x.1="character"))

def <- getRefClass("MyClass")

# Inspect object >> no expression
> def
Generator object for class "MyClass":

Class fields:

Name:        x.1
Class: character

 Class Methods:  
    "callSuper", "copy", "export", "field", "getClass", "getRefClass", "import", 
"initFields", "show", "trace", "untrace"


 Reference Superclasses:  
    "envRefClass"

def.temp <- attributes(attributes(def)$.xData$def)

# Inspect attributes >> no expression
> attributes(def.temp)
$names
 [1] "fieldClasses"    "fieldPrototypes" "refMethods"      "refSuperClasses"
 [5] "slots"           "contains"        "virtual"         "prototype"      
 [9] "validity"        "access"          "className"       "package"        
[13] "subclasses"      "versionKey"      "sealed"          "class"          

# Alternatively:
> names(attributes(getClass("MyClass")))
 [1] "fieldClasses"    "fieldPrototypes" "refMethods"      "refSuperClasses"
 [5] "slots"           "contains"        "virtual"         "prototype"      
 [9] "validity"        "access"          "className"       "package"        
[13] "subclasses"      "versionKey"      "sealed"          "class"  

Кажется, я не могу найти атрибут, в котором хранится фактический исходный код/выражение, которое точно определяет класс.

Просто чтобы убедиться: я хочу получить доступ к этому выражению

setRefClass(Class="MyClass", fields=list(x.1="character"))

Фон/мотивация

Я много работаю с эталонными классами S4 (?setRefClass) и, следовательно, с аспектами ООП, такими как наследование классов играет большую роль в моей повседневной работе. Я также придерживаюсь парадигмы «одно определение на файл», чтобы все было организовано, поэтому различные определения классов хранятся в отдельных файлах, где имена файлов соответствуют именам. соответствующих классов.

Как и у всего в жизни, у такого подхода есть некоторые преимущества, но также и некоторые присущие ему недостатки:

Аспект 1

Кратко или долго вы получите структуру наследования, которая больше не соответствует алфавитному порядку файлов отдельных исходных файлов. Таким образом, простой поиск одного файла за другим приведет к ошибке в определенной точке, где некоторый требуемый суперкласс еще не был получен.

dir.create("classes", showWarnings=FALSE)
write("setRefClass(Class=\"A\", contains=\"B\", fields=list(x.3=\"logical\"))", 
    file="classes/class_A.R")
write("setRefClass(Class=\"B\", contains=\"C\", fields=list(x.2=\"numeric\"))", 
    file="classes/class_B.R")
write("setRefClass(Class=\"C\", fields=list(x.1=\"character\"))", 
    file="classes/class_C.R")

class_A.R — это первый файл в папке classes, но для того, чтобы получить его, нам сначала нужно получить class_B.R (поскольку этот файл определяет класс B), который, в свою очередь, требует класса C и, следовательно, предварительного источника class_C.R.

Таким образом, правильное сопоставление:

c("class_C.R", "class_B.R", "class_A.R")

Аспект 2

Для определенных задач вам нужна парадигма «несколько определений на файл»: быстрое и простое распределение необходимых объектов/функций/классов по рабочим процессам при распараллеливании, организация кода при сборке пакета и т. д.

path <- "classes/classes.R"
file.create(path)
write("setRefClass(Class=\"C\", fields=list(x.1=\"character\"))", 
    file=path, append=TRUE)
write("setRefClass(Class=\"B\", contains=\"C\", fields=list(x.2=\"numeric\"))", 
    file=path, append=TRUE)
write("setRefClass(Class=\"A\", contains=\"B\", fields=list(x.3=\"logical\"))", 
    file=path, append=TRUE)

Аспект объявления 1

Мне не нравится идея хранить какую-то ручную ссылку на сортировку, которая указывает правильный порядок поиска, поскольку я думаю, что это работа, которую компьютер может легко сделать за меня (выявление правильной сортировки). Единственное, что вам нужно сделать здесь, это выяснить суперклассы каждого класса (своего рода его зависимости), а затем получить правильное сопоставление — это кусок пирога.

ИЗМЕНИТЬ

В случае, если кому-то интересно: я придумал для этого рабочий подход. Просто напишите мне, если хотите увидеть код. Он основан на анализе (без оценки) соответствующих исходных файлов определения класса, чтобы исследовать значение аргумента contains, в котором перечислены суперклассы. Затем весь процесс рекурсивно повторяется для исходных файлов этих суперклассов, пока вы в конечном итоге не получите правильную сортировку. Это не так уж и много времени.

Вот план:

x <- list.files("classes", full.names=TRUE)    
code <- base::parse(file=x[1])

> code 
expression(setRefClass(Class="A", contains="B", fields=list(x.3="logical")))

superclasses <- eval(code[[1]][["contains"]])
> superclasses
[1] "B"

# Next: investigate source file for class 'B'

Аспект объявления 2

Мне также не нравится ручное копирование и вставка, поэтому я реализовал процедуру, которая позволяет мне консолидировать исходный код, хранящийся в отдельных файлах или извлеченный из соответствующих объектов, в один «консолидированный» файл (через deparse(<source_code_expression>) и write(..., append=TRUE)). Что касается классов, то здесь также важна правильная сортировка, иначе при попытке получения консолидированного файла снова возникнут ошибки.

Для обоих аспектов было бы неплохо иметь возможность выбирать, как добраться до фактического исходного кода/выражения для классов/функций/методов:

  1. либо на основе исследования кода, хранящегося в соответствующих исходных файлах (parse(file=*)),
  2. или на основе доступа к нужной информации непосредственно из соответствующего объекта.

Вторым вариантом будет ссылка на фактический вопрос выше.


person Community    schedule 15.08.2012    source источник
comment
Для объявления 1) как программное обеспечение может узнать правильное сопоставление без предварительной оценки определения класса, которое требует правильного сопоставления? Если вы хотите преодолеть это, то решение, вероятно, состоит в том, чтобы создать пакет (в конце концов, не так уж сложно) с полем Collate:.   -  person Martin Morgan    schedule 15.08.2012
comment
Ну, просто проанализировав (без оценки) соответствующие определения классов, исследуя значение аргумента contains и рекурсивно выполняя весь процесс снова для встреченных суперклассов, пока вы не получите вектор, который имеет право приказ. Ручное указание поля Collate: — это именно то, чего я хочу избежать. Я согласен, что обычно хорошо быть явным, но для быстрого прототипирования с 30-50 классами, где вы исследуете и, таким образом, многое меняете, я думаю, что это работа, которую компьютер может сделать для меня.   -  person Rappster    schedule 15.08.2012
comment
base="foo"; setRefClass(base); setRefClass("bar", contains=base) требует оценки, верно?   -  person Martin Morgan    schedule 15.08.2012
comment
Верно, но это не мой вариант использования: определения классов хранятся в файлах, поэтому я анализирую содержимое каждого файла, и для работы с проанализированным выражением не требуется оценка. См. структуру моих эталонных классов (contains="some string", а не contains=some.object) и мою EDIT в аспекте рекламы 1.   -  person Rappster    schedule 15.08.2012


Ответы (2)


«Исходный» код команды не сохраняется, поэтому вы не увидите его при проверке объекта.

Взгляните на источник setRefClass, введя его в консоли и нажав [ENTER]. Обратите внимание, что вы просто передали аргументы функции... не определили новое выражение. Итак, когда вы getRefClass, вы получаете все, что класс знает о себе.

Вы можете перестроить его, создав функцию parseRefClassArgs, которая перестраивает аргументы для setRefClass.

person Thell    schedule 15.08.2012
comment
Но разве это не верно для setMethod или подобных? Тем не менее, вы можете получить информацию об источнике. Может быть, можно настроить функцию ?setRefClass, чтобы она сразу включала эту информацию? - person Rappster; 15.08.2012
comment
Да и нет, setMethod похож на setRefClass в том, что: Да, setMethod просто принимает аргументы, такие как setRefClass; но нет, потому что setMethod на самом деле хранит srcref (дополнительную информацию см. в ?srcref и Duncan Murdoch. Source References. The R Journal, 2(2):16-19, декабрь 2010 г.). Так что... да, настройка может позволить вам сохранить его; хотя потребуется некоторая охота, чтобы выяснить, где; точно так же, как с определением public/private в ReferenceClasses. Это можно сделать, но это не будет чисто. ;) - person Thell; 16.08.2012
comment
Спасибо за указатели. Будет отличный проект «дождливый воскресный день», чтобы копнуть немного глубже ;-) - person Rappster; 16.08.2012

Определения классов

Поскольку мы не можем оценить файлы (поскольку мы не знаем порядок сортировки), source или изучение определенных классов не рассматривается. Здесь мы анализируем текст в каждом файле в вектор символов, сохраняя проанализированные строки, начинающиеся с «setRefClass». Синтаксический анализатор удаляет пробелы и выполняет другие преобразования, чтобы сделать текст более однородно отформатированным, хотя последующее будет опираться на согласованное определение класса (например, с использованием именованных аргументов).

fls <- dir()
names(fls) <- fls
p0 <- lapply(fls, function(fl) as.character(parse(text=readLines(fl))))
p1 <- lapply(p0, grep, pattern="^setRefClass", value=TRUE)

Давайте нацелимся на data.frame, содержащий определяемый класс, содержащийся класс и имя файла, в котором происходит определение класса.

df <- data.frame(Class=NA_character_, contains=NA_character_,
                 File=rep(fls, sapply(p1, length)),
                 stringsAsFactors=FALSE)

а затем заполните его, используя сопоставление с образцом/подстановку

p2 <- unlist(p1, use.names=FALSE)
pat <- '.*Class = "([[:alnum:]]+)".*'
df[,"Class"] <- sub(pat, "\\1", p2)

pat <- '.*contains = "([[:alnum:]]+)".*'
idx <- grep(pat, p2)
df[idx,"contains"] <- sub(pat, "\\1", p2[idx])

Окончание на (я добавил класс A1, также производный от B, в файл class_A.R, для развлечения)

> df
  Class contains      File
1     A        B class_A.R
2    A1        B class_A.R
3     B        C class_B.R
4     C     <NA> class_C.R

Еще одна стратегия сбора фрейма данных Class / contains заключается в перехвате вызовов setRefClass.

gClass <- character()
gcontains <- character()
setRefClass <- function(Class, fields, contains=NA_character_, ...)
{
    gClass <<- c(gClass, Class)
    gcontains <<- c(gcontains, contains)
}

gClass и gcontains будут содержать соответствующие данные для построения графа зависимостей после получения соответствующих файлов (при условии, что файлы могут быть получены без доступных определений классов).

for (fl in dir()) source(fl)

Зависимости

Что нам нужно, так это граф зависимостей для классов, у которых есть зависимости. Поэтому мы будем использовать граф и RBGL пакеты из Биопроводник для построения соответствующего графика

## From --> To == contains --> Class
m <- as.matrix(df[!is.na(df$contains), c("contains", "Class")])
gr <- ftM2graphNEL(m, edgemode="directed")

затем запросите поиск в ширину, начиная с каждого из наших базовых пакетов (df[is.na(df$contains), "Class"]), и используйте полученный порядок для получения соответствующего порядка сопоставления.

o <- bfs(gr, "C")                       # order: breadth-first search
unique(df[match(o, df$Class), "File"])

So

classDefFilesCollateOrder <- function(fls)
{
    names(fls) <- fls
    p0 <- lapply(fls, function(fl) as.character(parse(text=readLines(fl))))
    p1 <- lapply(p0, grep, pattern="^setRefClass", value=TRUE)

    df <- data.frame(Class=NA_character_, contains=NA_character_,
                     File=rep(fls, sapply(p1, length)),
                     stringsAsFactors=FALSE)

    p2 <- unlist(p1, use.names=FALSE)
    pat <- '.*Class = "([[:alnum:]]+)".*'
    df[,"Class"] <- sub(pat, "\\1", p2)

    pat <- '.*contains = "([[:alnum:]]+)".*'
    idx <- grep(pat, p2)
    df[idx, "contains"] <- sub(pat, "\\1", p2[idx])

    ## From --> To == contains --> Class
    m <- as.matrix(df[!is.na(df$contains), c("contains", "Class")])
    gr <- ftM2graphNEL(m, edgemode="directed")

    ## single class only
    base <- df$Class[is.na(df$contains)]
    if (length(base) != 1)
        stop("don't yet know how to deal with more than 1 base class")
    o <- bfs(gr, base)

    unique(df[match(o, df$Class), "File"])
}
person Martin Morgan    schedule 15.08.2012
comment
Хороший! Хотя после первого быстрого сканирования ваш ответ, похоже, в основном касается справочной/мотивационной части моего вопроса (как получить график зависимостей или как автоматически получить правильную сортировку), но не как/можно ли получить доступ к исходной части определение класса из его объекта (так что после определение класса было получено; при условии, что правильное сопоставление было либо явно указано, либо получено с помощью подхода, такого как ваш или мой) - person Rappster; 16.08.2012