2 votes

Comment R fait-il référence aux valeurs non attribuées ?

Je suis familier avec tracemem() montrant l'adresse mémoire hexagonale d'une variable assignée, par ex.

x <- 2
tracemem(x)
#> [1] "<0x876df68>"

mais qu'est-ce que cela implique (sous le capot) lorsque la valeur est littéralement juste une valeur non assignée ? par exemple

tracemem(4)
#> [1] "<0x9bd93b8>"

La même question s'applique à l'évaluation d'une expression sans affectation.

4
#> [1] 4

Il semble que si j'évalue ceci plusieurs fois dans la console, j'obtiens des adresses hexadécimales de plus en plus grandes.

tracemem(4)
#> [1] "<0x8779968>"
tracemem(4)
#> [1] "<0x87799c8>"
tracemem(4)
#> [1] "<0x8779a28>"

mais si je boucle explicitement cette opération

for ( i in 1:3 ) { print(tracemem(4)) }
#> [1] "<0x28bda48>"
#> [1] "<0x28bda48>"
#> [1] "<0x28bda48>"

ou avec sapply via replicate

replicate(3, tracemem(4))
#> [1] "<0xba88208>" "<0xba88208>" "<0xba88208>"

J'obtiens des répétitions de la même adresse, même si je retarde explicitement l'impression entre les itérations.

for ( i in 1:3 ) { print(tracemem(4)); Sys.sleep(1) }
#> [1] "<0xa3c4058>"
#> [1] "<0xa3c4058>"
#> [1] "<0xa3c4058>"

Mon hypothèse la plus probable est que l'appel fait référence à une valeur déjà temporairement affectée dans le fichier parent.frame donné eval.parent(substitute( sur replicate mais je n'en sais pas assez sur le système sous-jacent. .Primitive code de for pour savoir si ça fait la même chose là-bas.

J'ai confiance dans le fait que R est de créer des variables temporaires étant donné que je peux faire

list(x = 1)
#> $x
#> [1] 1

donc R doit traiter les données même s'il n'affecte jamais rien. Je suis conscient de la stricte formalité résumée par le tweet de @hadleywickham :

enter image description here

mais je ne suis pas sûr de savoir comment cela fonctionne ici. Est-ce que le nom temporaire n'est pas préservé ? Est-ce que le for boucle utilise toujours ce nom/objet ? L'évaluation d'un grand nombre de codes, qu'ils soient assignés ou non, consomme-t-elle toujours de la mémoire ? (jusqu'à gc() est appelé, quel que soit le moment où il est appelé)

tl;dr -- comment R "stocker" les valeurs non attribuées pour l'impression ?

1voto

Gabe Becker Points 156

Ok, donc je vais faire ce que je peux ici.

Tout d'abord, tracemem est un primitif . Cela signifie qu'il ne s'agit pas d'une fermeture comme la grande majorité des fonctions de niveau R que vous pouvez appeler à partir du code R. Plus précisément, il s'agit d'une primitive BUILTINSXP :

> .Internal(inspect(tracemem))
@62f548 08 BUILTINSXP g0c0 [MARK,NAM(1)] 

Cela signifie que lorsqu'il est appelé, un fermeture n'est PAS appliqué (car il s'agit d'une primitive) et son argument EST évalué, car il s'agit d'un BUILTINSXP (cf. cette section du manuel interne de R ).

On parle d'application de fermeture lorsque les objets R passés comme arguments dans les appels de fonction sont affectés aux variables appropriées dans le cadre de l'appel. Ainsi, cela ne se produit pas pour tracemem. Au lieu de cela, ses arguments sont évalués au niveau C dans un SEXP qui n'est jamais lié à aucun symbole dans aucun environnement, mais qui est passé directement à la fonction do_tracemem au niveau C. Voir cette ligne dans la fonction eval du niveau C

Cela signifie que lorsqu'une constante numérique est transmise à tracemem (un appel valide bien qu'il n'y ait généralement aucune raison de le faire), vous obtenez le SEXP réel de la constante, et non celui représentant une variable de niveau R avec la valeur 4, transmis à do_tracemem.

Pour autant que je puisse dire, dans n'importe quel cadre d'évaluation (je n'utilise peut-être pas ce terme précisément, mais les cadres d'appel et les étapes d'une boucle for sont qualifiés, tout comme les expressions de haut niveau individuelles), chaque évaluation de, par exemple, "4L" obtient un SEXP entièrement nouveau (INTSXP, spécifiquement) avec NAMED fixé immédiatement à 4. Entre ces cadres, il semble qu'ils puissent être partagés, bien que je soupçonne fortement que cela puisse être un artefact de réutilisation de la mémoire plutôt que des SEXP réellement partagés.

La sortie ci-dessous semble corroborer la théorie de la réutilisation de la mémoire mais je n'ai pas les cycles libres pour la confirmer au-delà pour le moment.

> for(i in 1:3) {print(tracemem(4L)); print(tracemem(4L))}
[1] "<0x1c3f3b8>"
[1] "<0x1c3f328>"
[1] "<0x1c3f3b8>"
[1] "<0x1c3f328>"
[1] "<0x1c3f3b8>"
[1] "<0x1c3f328>"

J'espère que cela vous aidera.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X