R: распутывание объемов

Мой вопрос о предотвращении загрязнения пространства имен при записи модулей в R.

Прямо сейчас, в моем проекте R, я имею functions1.R с doFoo() и doBar(), functions2.R с другими функциями, и main.R с основной программой в нем, которая сначала делает source('functions1.R'); source('functions2.R'), и затем вызывает другие функции.

Я запускал программу от GUI R в Mac OS X, с source('main.R'). Это прекрасно в первый раз, но после этого, переменные, которые были определены в первый раз через программу, определяются во второй раз functions*.R получены, и таким образом, функции получают целый набор дополнительных определенных переменных.

Я не хочу это! Я хочу ошибку "неопределенной переменной", когда моя функция использует переменную, она не была должна! Дважды это дало мне очень поздно ночи отладки!

Таким образом, как другие люди имеют дело с этим видом проблемы? Есть ли что-то как source(), но это делает независимое пространство имен, которое не падает до основного? Создание пакета походит на одно решение, но на большую боль в торце по сравнению с, например, Python, походит, где исходный файл является автоматически отдельным пространством имен.

Какие-либо подсказки?Спасибо!

5
задан rescdsk 14 April 2010 в 02:41
поделиться

4 ответа

Основная функция, которую вы хотите использовать, - это sys.source () , которая загрузит ваши функции / переменные в пространство имен (" environment "в R) кроме глобального. Еще одна замечательная вещь, которую вы можете сделать в R, - это присоединить пространства имен к вашему пути search () , чтобы вам не нужно было напрямую ссылаться на пространство имен. То есть, если «namespace1» находится на вашем пути поиска, функцию в нем, скажем «fun1», не нужно вызывать как namespace1.fun1 () , как в Python, но как fun1 ( ) .[Порядок разрешения методов:] Если имеется много функций с одинаковыми именами, будет вызвана функция в среде, которая появляется первой в списке search () . Чтобы вызвать функцию в определенном пространстве имен явно, один из многих возможных синтаксисов - хотя и немного уродливый - это get ("fun1", "namespace1") (...) где ... - аргументы fun1 () . Это также должно работать с переменными, используя синтаксис get ("var1", "namespace1") . Я делаю это все время (обычно я загружаю только функции, но различие между функциями и переменными в R невелико), поэтому я написал несколько вспомогательных функций, которые загружаются из моего ~ / .Rprofile .

  name.to.env <- function(env.name)
    ## returns named environment on search() path
    pos.to.env(grep(env.name,search()))

  attach.env <- function(env.name)
    ## creates and attaches environment to search path if it doesn't already exist
    if( all(regexpr(env.name,search())<0) ) attach(NULL,name=env.name,pos=2)

  populate.env <- function(env.name,path,...) {
    ## populates environment with functions in file or directory
    ## creates and attaches named environment to search() path 
    ##        if it doesn't already exist
    attach.env(env.name)
    if( file.info(path[1])$isdir )
      lapply(list.files(path,full.names=TRUE,...),
             sys.source,name.to.env(env.name)) else
    lapply(path,sys.source,name.to.env(env.name))
    invisible()
  }

Пример использования:

populate.env("fun1","pathtofile/functions1.R")
populate.env("fun2","pathtofile/functions2.R")

и так далее, что создаст два отдельных пространства имен: «fun1» и «fun2», которые будут прикреплены к пути search () («fun2» будет выше в списке search () в данном случае). Это похоже на выполнение чего-то вроде

attach(NULL,name="fun1")
sys.source("pathtofile/functions1.R",pos.to.env(2))

вручную для каждого файла ("2" - позиция по умолчанию в пути search () ). Способ записи populate.env () : если каталог, скажем «functions /», содержит много файлов R без конфликтующих имен функций, вы можете вызвать его как

populate.env("myfunctions","functions/")

, чтобы загрузить все функции (и переменные) в единое пространство имен. С помощью name.to.env () вы также можете сделать что-то вроде

with(name.to.env("fun1"), doStuff(var1))

или

evalq(doStuff(var1), name.to.env("fun1"))

. Конечно, если ваш проект разрастается и у вас много-много функций (и переменных), напишите пакет - это то, что нужно.

4
ответ дан 18 December 2019 в 11:54
поделиться

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

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

3
ответ дан 18 December 2019 в 11:54
поделиться

Я бы рассмотрел два возможных решения этой проблемы.

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

b) Наведите порядок вручную:

#main.R
prior_variables <- ls()
source('functions1.R')
source('functions2.R')

#stuff happens

rm(list = setdiff(ls(),prior_variables))`
5
ответ дан 18 December 2019 в 11:54
поделиться

Чтобы избежать загрязнения пространства имен, как вы выразились, нужно всего лишь тщательно разбить пространство имен и сохранить незагроможденное глобальное пространство имен.

Вот основные функции для этих двух типов задач:

Понимание / навигация по структуре пространства имен

При запуске R создает новую среду для хранения всех объектов, созданных во время этого сеанса - это «глобальная среда».

# to get the name of that environment:
globalenv()

Но это не корневая среда. Корень - это среда, называемая «пустая среда» - все среды связаны с ней:

emptyenv()
returns: <environment: R_EmptyEnv>

# to view all of the chained parent environments (which includes '.GlobalEnv'):
search()

Создание новых сред:

workspace1 = new.env()

is.environment(workspace1)
returns: [1] TRUE

class(workspace1)
returns: [1] "environment"

# add an object to this new environment:
with(workspace1, attach(what="/Users/doug/Documents/test_obj.RData",
     name=deparse(substitute(what)), warn.conflicts=T, pos=2))

# verify that it's there:
exists("test_obj", where=workspace1)
returns: [1] TRUE

# to locate the new environment (if it's not visible from your current environment)
parent.env(workspace1)
returns: <environment: R_GlobalEnv>

objects(".GlobalEnv")
returns: [1] "test_obj"

Исходя из python и др., Эта система (сначала) казалась мне комнатой полный карнавальных зеркал. С другой стороны, R Gurus, похоже, вполне с этим справляется. Я уверен, что этому есть ряд причин, но моя интуиция подсказывает, что они не позволяют окружающей среде сохраняться. Я заметил, что новички в R используют "прикрепить", как в "attach (" this_dataframe "); Я заметил, что опытные пользователи R этого не делают; вместо этого они используют «с», например

with(this_dataframe, tapply(etc....))

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

3
ответ дан 18 December 2019 в 11:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: