Как может не импортированный метод в ненастроенном пакете быть вызван вызовами функций, не имеющих его в своем пространстве имен?
Пространство имен R действует как непосредственная среда для всех функций в соответствующем пакете. Другими словами, когда функция bar()
из пакета foo вызывает другую функцию, оценщик R сначала ищет другую функцию в <environment: namespace:foo>
, затем в "imports.foo"
, <environment: namespace:base>
, <environment: R_GlobalEnv>
, и т.д. вниз по списку поиска, возвращаемому путем ввода search()
.
Один приятный аспект пространств имен заключается в том, что они могут делать пакеты лучше, чем лучшие граждане: недоступные функции в <environment: namespace:foo>
и функции в imports:foo
доступны только: (a) для функций в foo; (b) другим пакетам, импортирующим из foo; или (c) с помощью полнофункциональных вызовов функций, таких как foo:::bar()
.
Или так я думал до недавнего времени...
Поведение
Этот недавний вопрос SO выделил случай, когда функция, хорошо скрытая в пространстве имен пакетов, тем не менее была найдена вызовом, казалось бы, несвязанной функции:
group <- c("C","F","D","B","A","E")
num <- c(12,11,7,7,2,1)
data <- data.frame(group,num)
## Evaluated **before** attaching 'gmodels' package
T1 <- transform(data, group = reorder(group,-num))
## Evaluated **after** attaching 'gmodels
library(gmodels)
T2 <- transform(data, group = reorder(group,-num))
identical(T1, T2)
# [1] FALSE
Его непосредственная причина
@Andrie ответил на исходный вопрос, указав, что gmodels импортирует из пакета gdatastrong > , который включает в себя функцию reorder.factor
, которая отправляется во второй вызов до transform()
. T1
отличается от T2
, потому что первый вычисляется через stats:::reorder.default()
, а второй - gdata:::reorder.factor()
.
Мой вопрос
Как получилось, что в приведенном выше вызове transform(data, group=reorder(...))
механизм диспетчеризации для reorder
находит, а затем отправляется на gdata:::reorder.factor()
?
(Ответ должен включать объяснение правил определения области видимости, которые приводят к вызову с участием функций в пакетах stats и base, к видимому хорошо скрытому методу в GDataSTRONG > ).
Дополнительные полезные сведения
-
Ни gdata:::reorder.factor
, ни пакет gdatastrong > в целом явно не импортируются gmodels. Вот директивы import*
в файле gmodels ' NAMESPACE:
importFrom(MASS, ginv)
importFrom(gdata, frameApply)
importFrom(gdata, nobs)
-
Нет методов для reorder()
или transform()
в <environment: namespace:gmodels>
, а не в "imports:gmodels"
:
ls(getNamespace("gmodels"))
ls(parent.env(getNamespace("gmodels")))
-
Отсоединение gmodels не возвращает reorder()
поведение: gdata:::reorder.factor()
по-прежнему отправляется:
detach("package:gmodels")
T3 <- transform(data, group=reorder(group,-num))
identical(T3, T2)
# [1] TRUE
-
reorder.factor()
не сохраняется в списке методов S3 в базовой среде:
grep("reorder", ls(.__S3MethodsTable__.))
# integer(0)
Темы
R chat за последние пару дней включают некоторые дополнительные идеи. Спасибо Andrie, Брайан Диггс и Гэвин Симпсон, которые (с другими) должны отредактировать или добавить возможно impt. подробности на этот вопрос.
Ответы
Ответ 1
Я не уверен, правильно ли я понял ваш вопрос, но главное, что group
- это вектор символов, а data$group
- фактор.
После присоединения gmodels
вызов reorder(factor)
вызывает gdata:::reorder.factor
.
поэтому reorder(factor(group))
вызывает его.
В transform
функция оценивается в среде первого аргумента, поэтому в T2 <- transform(data, group = reorder(group,-num))
, group
является фактором.
ОБНОВЛЕНО
library
прикрепляет пакеты импорта к загруженному пространству имен.
> loadedNamespaces()
[1] "RCurl" "base" "datasets" "devtools" "grDevices" "graphics" "methods"
[8] "stats" "tools" "utils"
> library(gmodels) # here, namespace:gdata is loaded
> loadedNamespaces()
[1] "MASS" "RCurl" "base" "datasets" "devtools" "gdata" "gmodels"
[8] "grDevices" "graphics" "gtools" "methods" "stats" "tools" "utils"
На всякий случай, в namespace:stats
существует общий reorder
:
> r <- ls(.__S3MethodsTable__., envir = asNamespace("stats"))
> r[grep("reorder", r)]
[1] "reorder" "reorder.default" "reorder.dendrogram"
И для более подробной информации
Вызов reorder
будет искать S3generics в двух envs:
см. ?UseMethod
сначала в среде, в которой вызывается общая функция, а затем в базе данных регистрации для среды, в которой определяется общий тип (обычно это пространство имен).
тогда loadNamespace
регистрирует функции S3 в пространстве имен.
Итак, в вашем случае library(gmodels)
→ loadNamespace(gdata)
→ registerS3Methods(gdata)
.
После этого вы можете найти его по:
> methods(reorder)
[1] reorder.default* reorder.dendrogram* reorder.factor*
Non-visible functions are asterisked
Однако, поскольку reorder.factor
не указан в вашем пути поиска, вы не можете получить к нему доступ напрямую:
> reorder.factor
Error: object 'reorder.factor' not found
Возможно, это целый сценарий.