Почему / как некоторые пакеты определяют свои функции в безымянной среде? - PullRequest
3 голосов
/ 20 февраля 2020

В моем коде мне нужно было проверить, из какого пакета определена функция (в моем случае это было exprs(): мне нужно было из Biobase, но оказалось, что оно переопределено rlang).
Из этого ТАКОГО вопроса , я думал, что смогу просто использовать environmentName(environment(functionname)). Но для exprs из Biobase это выражение вернуло пустую строку:

environmentName(environment(exprs))
# [1] ""

После проверки структуры environment(exprs) я заметил, что в нем есть .Generic член, который содержит имя пакета в качестве атрибута:

environment(exprs)$.Generic
# [1] "exprs"
# attr(,"package")
# [1] "Biobase"

Итак, пока я сделал эту вспомогательную функцию:

pkgparent <- function(functionObj) {
  functionEnv <- environment(functionObj)
  envName <- environmentName(functionEnv)
  if (envName!="") 
    return(envName) else
      return(attr(functionEnv$.Generic,'package'))
}

Она выполняет работу и правильно возвращает имя пакета для функции, если она загружена, например:

pkgparent(exprs)
# Error in environment(functionObj) : object 'exprs' not found

library(Biobase)

pkgparent(exprs)
# [1] "Biobase"

library(rlang)
# The following object is masked from ‘package:Biobase’:
#   exprs

pkgparent(exprs)
# [1] "rlang"

Но я все же хотел бы узнать, как получается, что для некоторых пакетов их функции определены в «неназванной» среде, в то время как другие будут выглядеть как <environment: namespace:packagename>.

1 Ответ

1 голос
/ 20 февраля 2020

То, что вы видите здесь, является частью того, как работает диспетчеризация метода S4. Фактически, .Generic является частью механизма отправки метода R .

Кстати, пакет rlang - это красная сельдь: проблема возникает исключительно из-за использования S4 компанией Biobase .

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

Например, обычно невозможно изменить переменные, определенные внутри пакета на уровне пространства имен, поскольку пространство имен блокируется при загрузке. Есть несколько способов обойти это. Простой способ, если пакету нужна функция с состоянием, - определить эту функцию внутри среды. Например, вы можете определить функцию counter, которая увеличивает ее счет при каждом вызове, следующим образом:

counter = local({
    current = 0L

    function () {
        current <<- current + 1L
        current
    }
})

local определяет среду, в которую обернута функция.

Кому чтобы справиться с такой ситуацией, вместо этого вам следует перебирать родительские среды, пока не найдете среду пространства имен. Но есть более простое решение, потому что R уже предоставляет функцию для поиска среды пространства имен для данной среды (путем выполнения указанной итерации):

pkgparent = function (fun) {
    nsenv = topenv(environment(fun))
    environmentName(nsenv)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...