Ответ 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.
Из комментариев,
- исправление языка R, спасибо @BrodieG
- Сообщение в блоге о интерфейсе R C
- Спасибо @Alexis_laz, чтобы заставить меня получить реальную основную причину более подробно.