Глобальные и локальные переменные в R


126

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

Я прочитал несколько сообщений в Интернете, в которых говорится, использую ли я =или <-назначу переменную в текущей среде, и с помощью <<-я могу получить доступ к глобальной переменной, находясь внутри функции.

Однако, как я помню, в C ++ локальные переменные возникают всякий раз, когда вы объявляете переменную в скобках {}, поэтому мне интересно, то же самое для R? Или это просто для функций в R, что у нас есть концепция локальных переменных.

Я провел небольшой эксперимент, который, кажется, предполагает, что одних скобок недостаточно, я что-то не так понимаю?

{
   x=matrix(1:10,2,5)
}
print(x[2,2])
[1] 4

Некоторый код для запуска в дополнение к этим ответам: globalenv(); globalenv() %>% parent.env; globalenv() %>% parent.env %>% parent.env,…
isomorphismes

@isomorphismes, Error: could not find function "%>%". Это еще одна форма назначения?
Aaron McDaid

1
Соответствующая ветка в R-help: Что означает оператор «<< -»? ,
Henrik

2
@AaronMcDaid Привет, извини, что не ответил раньше! Это из require(magrittr). Это способ применения функций справа ( x | f1 | f2 | f3), а не слева ( f3( f2( f1( x ) ) )).
isomorphismes

Ответы:


153

Переменные, объявленные внутри функции, являются локальными для этой функции. Например:

foo <- function() {
    bar <- 1
}
foo()
bar

дает следующее сообщение об ошибке: Error: object 'bar' not found.

Если вы хотите создать barглобальную переменную, вам следует сделать:

foo <- function() {
    bar <<- 1
}
foo()
bar

В этом случае barдоступен извне функции.

Однако, в отличие от C, C ++ или многих других языков, скобки не определяют объем переменных. Например, в следующем фрагменте кода:

if (x > 10) {
    y <- 0
}
else {
    y <- 1
}

yостается доступным после if-elseутверждения.

Как вы хорошо сказали, вы также можете создавать вложенные среды. Вы можете взглянуть на эти две ссылки, чтобы понять, как их использовать:

  1. http://stat.ethz.ch/R-manual/R-devel/library/base/html/environment.html
  2. http://stat.ethz.ch/R-manual/R-devel/library/base/html/get.html

Вот небольшой пример:

test.env <- new.env()

assign('var', 100, envir=test.env)
# or simply
test.env$var <- 100

get('var') # var cannot be found since it is not defined in this environment
get('var', envir=test.env) # now it can be found

136

<- выполняет присваивание в текущей среде.

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

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

Это может проиллюстрировать, что происходит.

bar <- "global"
foo <- function(){
    bar <- "in foo"
    baz <- function(){
        bar <- "in baz - before <<-"
        bar <<- "in baz - after <<-"
        print(bar)
    }
    print(bar)
    baz()
    print(bar)
}
> bar
[1] "global"
> foo()
[1] "in foo"
[1] "in baz - before <<-"
[1] "in baz - after <<-"
> bar
[1] "global"

В первый раз, когда мы печатаем bar, мы еще не вызвали fooего, поэтому он все равно должен быть глобальным - это имеет смысл. Во второй раз мы печатаем его внутри fooперед вызовом, bazпоэтому значение in foo имеет смысл. Ниже мы видим, что <<-происходит на самом деле. Следующим напечатанным значением будет «in baz - before << -», хотя оператор печати идет после <<-. Это потому, <<-что не смотрит в текущую среду (если вы не находитесь в глобальной среде, и в этом случае <<-действует как <-). Таким образом, внутри bazзначение бара остается как «in baz - before << -». Как только мы вызываем bazкопию бара внутри, меняем fooна «in baz», но, как мы видим, глобальное значение barне меняется.barкоторый определен внутри, fooнаходится в родительской среде, когда мы создавали, bazпоэтому это первая копия, barкоторую <<-видит, и, следовательно, копия, которой она назначается. Так что <<-это не просто прямое присвоение глобальной среде.

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

Теперь я меняю <<-оператор на оператор assign, и мы можем увидеть, какой эффект это имеет:

bar <- "global"
foo <- function(){
    bar <- "in foo"   
    baz <- function(){
        assign("bar", "in baz", envir = .GlobalEnv)
    }
    print(bar)
    baz()
    print(bar)
}
bar
#[1] "global"
foo()
#[1] "in foo"
#[1] "in foo"
bar
#[1] "in baz"

Итак, оба раза мы печатаем bar внутри fooзначения «in foo» даже после вызова baz. Это потому, что мы assignдаже не рассматривали копию barвнутри foo, потому что мы сказали, где именно искать. Однако на этот раз значение bar в глобальной среде было изменено, потому что мы явно присвоили ему значение.

Теперь вы также спросили о создании локальных переменных, и вы можете сделать это довольно легко, не создавая функцию ... Нам просто нужно использовать localфункцию.

bar <- "global"
# local will create a new environment for us to play in
local({
    bar <- "local"
    print(bar)
})
#[1] "local"
bar
#[1] "global"

2

Еще немного в том же духе

attrs <- {}

attrs.a <- 1

f <- function(d) {
    attrs.a <- d
}

f(20)
print(attrs.a)

напечатает "1"

attrs <- {}

attrs.a <- 1

f <- function(d) {
   attrs.a <<- d
}

f(20)
print(attrs.a)

Напечатает «20»

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.