44 votes

Exemples de périls de globals dans R et Stata

Au cours des dernières conversations avec d'autres étudiants, j'ai plaidé pour éviter les variables globales, sauf pour stocker des constantes. C'est une sorte de typique statistiques appliquées-type de programme où tout le monde écrit leur propre code et la taille des projets sont sur le petit côté, de sorte qu'il peut être difficile pour les gens de voir les problèmes causés par bâclée habitudes.

En parlant d'évitement de variables globales, je me concentre sur les points suivants raisons pour lesquelles les variables globales peuvent causer des problèmes, mais j'aimerais avoir quelques exemples de R et/ou Stata pour aller avec les principes (et de tout autres principes que vous pourriez trouver important), et je vais avoir un moment difficile à venir avec de crédible ceux.

  • La Non-localité: Globals faciliter le débogage plus difficile parce qu'ils font de comprendre le flux de code plus difficile
  • Association implicite: Globals briser la simplicité de la programmation fonctionnelle en permettant des interactions complexes entre éloignés des segments de code
  • Collisions d'espace de noms: les noms Communs (x, i, et ainsi de suite) re-utilisé, provoquant des collisions d'espace de noms

Une réponse utile à cette question serait un reproductibles et autonome extrait de code dans lequel globals cause d'un type spécifique de la difficulté, idéalement avec un autre extrait de code dans lequel le problème est corrigé. Je peux générer le corrigé des solutions si nécessaire, afin que l'exemple de ce problème est le plus important.

Pertinentes liens:

Les Variables globales sont Mauvais

Sont des variables globales mauvais?

28voto

csgillespie Points 20349

J'ai aussi le plaisir de l'enseignement de la R pour les étudiants de premier cycle qui n'ont aucune expérience de la programmation. Le problème que j'ai constaté, c'est que la plupart des exemples de cas où les variables globales sont mauvais, sont plutôt simpliste et n'a pas vraiment d'obtenir le point à travers.

Au lieu de cela, j'essaie d'illustrer le principe de moindre étonnement. J'utilise des exemples où il est difficile de comprendre ce qui se passait. Voici quelques exemples:

  1. Je demande à la classe d'écrire ce qu'ils pensent de la valeur finale de l' i sera:

    i = 10
    for(i in 1:5)
        i = i + 1
    i
    

    Une partie de la classe de deviner correctement. Puis-je demander si jamais vous écrire du code comme celui-ci?

    Dans un certain sens, i est une variable globale qui est en cours de modification.

  2. Quel est le morceau de code suivant retour:

    x = 5:10
    x[x=1]
    

    Le problème est qu'entendons-nous exactement par x

  3. La fonction suivante, de retour d'un mondial ou local variable:

     z = 0
     f = function() {
         if(runif(1) < 0.5)
              z = 1
         return(z)
      }
    

    Réponse: les deux. De nouveau discuter de pourquoi c'est mauvais.

16voto

StasK Points 955

Oh, la merveilleuse odeur de variables globales...

Toutes les réponses dans ce post a R des exemples, et l'OP voulait Stata exemples, ainsi. Permettez-moi donc de carillon avec ces.

Contrairement R, Stata ne prendre soin de la localité de sa macros locales (celles que vous créez avec local de la commande), de sorte que la question de "Est-ce de ce mondial z ou z qui est de retour?" ne jamais se lève. (Mon dieu... comment pouvez-vous R gars écrire du code si la localité n'est pas appliquée???) Stata est un autre bizarrerie, cependant, à savoir qu'un inexistante local ou global macro est évalué comme une chaîne vide, ce qui peut ou peut ne pas être souhaitable.

J'ai vu des variables globales utilisées pour plusieurs raisons principales:

  1. Globales sont souvent utilisées comme raccourcis pour la liste des variables, comme dans

    sysuse auto, clear
    regress price $myvars
    

    Je soupçonne que l'utilisation principale de cette construction est pour quelqu'un qui alterne entre la frappe interactives et de stocker le code dans un fichier comme ils essaient plusieurs spécifications. Dire qu'ils essaient de régression avec homoskedastic erreurs standard, heteroskedastic standard des erreurs, et la médiane de régression:

    regress price mpg foreign
    regress price mpg foreign, robust
    qreg    price mpg foreign
    

    Et puis ils s'ces régressions avec un autre ensemble de variables, puis à un autre encore, et finalement abandonner et de le configurer comme un faire-fichier myreg.do avec

    regress price $myvars
    regress price $myvars, robust
    qreg    price $myvars
    exit
    

    pour être accompagné avec un réglage approprié de la global macro. C'est très bien; l'extrait de code

    global myvars mpg foreign
    do myreg
    

    produit les résultats désirés. Maintenant, disons qu'ils ont envoyer leur fameux faire-fichier qui prétend produire de très bons résultats de la régression pour les collaborateurs et leur demander de type

    do myreg
    

    Quelles seront leurs collaborateurs de voir? Dans le meilleur des cas, la moyenne et la médiane d' mpg si ils ont démarré une nouvelle instance de Stata (échec du couplage: myreg.do n'a pas vraiment de savoir que vous vouliez exécuter ce avec un non-vide de la liste des variables). Mais si les collaborateurs ont quelque chose dans les travaux, et avaient aussi un mondial myvars défini (collision de nom)... l'homme, ce serait un désastre.

  2. Globales sont utilisées pour de fichier ou de répertoire des noms, comme dans:

    use $mydir\data1, clear
    

    Dieu seul sait ce qui va être chargé. Dans les grands projets, cependant, ne viennent à portée de main. Vous souhaitez définir global mydir quelque part dans ton maître n'-fichier, peut-être même que

    global mydir `c(pwd)'
    
  3. Globals peut être utilisé pour stocker une imprévisible de la merde, comme un ensemble de commande:

    capture $RunThis
    

    Dieu seul sait ce que sera exécuté. C'est le pire cas de l'implicite de couplage fort, mais depuis que je ne suis même pas sûr qu' RunThis contiendra rien de constructif, j'ai mis un capture , en face de lui, et sera prêt à traiter les non-zéro code de retour _rc. (Voir, cependant, mon exemple ci-dessous).

  4. Stata est propre utilisation de variables globales est pour Dieu paramètres, comme le type I de la probabilité d'erreur/niveau de confiance: le global $S_level est toujours défini (et vous devez être un total idiot de redéfinir ce mondial, même si bien sûr il est techniquement faisable). C'est, cependant, la plupart du temps héritage problème avec le code de la version 5 et au-dessous (à peu près), que la même information peut être obtenue à partir de moins fragiles du système de la constante:

    set level 90
    display $S_level
    display c(level)
    

Heureusement, globales sont tout à fait explicite dans Stata, et sont donc facile à déboguer et à enlever. Dans certains des cas ci-dessus, et certainement dans la première, vous voulez passer des paramètres à faire des fichiers qui sont considérés comme les locaux `0' à l'intérieur du faire-fichier. Au lieu d'utiliser des variables globales dans l' myreg.do le fichier, je serais probablement de code comme

    unab varlist : `0'
    regress price `varlist'
    regress price `varlist', robust
    qreg    price `varlist'
    exit

L' unab chose va servir d'élément de protection: si l'entrée n'est pas un varlist, le programme s'arrête avec un message d'erreur.

Dans le pire des cas, j'ai vu, le mondial a été utilisé qu'une seule fois après avoir été définies.

Il ya des occasions où vous voulez utiliser des variables globales, parce que sinon vous auriez à passer le truc à chaque autre, d'un fichier ou d'un programme. Un exemple où j'ai trouvé la globals pratiquement inévitable a été le codage d'un estimateur du maximum de vraisemblance où je ne savais pas à l'avance combien d'équations et de paramètres je l'aurais fait. Stata insiste sur le fait que l' (fourni par l'utilisateur) la probabilité de l'évaluateur aura équations spécifiques. J'ai donc dû s'accumuler mes équations dans le globals, et ensuite appeler mon évaluateur avec les variables globales dans la description de la syntaxe que Stata aurait besoin d'analyser:

args lf $parameters

lf a la fonction objectif (la log-vraisemblance). J'ai rencontré au moins deux fois, dans le mélange habituel de package (denormix) et de l'analyse factorielle confirmatoire paquet (confa); vous pouvez l' findit tous les deux, bien sûr.

11voto

Gavin Simpson Points 72349

Un R exemple d'une variable globale qui divise l'opinion est l' stringsAsFactors question sur la lecture de données dans R ou de la création d'une trame de données.

set.seed(1)
str(data.frame(A = sample(LETTERS, 100, replace = TRUE),
               DATES = as.character(seq(Sys.Date(), length = 100, by = "days"))))
options("stringsAsFactors" = FALSE)
set.seed(1)
str(data.frame(A = sample(LETTERS, 100, replace = TRUE),
               DATES = as.character(seq(Sys.Date(), length = 100, by = "days"))))
options("stringsAsFactors" = TRUE) ## reset

Cela ne peut pas vraiment être corrigé, car de la façon dont les options sont mises en œuvre dans la R - tout pourrait changer, sans le savoir et donc le même bloc de code n'est pas garanti pour revenir exactement le même objet. John Chambers, se plaint cette fonctionnalité dans son récent livre.

8voto

Gavin Simpson Points 72349

Un pathologiques exemple dans R est l'utilisation de l'une des variables globales disponibles dans R, pi, pour calculer l'aire d'un cercle.

> r <- 3
> pi * r^2
[1] 28.27433
> 
> pi <- 2
> pi * r^2
[1] 18
> 
> foo <- function(r) {
+     pi * r^2
+ }
> foo(r)
[1] 18
> 
> rm(pi)
> foo(r)
[1] 28.27433
> pi * r^2
[1] 28.27433

Bien sûr, on peut écrire la fonction foo() défensivement en forçant l'utilisation de base::pi mais un tel recours peut ne pas être disponible en normal le code de l'utilisateur, à moins emballé et à l'aide d'un NAMESPACE:

> foo <- function(r) {
+     base::pi * r^2
+ }
> foo(r = 3)
[1] 28.27433
> pi <- 2
> foo(r = 3)
[1] 28.27433
> rm(pi)

Cela met en évidence le désordre, vous pouvez obtenir dans, en s'appuyant sur tout ce qui n'est pas uniquement dans le cadre de votre fonction ou transmis explicitement comme un argument.

8voto

Michael Points 1466

Voici une intéressante pathologique exemple impliquant des fonctions de remplacement, le mondial de céder et de x défini à la fois globalement et localement...

x <- c(1,NA,NA,NA,1,NA,1,NA)

local({

    #some other code involving some other x begin
    x <- c(NA,2,3,4)
    #some other code involving some other x end

    #now you want to replace NAs in the the global/parent frame x with 0s
    x[is.na(x)] <<- 0
})
x
[1]  0 NA NA NA  0 NA  1 NA

Au lieu de renvoyer [1] 1 0 0 0 1 0 1 0, le remplacement de la fonction utilise l'index retourné par la valeur locale de l' is.na(x), même si vous êtes à l'affectation de la valeur globale de x. Ce comportement est documenté dans la R de Définition de Langage.

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