Re : Déclaration locale par défaut vs déclaration explicite des sections localesJ'aime la liste de déclaration locale explicite. Quand j'en manque un, il atterrit dans l'espace de noms global et showglobals() l'identifie.Faire mousser, rincer, répéter jusqu'à ce que l'espace global reste propre.
Re : Déclaration locale par défaut vs déclaration explicite des sections localesPuis-je vous demander quel est votre flux de travail pour vérifier cela ? Ajoutez-vous "Show Globals()" à la fin de vos fonctions pendant le développement et vérifiez manuellement que vous n'avez rien ajouté d'involontaire à l'espace de noms global ? Ou est-ce quelque chose que vous pouvez appliquer avec des tests ?
Question annexe : est-ce que c'est Afficher les valeurs globales ou Afficher les symboles que je devrais utiliser ? Je ne vois aucune sortie de Show Globals même lorsque j'omets intentionnellement des variables locales de mes fonctions, mais lorsque j'utilise Show Symbols, j'obtiens tellement de sortie que je ne remarquerais de toute façon aucune nouvelle liste.
Si cela aide, j'ai activé le mode "Names Default To Here" dans tous mes fichiers de script.
Re : Déclaration locale par défaut vs déclaration explicite des sections localesMon flux de travail dans le passé consistait à ne pas utiliser Names Default To Here et à conserver simplement l'espace de noms global clair. Show Globals() ne voit probablement jamais rien si vous utilisez toujours Names Default To Here. Show Symbols() est bruyant car il rapporte des informations à travers la pile d'appels si vous l'utilisez dans une fonction profondément imbriquée. Voici mon exemple de flux de travail, retravaillé pour utiliser les noms par défaut ici et afficher les symboles. Exécutez-le, tel quel, et il ne fuit pas. commentez la ligne 17 (g3) et cela générera une erreur car g3 est placé dans l'espace de noms 'here'. L'utilisation de g2 dans l'exemple met en évidence un vilain problème ; locale par défaut résout ce problème mais ajoute les problèmes que vous avez notés. Je pourrais essayer de revenir à Default Local dans un futur projet. Je pense que ce pourrait être un choix plus sûr pour moi; Je suis plus susceptible de laisser une variable en dehors des variables locales explicites que de lire une variable avant de l'écrire.
Je pense que vous essayez de faire un test plus fin que moi; Je ne teste (globals ou ici) qu'à la fin. Vous pourrez peut-être utiliser certaines de ces idées si vous devez tester les fuites d'espace de noms dans chaque fonction.
// **** you will probably need to restart JMP to really understand each change you make. ****
xyzzy = "before names default to here"; // this goes into globals. If you run again it goes into 'here'.
Names Default To Here( 1 ); // takes effect when executed
New Namespace( // don't put the functions in the here namespace, keep them in appDemo.
"appDemo" // this keeps the here namespace free of voluminous function definitions.
);
appDemo:originalLeakState = Log Capture( Show Symbols() ); // remember the original state
appDemo:f1 = Function( {p1}, // parameter list
{
//g2, // <<<< comment this use f2's local
g3, // <<<< comment this to make a leak
g1}, // local list
Write( "\!ng2=", g2 );
g1 = 1; // very local to this function
g2 = 2; // not local, in this example it is local to f2, a bug if unintentional
g3 = 3; // not local, lands in the 'here'
);
appDemo:f2 = Function( {p2},
{g2},
g2 = 42; // this will be over written by f1
appDemo:f1( p2 );
);
appDemo:f2( "x" );
// if you call f1 from here, the here:g2 gets written
// g2 = 142;
// appDemo:f1( "x" );
// a test might look like this
If( Log Capture( Show Symbols() ) != appDemo:originalLeakState,
Write( "\!n=============leak report==============" );
Show Symbols();
Throw( "variable namespace leaks, see log!" );
,
Write( "\!n========= no change to symbols =======" );
If( Length( appDemo:originalLeakState ),
Write(
"\!n========= original leak state: =======",
appDemo:originalLeakState
)
);
);Re : Déclaration locale par défaut vs déclaration explicite des sections localesMerci, ceci est très utile! Je pense que je devrais pouvoir utiliser une partie de ce que vous avez écrit ici afin d'écrire des vérifications automatisées qui garantissent qu'il n'y a aucune fuite.
Re : Déclaration locale par défaut vs déclaration explicite des sections localesIl y a aussi quelque chose de bizarre à propos de DEFAULT LOCAL.En fait, il lit le script et crée les affectations locales en fonction de son interprétation des variables créées.Il y a plus que quelques façons de tout foutre en l'air. Celui que je vois probablement le plus souvent chez les gens, c'est quand ils essaient de créer dynamiquement un nom de variable.Ce que je dirais n'est pas génial à faire et vous ne pouvez pas le dire explicitement non plus, donc c'est un peu la même chose.
Names default to here(1);
f = function({},
{DEFAULT LOCAL},
x = 14;
Eval(Parse("x"||char(x)||"=28"));//not that anyone should ever do this BUT ...
return(x);
);
f();
namespace("here"); // notice how x14 is in the here namespace
/*
New Namespace(
{
f = Function( {},
{Default Local},
x = 14;
Eval( Parse( "x" || Char( x ) || "=28" ) );
Return( x );
),
x14 = 28
}
)
/*Re : Déclaration locale par défaut vs déclaration explicite des sections localesPour tous les futurs lecteurs, j'ai finalement écrit un assistant de test que j'utilise pour imposer que tous les locaux soient explicitement déclarés
/*
* Test that `invocation` does not add any variables to the Here namespace. It is important that
* this test be run first, else whatever variables may leak will leak before running this test and
* so they will already be present in `hereVariablesBefore`.
*
* Example:
* someFunction1 = Function( {}, {}, leakedVar = 1);
* someFunction2 = Function( {}, {safeVar}, safeVar = 1);
* assertNoLeakedVariables( Function( {}, {}, someFunction1() ) ); // Fails
* assertNoLeakedVariables( Function( {}, {}, someFunction2() ) ); // Passes
*/
assertNoLeakedVariables = Function( {invocation},
{hereVariablesBefore, hereVariablesAfter},
Try(
// Declare both of these in advance, because otherwise the key `hereVariablesBefore` will not be
// present in `hereVariablesBefore` and it makes the assertion confusing.
hereVariablesBefore = .;
hereVariablesAfter = .;
hereVariablesBefore = Namespace( "here" ) << Get Keys;
invocation();
hereVariablesAfter = Namespace( "here" ) << Get Keys;
UT Assert( Expr( Length( hereVariablesBefore ) ), Length( hereVariablesAfter ) );
If( Length( hereVariablesBefore ) != Length( hereVariablesAfter ),
showLeakedVariableNames( hereVariablesBefore, hereVariablesAfter )
);
,
UT Assert( exception_msg, "" )
)
);Re : Déclaration locale par défaut vs déclaration explicite des sections localesCela m'est revenu assez souvent - cela peut être très frustrant :
ce script écrase les noms (les noms échappent à la portée locale de la fonction) :
Names Default to Here( 1 );
some expr = Expr(
a = 4;
b = 5;
g = 6;
);
f = Function( {input expr},
{Default Local},
Print( "running expr" );
input expr
);
f( Name Expr( some expr ) );
show( a, b, g )Voici une solution, mais le problème avec cette solution est qu'il s'agit techniquement de deux appels de fonction (compte comme 2 éléments de pile, atteindra donc plus rapidement la limite de 225 piles)
Names Default to Here( 1 );
some expr = Expr(
a = 4;
b = 5;
g = 6;
Show( Eval List( {a, b, g} ) );
);
f = Function( {input expr},
{Default Local},
Print( "running expr" );
Eval( Eval Expr(
Local( {Default Local},
Expr( Name Expr( input expr ) )
)
) );
1
);
f( Name Expr( some expr ) );
show( a, b, g )Ce serait bien si la directive "Default Local" n'était pas une directive de temps d'analyse mais un indicateur d'exécution, mais peut-être que ce qui se passe est que la résolution de nom est effectuée au moment de l'analyse, rendant ainsi inutiles les règles de recherche de nom à l'exécution .
Local par défaut vs déclaration explicite des locauxSalut, j'essaie de comprendre comment limiter au mieux mon risque en ce qui concerne la portée locale des fonctions. D'après ce que j'ai compris, mes options pour limiter le potentiel de pollution de la portée Here à partir de l'exécution d'une fonction consistent à déclarer mes variables locales ou à utiliser Default Local. Cependant, chacun d'entre eux a quelques problèmes:
Locale par défaut :
- Les valeurs de référence peuvent dépendre du contexte, car les noms qui n'ont pas été définis localement se replient sur Ici, puis éventuellement (si les noms par défaut ici sont désactivés) Global puis sur la table actuelle. Comme indiqué dans la documentation, cela peut signifier que le comportement est moins prévisible car les valeurs dépendent du contexte.
Déclaration explicite :
- Les valeurs de référence peuvent dépendre du contexte, puisque les noms utilisent toujours ce comportement de secours jusqu'à ce qu'ils soient déclarés s'ils ne sont pas déclarés dans les noms locaux
- Les affectations peuvent accidentellement fuir dans la portée Here. Autrement dit, si un nom destiné à être local est omis des déclarations de nom local, il polluera la portée Here environnante
- Généralement, cette pollution à portée extérieure est difficile à détecter.
- Par exemple, si vous appelez une fonction dans un For où vous utilisez "i" comme itérateur et que cette fonction a également un For qui utilise i et ne parvient pas à le déclarer comme local, cela affectera votre boucle For externe. Certes, il s'agit d'une erreur évidente du programmeur, mais elle est facile à faire et difficile à déboguer.
Dans mon esprit, l'option idéale serait capable de déclarer quelque chose comme "Strict Local", qui traiterait toujours les noms non qualifiés (à la fois lors de l'attribution de valeurs et des valeurs de référence) car les portées locales et environnantes ne seraient accessibles qu'avec des noms qualifiés. Cela me permettrait de ne pas risquer de polluer les champs d'application environnants et de m'obliger à déclarer quand je dépends de ces champs d'application, limitant ainsi la pollution locale des champs d'application environnants.
Modélisation statistique avancéeÉtudes de consommation et de marchéAccès aux donnéesExploration et visualisation des donnéesPersonnalisation de masseRe : Déclaration locale par défaut ou explicite des variables localesConcernant « Workflow » – Une petite aide comme celle-ci #NamespaceInspector aidera beaucoup à garder une trace des variables.
Apparaissent-ils dans un espace de noms Here ou dans un espace de noms local ?
Une fois le débogage terminé, je le téléchargerai en tant que module complémentaire...