Ответ 1
Во-первых, как сказал @Spacedman, вам будет лучше всего подан пакет, но есть и другие варианты.
Методы S3
R оригинальная "объектная ориентация" называется S3. Большая часть базы кода R использует эту конкретную парадигму. Это то, что делает работу plot()
для всех видов объектов. plot()
является общей функцией, а команда R Core Team и разработчики пакетов могут и написали свои собственные методы для plot()
. Строго эти методы могут иметь имена типа plot.foo()
, где foo
- это класс объекта, для которого функция определяет метод plot()
. Красота S3 заключается в том, что вам (вряд ли) когда-либо нужно знать или вызывать plot.foo()
, вы просто используете plot(bar)
, а R выдает метод plot()
для отправки на основе класса объекта bar
.
В ваших комментариях к вашему Вопросу вы упомянули, что у вас есть функция populate()
, которая имеет методы (по существу) для классов "crossvalidate"
и "prod"
, которые вы храните в отдельных файлах .r
. Способ S3 для этого:
populate <- function(x, ...) { ## add whatever args you want/need
UseMethod("populate")
}
populate.crossvalidate <-
function(x, y, z, ...) { ## add args but must those of generic
## function code here
}
populate.prod <-
function(x, y, z, ...) { ## add args but must have those of generic
## function code here
}
Данный некоторый объект bar
с классом "prod"
, вызывающий
populate(bar)
приведет к вызову R, вызывающему populate()
(общий), затем он ищет функцию с именем populate.prod
, потому что это класс bar
. Он находит наш populate.prod()
и так отправляет, что функция передает ему аргументы, которые мы изначально указали.
Итак, вы видите, что вы ссылаетесь только на методы, используя имя общего, а не полное имя функции. R выдает вам, какой метод нужно вызывать.
Два метода populate()
могут иметь очень разные аргументы, за исключением того, что строго они должны иметь те же аргументы, что и общая функция. Поэтому в приведенном выше примере все методы должны иметь аргументы x
и ...
. (Существует исключение для методов, которые используют объекты формулы, но здесь не нужно беспокоиться об этом.)
Пространства имен пакетов
Так как R 2.14.0, все пакеты R имели собственное пространство имен, даже если один из них не был предоставлен автором пакета, хотя пространства имен были вокруг намного дольше в R, чем это.
В вашем примере мы хотим зарегистрировать общий набор populate()
и два метода с системой S3. Мы также хотим экспортировать общую функцию. Обычно мы не хотим или не должны экспортировать отдельные методы. Итак, поместите свои функции в файлы .r
в папке R
источников пакетов, а затем на верхнем уровне источников пакетов создайте файл с именем NAMESPACE
и добавьте следующие инструкции:
export(populate) ## export generic
S3method(populate, crossvalidate) ## register methods
S3method(populate, prod)
Затем, как только вы установили свой пакет, вы заметите, что вы можете вызвать populate()
, но R будет жаловаться, если вы попытаетесь вызвать populate.prod()
и т.д. напрямую по имени из приглашения или другой функции. Это связано с тем, что функции, которые являются отдельными методами, не были экспортированы из пространства имен, а оттуда не видны вне его. Любая функция в вашем пакете, вызывающая populate()
, сможет получить доступ к указанным вами методам, но любые функции или код вне вашего пакета вообще не могут видеть методы. Если вы хотите, вы можете вызывать неэкспортируемые функции с помощью оператора :::
, т.е.
mypkg:::populate.crossvalidate(foo, bar)
будет работать, где mypkg
- это имя вашего пакета.
Честно говоря, вам даже не нужен файл NAMESPACE
, так как R будет автоматически генерировать его при установке пакета, который автоматически экспортирует все функции. Таким образом, ваши два метода будут видны как populate.xxx()
(где xxx
является конкретным методом) и будут работать как методы S3.
Прочитайте Раздел 1 Создание R-пакетов в руководстве по написанию R-расширений для получения подробной информации о том, что происходит, но yuo не потребуется делать половину этого, если вы Также не хочу, особенно если пакет предназначен для вашего собственного использования. Просто создайте соответствующие папки папок (т.е. R
и man
), вставьте файлы .r
в R
. Напишите один .Rd
файл в man
, где вы добавляете
\name{Misc Functions}
\alias{populate}
\alias{populate.crossvalidate}
\alias{populate.prod}
в верхней части файла. Добавьте \alias{}
для любых других функций, которые у вас есть. Затем вам нужно будет создать и установить пакет.
Альтернативное использование sys.source()
Хотя я не могу (не могу!) действительно рекомендовать то, что я упоминаю ниже, как долгосрочный жизнеспособный вариант здесь, есть альтернатива, которая позволит вам изолировать функции от отдельных файлов .r
, поскольку вы изначально просил. Это достигается за счет использования окружений, а не пространств имен, и не требует создания пакета.
Функция sys.source()
может использоваться для источника R-кода/функций из файла .r
и оценивать его в среде. Поскольку вы .r
файл создаете/определяете функции, если вы отправляете его в другую среду, тогда эти функции будут определены там, в этой среде. Они не будут отображаться на стандартном пути поиска по умолчанию, и, следовательно, функция populate()
, определенная в crossvalidate.R
, не будет сталкиваться с populate()
, определенной в prod.R
, если вы используете две отдельные среды. Когда вам нужно использовать один набор функций, вы можете назначить среду пути поиска, после чего она будет чудесным образом видна всем, и когда вы закончите, вы сможете отделить ее. Прикрепите другую среду, используйте ее, отсоедините и т.д. Или вы можете организовать, чтобы R-код оценивался в определенной среде, используя такие вещи, как eval()
.
Как я уже сказал, это не рекомендуемое решение, но оно будет работать по моде, как вы описываете. Например
## two source files that both define the same function
writeLines("populate <- function(x) 1:10", con = "crossvalidate.R")
writeLines("populate <- function(x) letters[1:10]", con = "prod.R")
## create two environments
crossvalidate <- new.env()
prod <- new.env()
## source the .R files into their respective environments
sys.source("crossvalidate.R", envir = crossvalidate)
sys.source("prod.R", envir = prod)
## show that there are no populates find-able on the search path
> ls()
[1] "crossvalidate" "prod"
> find("populate")
character(0)
Теперь присоедините одну из сред и вызовите populate()
:
> attach(crossvalidate)
> populate()
[1] 1 2 3 4 5 6 7 8 9 10
> detach(crossvalidate)
Теперь вызовите функцию в другой среде
> attach(prod)
> populate()
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"
> detach(prod)
Ясно, что каждый раз, когда вы хотите использовать определенную функцию, вам нужно attach()
ее среду, а затем вызвать ее, а затем вызов detach()
. Это боль.
Я сказал, что вы можете организовать для R-кода (выражения действительно) для оценки в заявленной среде. Вы можете использовать eval()
для with()
для этого, например.
> with(crossvalidate, populate())
[1] 1 2 3 4 5 6 7 8 9 10
По крайней мере, теперь вам нужен только один вызов для запуска версии populate()
по вашему выбору. Однако, если вы вызываете функции по их полному имени, например. populate.crossvalidate()
- это слишком много усилий (согласно вашим комментариям), то осмелюсь сказать, что даже идея with()
будет слишком сложной? И вообще, почему бы вам использовать это, когда у вас вполне может быть свой собственный пакет R.