Структура файлов и каталогов проекта r
В распространенных языках программирования, таких как java, каждый файл обычно соответствует классу.
Я только что начал с R. Я хотел бы построить небольшую программу, и я хотел бы создать определенную структуру файлов и каталогов, как этоMain.R # the main control script MyClass.R # A class that is referenced from within Main.R ProcessData.R # Another class that uses an object of MyClass.R as input
Поэтому я хотел бы сделать что-то вроде этого (псевдокод):
Главная.R
myc <- new MyClass # create a new instance of MyClass from within Main.R pd <- new ProcessData pd$processMyClass( myc ) # call a method in ProcessData that processes the myc object in some way
Так что это довольно абстрактно, но я просто хотел узнать, возможно ли это в принципе в R.
Обновление: мне нужно получить более конкретный. Поэтому возникает вопрос: как бы вы перевели следующую java-программу в программу R, сохранив такое же количество файлов и структуру следующей игрушечной программы?
Главная.java:
public static void main( String[] args ) {
MyClass myc = new MyClass("SampleWord");
ProcessData pd = new ProcessData();
pd.processData( myc );
}
Микласс.java
class MyClass {
public String word;
public MyClass( String word ) {
this.word = word;
}
}
ProcessData.java
class ProcessData.java {
public void processData( MyClass myc ) {
System.out.println( "pd.processData = " + myc.word );
}
}
2 ответа:
Ссылочные классы ниже мы попытаемся воспроизвести код java в вопросе, используя R настолько близко, насколько это возможно. В этом отношении из трех встроенных в R-класс систем (S3, S4, Reference Classes) референсные классы кажутся наиболее близкими к этому стилю. Reference Classes - это самая последняя система классов, добавленная в R, и ее быстрое освоение может быть связано с тем, что в R приходят Java-программисты, знакомые с этим стилем.
(Если вы создаете пакет из этого, то опустите все исходные заявления.)
Главная.Файл R:
source("MyClass.R") source("ProcessData.R") main <- function() { myc <- new("MyClass", word = "SampleWord") pd <- new("ProcessData") cat("pd$processData =", pd$processData(myc), "\n") }Микласс.Файл R:
setRefClass("MyClass", fields = list(word = "character") )ProcessData.Файл R:
setRefClass("ProcessData", fields = list(myc = "MyClass"), methods = list( processData = function(myc) myc$word ) )Для запуска:
source("Main.R") main()Proto package пакетproto package реализует прототипную модель объектно-ориентированного программирования, которая возникла из языка самопрограммирования и существует в некоторой степени в javascript, Lua и, в частности, является основой языкаio . прото может легко подражать этому стилю (как обсуждалось в разделе черт протовиньетки):
Главная.Файл R:
source("MyClass.R") source("ProcessData.R") library(proto) main <- function() { myc <- MyClass$new("SampleWord") pd <- ProcessData$new() cat("pd$processData =", pd$processData(myc), "\n") }Микласс.Файл R:
MyClass <- proto( new = function(., word) proto(word = word) )ProcessData.Файл R:
ProcessData <- proto( new = function(.) proto(.), processData = function(., myc) myc$word )Для запуска:
source("Main.R") main()Обновление: добавлен пример proto.
Обновление 2: улучшено
mainиMyClassв Примере ссылочного класса.
Системы
Проверьте три системы классов в R, S3, S4 и ссылочных классах.
## S3 methods, Section 5 of RShowDoc("R-lang") ## S4 classes ?Classes ?Methods ## Reference classes ?ReferenceClassesС фоном Java вы будете испытывать искушение пойти с ссылочными классами, но они имеют "ссылочную семантику" и действие на расстоянии (изменение одного объекта изменяет другой, который ссылается на те же данные), в то время как большинство пользователей R ожидают семантику "копировать при изменении". Можно сделать большой прогресс с классами S3, но более дисциплинированный подход, на мой взгляд, принял бы S4. Особенности S4 удивит вас, отчасти потому, что система классов ближе к общей объектной системе lisp, чем к java.
Есть другие мнения и варианты.
Базовая реализация
Я не совсем уверен, какова ваша цель проектирования с помощью ProcessData; я бы реализовал ваши два класса как класс, универсальный и метод для универсального, который работает с классом MyClass.
## definition and 'low-level' constructor .MyClass <- setClass("MyClass", representation(word="character")) ## definition of a generic setGeneric("processData", function(x, ...) standardGeneric("processData")) setMethod("processData", "MyClass", function(x, ...) { cat("processData(MyClass) =", x@word, "\n") })Это полное и полностью функциональное
> myClass <- .MyClass(word="hello world") > processData(myClass) processData(MyClass) = hello worldТрое строки кода могут быть помещены в два файла, " AllGenerics.Р " и " Майкласс.R "(включая метод) или три файла " AllGenerics.R", " Все Классы.R", " processData-методы.R "(обратите внимание, что методы связаны с универсальными, и отправка по классу).
Дополнительная реализация
Обычно добавляется более удобный конструктор, например, предоставляющий пользователю подсказки об ожидаемых типах данных или выполняющий сложные шаги инициализации аргумента
MyClass <- function(word=character(), ...) { .MyClass(word=word, ...) }Обычно один требуется слот доступа, а не прямой слот доступа. Это может быть простая функция (как показано на рисунке) или универсальный метод+.
word <- function(x, ...) x@wordЕсли слот должен быть обновлен, то пишется функция или метод замены. Функция или метод обычно имеет три аргумента: объект для обновления, возможные дополнительные аргументы и значение для обновления объекта. Вот общая реализация метода +
setGeneric("word<-", function(x, ..., value) standardGeneric("word<-")) setReplaceMethod("word", c("MyClass", "character"), function(x, ..., value) { ## note double dispatch on x=MyClass, value=character x@word <- value x })Несколько хитрая альтернативная реализация is
setReplaceMethod("word", c("MyClass", "character"), function(x, ..., value) { initialize(x, word=value) }), который использует универсальный метод
initializeи метод по умолчанию в качестве конструктора копирования; это может быть эффективно, если обновить несколько слотов одновременно.Поскольку класс виден пользователям, его нужно отобразить удобным для пользователя способом с помощью метода "show", для которого уже существует универсальный (
getGeneric("show"))setMethod("show", "MyClass", function(object) { cat("class:", class(object), "\n") cat("word:", word(object), "\n") })И теперь наша пользовательская сессия выглядит как
> myClass class: MyClass word: hello world > word(myClass) [1] "hello world" > word(myClass) <- "goodbye world" > processData(myClass) processData(MyClass) = goodbye worldЭффективность
R эффективно работает на векторах; классы S4 не являются исключением. Так что ... дизайн заключается в том, что каждый слот класса представляет собой столбец, охватывающий несколько строк, а не элемент одной строки. Мы ожидаем, что слот "слово" обычно содержит вектор длины, намного превышающей 1, и для операций, которые будут действовать на все элементы вектора. Поэтому можно было бы написать методы с учетом этого, например, изменив метод show на
setMethod("show", "MyClass", function(object) { cat("class:", class(object), "\n") cat("word() length:", length(word(object)), "\n") })Вот большие объекты данных (используя файлы в моей системе Linux)
> amer <- MyClass(readLines("/usr/share/dict/american-english")) > brit <- MyClass(readLines("/usr/share/dict/british-english")) > amer class: MyClass word() length: 99171 > brit class: MyClass word() length: 99156 > sum(word(amer) %in% word(brit)) [1] 97423 > amer_uc <- amer ## no copy, but marked to be copied if either changed > word(amer_uc) <- toupper(word(amer_uc)) ## two distinct objectsИ все это вполне исполнитель.
Опасности эталонного класса "действие на расстоянии"
Давайте вернемся к более простой реализации класса S4, с прямым доступом к слоту и без причудливых конструкторов. Вот американский словарь и копия, преобразованная в верхний регистр.MyClass <- setClass("MyClass", representation(word="character")) amer <- .MyClass(word=readLines("/usr/share/dict/american-english")) amer_uc <- amer amer_uc@word <- toupper(amer_uc@word)Обратите внимание, что у нас есть верхний корпус
amer_uc, но неamer:> amer@word[99 + 1:10] [1] "Adana" "Adar" "Adar's" "Addams" "Adderley" [6] "Adderley's" "Addie" "Addie's" "Addison" "Adela" > amer_uc@word[99 + 1:10] [1] "ADANA" "ADAR" "ADAR'S" "ADDAMS" "ADDERLEY" [6] "ADDERLEY'S" "ADDIE" "ADDIE'S" "ADDISON" "ADELA"Это действительно то, что ожидают пользователи R - я создал отдельный объект и изменил его; исходный объект не модифицирован. Это утверждение с моей стороны; возможно, я не знаю, чего ожидают пользователи R. Я предполагаю, что пользователь R на самом деле не обращает внимания на то, что это ссылочный класс, но думает, что это просто другой объект R, такой как вектор
integer()илиdata.frameили возвращаемое значениеlm().Напротив, здесь минимальная реализация ссылочного класса и аналогичные операции
Но теперь мы изменили и.MyRefClass <- setRefClass("MyRefClass", fields = list(word="character")) amer <- .MyRefClass(word=readLines("/usr/share/dict/american-english")) amer_uc <- amer amer_uc$word <- toupper(amer_uc$word)amer, иamer_uc! Полностью ожидаемый C или Java программистами, но не R пользователи.> amer$word[99 + 1:10] [1] "ADANA" "ADAR" "ADAR'S" "ADDAMS" "ADDERLEY" [6] "ADDERLEY'S" "ADDIE" "ADDIE'S" "ADDISON" "ADELA" > amer_uc$word[99 + 1:10] [1] "ADANA" "ADAR" "ADAR'S" "ADDAMS" "ADDERLEY" [6] "ADDERLEY'S" "ADDIE" "ADDIE'S" "ADDISON" "ADELA"