Назначение нескольких сред

Может кто-нибудь объяснить мне такое поведение?

a <- b <- c <- new.env()
a$this <- 1
b$this 
# 1
c$this 
# 1 

Я бы ожидал, что a/b/c будет отличной средой, подобной переменной, созданной таким же образом?

Тем не менее, три среды отображаются в глобальной среде, но любое действие на одном вставляется во всех них.

Ответ 1

Отказ от ответственности: этот ответ может быть не полностью SFW, поскольку S-выражения, которые являются общим типом для почти всех объектов в R, сокращены SEXP (Yep, S-EXPression, дефис не там, где вы думали). Теперь, когда SALT 'N' PEPA поет: давайте поговорим о SEXP!


TL; DR: среда хранит в ней родительскую среду в качестве указателя, копирует переменную для доступа к ней, просто дублирует указатель и все еще нацеливает один и тот же объект.


Я сделал некоторую копанию в основной причине, основной причиной является то, что является средой, или, по сути, как она хранится в ней родительской среде. Посмотрим new.env:

> new.env
function (hash = TRUE, parent = parent.frame(), size = 29L) 
.Internal(new.env(hash, parent, size))
<bytecode: 0x0000000005972428>
<environment: namespace:base>

Хорошо, пора перейти к исходному коду, names.c:

{"new.env", do_newenv,  0,  11,     3,      {PP_FUNCALL, PREC_FN,   0}},

Поиск do_newenv приведет нас к builtin.c, которые возвращаются (я нашел здесь ярлык, но пусть это не слишком долго):

ans = NewEnvironment(R_NilValue, R_NilValue, enclos);

Этот NewEnvironment определяется здесь, в memory.c, а комментарии выше он дает нам представление о том, что происходит:

Создайте среду, расширив "rho" с помощью рамки, полученной с помощью сопоставление имен переменных, заданных тегами в "namelist", с помощью значения, заданные элементами "валуалистов".

Код сам по себе не так прост:

SEXP NewEnvironment(SEXP namelist, SEXP valuelist, SEXP rho)
{
    SEXP v, n, newrho;

    if (FORCE_GC || NO_FREE_NODES()) {
    PROTECT(namelist);
    PROTECT(valuelist);
    PROTECT(rho);
    R_gc_internal(0);
    UNPROTECT(3);
    if (NO_FREE_NODES())
        mem_err_cons();
    }
    GET_FREE_NODE(newrho);
    newrho->sxpinfo = UnmarkedNodeTemplate.sxpinfo;
    INIT_REFCNT(newrho);
    TYPEOF(newrho) = ENVSXP;
    FRAME(newrho) = valuelist;
    ENCLOS(newrho) = CHK(rho);
    HASHTAB(newrho) = R_NilValue;
    ATTRIB(newrho) = R_NilValue;

    v = CHK(valuelist);
    n = CHK(namelist);
    while (v != R_NilValue && n != R_NilValue) {
    SET_TAG(v, TAG(n));
    v = CDR(v);
    n = CDR(n);
    }
    return (newrho);
}

По сравнению с определением переменной в глобальной среде (например, выбранным для разумности сознания читателя) gsetVar:

void gsetVar(SEXP symbol, SEXP value, SEXP rho)
{
    if (FRAME_IS_LOCKED(rho)) {
    if(SYMVALUE(symbol) == R_UnboundValue)
        error(_("cannot add binding of '%s' to the base environment"),
          CHAR(PRINTNAME(symbol)));
    }
#ifdef USE_GLOBAL_CACHE
    R_FlushGlobalCache(symbol);
#endif
    SET_SYMBOL_BINDING_VALUE(symbol, value);
}

Мы можем видеть, что "ценность", доступная из родительской среды, - это новый адрес среды, заданный GET_FREE_NODE в родительской среде (я не уверен, что я здесь ясен, но я не нашел правильной формулировки).

Итак, с тем фактом, что <- определяется как x <- value, мы копируем указатель, у нас есть кратные независимые переменные, все указывающие на один и тот же объект.

Обновление объекта с использованием любого ссылочного обновления - единственный объект, существующий в памяти.

SEXP означают S-Expression в соответствии с различной литратурой и в основном являются указателем на C.

Из комментариев,

Ответ 2

new.env() вызывается только один раз, создавая только одну новую среду. Все они получают одну и ту же среду, потому что вы привязали все назначения к одному и тому же вызову new.env(). Поэтому, когда вы назначаете одно, вы назначаете им все.

a <- b <- c <- new.env()

a
# <environment: 0x49c1ed8>
b
# <environment: 0x49c1ed8>
c
# <environment: 0x49c1ed8>

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

Для полноты, принося Tensibai комментарий в

это побочный эффект <-, ваша строка кода такая же, как a <- new.env(); b <- a; c <- a (что более obvisouly не вызывает new.env() 3 раза, а ссылается на 3 имени переменных)