2016-02-03 15 views
11

Kann jemand dieses Verhalten mir das erklären?Zuweisen mehrerer Umgebungen

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

ich erwartet hätte, dass a/b/c verschiedene Umgebungen ähnlich wie eine Variable in der gleichen Art und Weise erstellt werden würde?

Allerdings zeigen drei Umgebungen in der globalen Umgebung, aber jede Aktion auf eine wird in alle von ihnen geschoben.

+2

Für eine schöne Erklärung der Referenz-Semantik von Umgebungen siehe Hadley http://adv-r.had.co.nz/Environments.html. -> "Neben dem Power-Scoping sind Umgebungen auch eigenständige Datenstrukturen, da sie Referenzsemantik haben. Im Gegensatz zu den meisten Objekten in R wird bei der Änderung einer Umgebung keine Kopie erstellt." – cryo111

Antwort

13

Haftungsausschluss: Diese Antwort kann nicht völlig SFW als S-Ausdrücke, die für in der Nähe von allen Objekten in R abgekürzt sexp (Yep, S-expression, wobei der Strich die häufigste Art ist, ist nicht, wo Sie dachten,). Jetzt als SALT 'N' PEPA hatte singen: lassen Sie uns über sexp sprechen!


TL; DR: Eine Umgebung, in ihm gespeichert ist, ist Elternumgebung als Zeiger, die Variable zu kopieren, darauf zuzugreifen, vervielfältigen nur den Zeiger und immer noch das gleiche Objekt anvisieren.


Ich habe einige auf die Ursache zu graben, Hauptgrund ist, was eine Umwelt ist, oder in der Tat, wie es in ihm gespeichert ist, ist Umwelt Eltern. Mal sehen, new.env:

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

Ok, Zeit zum Quellcode gehen, in names.c:

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

Suche do_newenv bringen uns builtin.c die Return (ich hier eine Verknüpfung nahm, aber lassen Sie uns dies nicht halten zu lang):

ans = NewEnvironment(R_NilValue, R_NilValue, enclos); 

Diese NewEnvironment definiert here in memory.c und die comm ents darüber uns einen Hinweis geben, was los ist:

Erstellen Sie eine Umgebung „rho“ mit einem Rahmen erhalten durch die Erweiterung von
die Variablennamen durch die Tags auf „Namensliste“ mit den angegebenen Werte Paarung gegeben durch die Elemente der "Werteliste".

Der Code auf sich selbst ist nicht so einfach zu folgen:

zu einer Variablendefinition in globalen Umfeld
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); 
} 

Vergleich (exemple für geistige Gesundheit des Lesers Geist gewählt) durch 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); 
} 

Wir können den "Wert" sehen, der von der Elternumgebung zugänglich ist, ist die neue Umgebungsadresse, gegeben durch die GET_FREE_NODE auf Elternumwelt (ich bin unsicher, dass ich hier klar bin, aber ich fand kein richtiges p rasing).

Also mit der Tatsache <- ist definiert als x <- value Wir kopieren einen Zeiger, wir haben mehrere unabhängige Variablen, die alle auf das gleiche Objekt zeigen.

Das Aktualisieren des Objekts mit einer Referenz aktualisiert das einzige im Speicher vorhandene Objekt.

SEXP Ständer für S-Expression nach verschiedenen literrature und ist in erster Linie ein Zeiger in C.

Von Kommentaren,

+2

Also, wenn ich zusammenfassen würde. Das liegt daran, dass 'new.env()' einen Zeiger auf eine Umgebung zurückgibt und im Wesentlichen, wenn Sie 'a <-b <-c <-neu.env()' verketten, nur einen Zeiger auf 'b' und' c schieben 'und deshalb drücken alle Aktionen, die an einem ausgeführt werden, automatisch auf die anderen (weil die anderen nur auf den ersten zeigen). –

+2

@Brandon genau. Und new.env-Dokumentation gibt es nicht deutlich. – Tensibai

+2

Wofür steht * SEXP *? Googeln, das mich beinahe aus dem Amt geworfen hätte. –

7

new.env() wird nur einmal obwohl genannt wird, nur eine neue Umgebung zu schaffen. Sie alle bekommen die gleiche Umgebung, weil Sie alle Zuweisungen auf den gleichen new.env() Anruf gekettet. Wenn Sie also einem zuweisen, weisen Sie ihnen alle zu.

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

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

Wenn Sie sie getrennte Umgebungen sein wollen, nicht Kette die Zuordnung (das heißt drei getrennte Anrufe zu new.env() verwenden).

Der Vollständigkeit halber in Tensibai Kommentar zu bringen -

Dies ist ein Nebeneffekt der <- Ihre Codezeile ist die gleiche wie a <- new.env(); b <- a; c <- a (die mehr obvisouly nicht new.env() 3 mal nicht nennen, aber verweisen sie auf 3 Variablennamen)

+1

Kannst du einen guten Grund für 3 Kopien derselben Umgebung in einem Arbeitsbereich finden? Ich wäre daran interessiert, einen Anwendungsfall für dieses Verhalten zu verstehen. Mein Verständnis war, dass Umgebungen wie 'lists()' entworfen wurden, aber dieses Verhalten ist ein wenig unintuitiv und unerwartet. –

+6

@BrandonBertelsen Dies ist ein Nebeneffekt von '<-' Ihre Codezeile ist die gleiche wie' a <- new.env(); b <- a; c <- a' (was obviously nicht new.env() 3 mal nennt, aber referenziere es auf 3 Variablennamen) – Tensibai

+0

Wie oben erwähnt, denke ich, dass dies mehr mit der Zuweisung zu tun hat. 'a <- b <- c <- 3 'wäre in einigen Fällen definitiv nützlich, aber offensichtlich nicht wirklich nützlich in Umgebungen. –

Verwandte Themen