Aide JSL : Aide sur la syntaxe de la commande Set Function()Salut tout le monde,
De retour avec une autre question de programmation JSL. C'est lié aux autres messages via, par exemple ici .
J'aimerais avoir une fonctionnalité Check Box() qui, lorsque la case est cochée (état = 1), les lignes sélectionnées (il s'agit de lignes où RSquare est négatif) sont sélectionnées puis masquées et supprimées afin que le générateur de graphiques mis à jour affiche la sortie souhaitée. Ensuite, si la case n'est pas cochée (état = 0), affiche et exclut ces lignes. L'appel Set Function() que j'utilise est ci-dessous, et il fonctionne si j'exécute manuellement le "script" de Set Function(), mais il ne le fait pas automatiquement. Je n'arrive pas à comprendre ce qui me manque pour qu'il se mette à jour automatiquement. J'ai trouvé un post sympa ici , et j'essaie de faire quelque chose comme ce que cette personne a fait, mais pour la mise à jour du générateur de graphiques.
Appel de fonction JSL Set :
cb << Set Function(<BR />{this},<BR />this = cb << Get;<BR />If(<BR />this == 1,<BR />dt_results << Select Rows( neg_rows ) << Hide and Exclude;<BR />dt_results << Clear Select;<BR />dt_tb = dt_results << Get As Report();,<BR />this == 0,<BR />dt_results << Select Rows( neg_rows ) << Unhide;<BR />dt_results << Select Rows( neg_rows ) << Unexclude;<BR />dt_results << Clear Select;<BR />dt_tb = dt_results << Get As Report()<BR />);<BR />);cb << Set Function(
{this},
this = cb << Get;
If(
this == 1,
dt_results << Select Rows( neg_rows ) << Hide and Exclude;
dt_results << Clear Select;,
this == 0,
dt_results << Select Rows( neg_rows ) << Unhide;
dt_results << Select Rows( neg_rows ) << Unexclude;
dt_results << Clear Select;
);
);Où cb est mentionné plus tôt dans le code comme :
cb = Check Box( "Remove bad points (*'s)?", <<SET></SET>
Démarrage de la sortie, ou chaque fois que la case à cocher est state = 0. :

Sortie souhaitée chaque fois que la case à cocher est state = 1 :

Ce serait bien de mettre également à jour le tableau de données en tant que rapport sur la droite afin que toutes les lignes exclues soient ensuite supprimées de la vue Rapport sur la droite, mais je pense que cela pourrait être beaucoup plus difficile qu'il n'y paraît. J'ai essayé d'ajouter une mise à jour supplémentaire Get As Report dans la commande Set Function(), mais cela ne fait rien.
Tout commentaire est apprécié, merci !,
DS
Mélange et nettoyage des donnéesRe : Aide JSL : aide sur la syntaxe de la commande Set Function()Dans mon travail quotidien, je suis plutôt confronté au problème inverse :
Non : 2 fenêtres qui partagent le même espace de noms
mais : 2 scripts qui ne partagent qu'occasionnellement *) un point commun Ici Espace de noms.
Lorsque j'inclus un fichier JSL dans un autre, lors de l'exécution , le script inclus partage le Ici espace de noms du fichier jsl principal.
Mais quand je veux déboguer certaines lignes de code et les exécuter à partir du deuxième script, comment puis-je donner le code inclus script_B accès au Ici espace de noms du script principal ? Il y a 2 secondes, lorsque j'ai exécuté le code de script_B, il avait juste accès à cet espace de noms.
https://community.jmp.com/t5/Discussions/Debugging-Jmp-applications/mp/701121/highlight/true#M88566
Re : Aide JSL : aide sur la syntaxe de la commande Set Function()Remercier, @ErraticAttack pour le bel exemple du fonctionnement de la portée dans Jmp.
Cela m'a beaucoup aidé à me rapprocher de la compréhension...
Jusqu'à présent, je ne comprenais pas ce que @drewfoglia signifiait avec

dans Scripts essentiels pour l'efficacité et la reproductibilité : faire moins pour en faire plus (2019-US-TUT-290)
Votre exemple montre bien que ce n'est pas vrai.
Pourquoi notre expérience ici diffère-t-elle de la déclaration de Drew ?
Je pense que c'est parce que les fenêtres sont créées à partir de la même fenêtre de l'éditeur JSL.
Après avoir ouvert la première fenêtre TEST, il est très utile que la fenêtre de script ait toujours accès aux objets référencés teb et tuberculose .
Après avoir ouvert la deuxième fenêtre TEST, teb et tuberculose référencez maintenant les objets dans la deuxième fenêtre - ce qui paralyse apparemment la fonctionnalité de la première fenêtre (le texte saisi apparaît dans tuberculose , c'est à dire dans la deuxième fenêtre).
Donc, en liant les revendications de Boh, que se passe-t-il si les deux Windows ne partagent pas un éditeur JSL commun.
-> Les deux auront un NameSpace indépendant, comme Drew l'a annoncé dans son discours.
Options pour le faire :
a) fermez le Script et rouvrez-le, puis créez la deuxième fenêtre
b) appeler le script depuis un menu :

Re : Aide JSL : aide sur la syntaxe de la commande Set Function()Salut @ErraticAttack ,
Merci pour votre réponse et votre message détaillé. Il me faudra certainement un certain temps pour y parvenir, car je ne suis pas un programmeur professionnel, une grande partie de cela est encore nouvelle pour moi.
Merci!,
DS
Re : Aide JSL : aide sur la syntaxe de la commande Set Function()@ih , pour illustrer que JMP ne fait pas de cadrage comme on pourrait s'y attendre (surtout venant de Python par exemple), lancez simplement deux fenêtres à partir de ce script - les noms ne renvoient qu'à la deuxième fenêtre, pas à la première.C’est le plus gros problème que j’ai rencontré avec la définition de la portée (mais pas le seul).JMP fournit des pseudo-espaces de noms pratiques, tels que l'espace de noms window:.Cependant, ce ne sont pas des variables d'espace de noms appropriées et ne fonctionnent pas pleinement comme prévu dans les scripts volumineux.
Names Default to Here( 1 );
New Window( "TEST",
V List box(
teb = Text Edit Box( "", <<SET hint=""></SET>
@SDF1 , pour la portée explicite de l'espace de noms, ce que j'ai fait est de créer une bibliothèque qui effectue toutes les tâches fastidieuses d'injection de portée via la méta-programmation.Cela présente l'avantage supplémentaire de pouvoir définir les classes presque exactement telles qu'elles fonctionnent en Python (sans les remplacements de recherche pratiques).Je ne peux pas partager la bibliothèque car elle fait partie du travail de mon entreprise, mais voici essentiellement ce qui se passe :
Disons que vous avez un script simple comme celui-ci
Names Default to Here( 1 );
/*
Doing something initialization here!!
*/
dt = Current Data Table();
New Window( "Super Useful Script",
,
<<ON close=""></ON>
Ce script comporte deux éléments d'interface utilisateur interactifs, une certaine initialisation et potentiellement un nettoyage à la fermeture de la fenêtre.Notez que conceptuellement, l'arborescence d'affichage et la logique sont mélangées et qu'il existe des blocs très profonds.Le code est plus beau (et plus facile à maintenir) lorsque des éléments conceptuellement distincts sont également distincts dans le code.Ce serait bien de simplement créer une boîte à boutons avec un titre et un nom de fonction à appeler.La fonction qu'il appelle sera alors une fonction de niveau supérieur (qui peut être facilement appelée autrement par n'importe quelle partie du script) et sera distincte de l'arborescence d'affichage.
Vous pouvez transformer cela en quelque chose _beaucoup_ plus facile à digérer et à utiliser qui ressemble à ce qui suit :
/* super useful script */
initialize = Function( {}, // entry point to script
/* perform any initialization needed here */
self:dt = Current Data Table();
1
);
content = Function( {}, // this holds the display tree
V List Box(
self:one click button = meta:Button Box( "Useful 1-click function", "call one click" )
,
H List Box(
self:value teb = meta:Text Edit Box( "", "handle value change" ) << Set Hint( "Enter Value" )
)
)
);
finalize = Function( {}, // called after display tree has finalized
1
);
on close = Function( {}, // called when window wants to close
1
);
call one click = Function( {}, // called when button is pressed (line 11)
Print( "Done" )
);
handle value change = Function( {}, // called when TEB value changes(line 14)
Print( "Doing something special" )
);Maintenant, il est clair que ce script ne fera pas grand-chose si vous appuyez sur le bouton normal du script d'exécution JSL.Ce que vous devez faire avec cela est d'abord de définir l'espace de noms méta (qui contient les wrappers ButtonBox et TextBox afin qu'ils puissent bien se lier avec le reste du script) et de définir une fonction wrapper de méta-programmation qui fera ce qui suit :
Prenez un prototype de script comme celui-ci
<SOME script="" is="" above="" me="">
do someting = Function( {},
...
);
finalize = Function( {},
...
);
<MORE script="" is="" below="" me=""></MORE></SOME>Transformez-le en ce prototype :
<SOME script="" is="" above="" me="">
self:do someting = Function( {},
self = Namespace( "#434" );
...
);
self:finalize = Function( {},
self = Namespace( "#434" );
...
);
<MORE script="" is="" below="" me=""></MORE></SOME>
Cette fonction de méta-programmation définira `self` comme simplement `self = New Namespace()` puis injectera la self définition dans chaque fonction comme ci-dessus (le "#434" sera bien sûr différent à chaque fois).Une fois le script réécrit, procédez simplement comme suit (cela provient presque exactement de ma bibliothèque)
If( self << Contains( "initialize" ), Try( self:initialize(), Print( "Error with self:initialize" ); Show( exception_msg ); ); );
Eval(
Substitute(
Expr(
self:window = New Window( self:tool name,
<<ON close="">>" ) );
Try(
self:finalize
,
Print( "Error with self:finalize" );
Show( exception_msg );
);</ON>
Voici un exemple réel que j'ai dans mon script.L'entrée avant la méta-programmation est la suivante :
tool name = "Example";
initialize = Function( {},
{Default Local},
Show( "initialized" )
);
content = Function( {},
H List Box(
meta:Button Box( "Hello World", "run" )
,
self:n days box = meta:Number Edit Box( 30, "run" )
)
);
run = Function( {},
{var1, var2},
Show( "running" )
);
finalize = Function( {},
Show( "finalized" );
show( self:n days box << Get );
);
0;juste avant d'évaluer le script (après l'étape de méta-programmation) ça ressemble à ça
self:tool name = "Example";
self:initialize = Function( {},
{Default Local},
self = Namespace( "#58" );
Show( "initialized" );
);
self:content = Function( {},
{self, this},
self = Namespace( "#58" );
H List Box(
meta:Button Box( "Hello World", "run" )
,
self:n days box = meta:Number Edit Box( 30, "run" )
);
);
self:run = Function( {},
{var1, var2, self, this},
self = Namespace( "#58" );
Show( "running" );
);
self:finalize = Function( {},
{self, this},
self = Namespace( "#58" );
Show( "finalized" );
Show( self:n days box << Get );
);
0;Pour récapituler : la méta-programmation prend n'importe quel uscopé variable de niveau supérieur et la limite explicitement à « self ».Ensuite, il prend n'importe quelle fonction de niveau supérieur et ajoute une première ligne (qui est `self = Namespace( )`).C'est assez simple sur le plan conceptuel et il s'avère qu'avec JSL, c'est assez facile à mettre en œuvre.Il existe malheureusement plusieurs branches selon la forme de la fonction `Function()` (que ce soit 2 ou 3 arguments, que le 2ème argument soit {Default Local} ou simplement une liste de variables locales)
Alors, qu’est-ce que toute cette méta-programmation vous apporte ?Simplement pour éviter de faire quelque chose d'équivalent, comme ci-dessous.Notez que le script ci-dessous est principalement encapsulé dans les instructions Eval( Eval Expr( ... ) ).cela vous empêche de pouvoir utiliser de telles instructions dans l'une des fonctions et n'est donc pas idéal pour les grands projets.De plus, l'arborescence d'affichage a encore un passe-partout laid (le bloc < ) ... ) ) est grand et laid)
self = New Namespace();
self:tool name = "Example";
Eval( Eval Expr(
self:initialize = Function( {},
{Default Local},
self = Namespace( Expr( self << Get Name ) );
Show( "initialized" )
);
self:content = Function( {},
self = Namespace( Expr( self << Get Name ) );
H List Box(
Button Box( "Hello World",
<<SET function="">>" ) );
Try(
self:finalize
,
Print( "Error with self:finalize" );
Show( exception_msg );
);</SET>Ce script contient beaucoup de passe-partout mais est nécessaire lorsque vous souhaitez avoir un contrôle de portée plus explicite.Notamment lorsque vous ne souhaitez pas dire aux utilisateurs « N’ouvrez qu’une seule fenêtre à la fois ! ».Créer l'étape de méta-programmation est un peu fastidieux et demande certainement beaucoup d'apprentissage, mais une fois que vous l'avez compris, c'est le cas. très gratifiant et permet également un code beaucoup plus succinct et maintenable.
Ce que j'ai montré ici, c'est la possibilité de créer des classes (complètement distinctes des classes JMP14+ NewObject) qui fonctionnent très très bien et permettent à la génération d'interface utilisateur interactive d'être un jeu d'enfant.
Je sais que c'est beaucoup (et plus précisément, loin de toute approche initiale de création d'un script d'interface utilisateur), mais si vous créez plus d'un ou deux petits scripts d'interface utilisateur, le temps perdu pour créer la méta-programmation le wrapper (que vous ne devez faire qu’une seule fois) vaudra rapidement sa valeur.
Re : Aide JSL : aide sur la syntaxe de la commande Set Function()Salut @ErraticAttack ,
Merci pour vos commentaires. J'ai remarqué que c'est la deuxième fois que vous me recommandez le Namespace() ;-).
En conséquence, j'ai passé la dernière journée et demie à lire sur l'utilisation de NameSpace dans mon guide de script, qui est meilleur que l'index de script, mais ne donne toujours pas vraiment les meilleurs exemples de la façon dont vous l'utilisez.Avez-vous une introduction meilleure que le guide de script JMP 14 ou l'index de script sur la façon d'utiliser l'espace de noms dans le contexte de la façon dont vous l'utilisez ?
Je dois admettre que je ne suis pas tout à fait ce que vous faites et que j'ai besoin de mieux l'apprendre. Cependant, sur la base de votre message ici et de l'exemple de code que vous avez fourni dans l'un de mes autres messages ici , j'ai pu réécrire ma section de script qui appelle la première fenêtre en utilisant Namespace(). Mais je ne sais toujours pas vraiment comment je pourrais écrire dans les fenêtres et affichages suivants que je peux me mettre au travail grâce à mes compétences en script pas si grandes. Je pense qu'il faudra un certain temps pour apprendre à utiliser correctement Namespace avant de pouvoir passer à cette méthode programmatique, ce que j'aimerais faire.
J'ai pu mettre à jour le générateur de graphique de l'affichage en cochant/décochant la case pour masquer/exclure les données négatives (dans ce cas, cela n'a aucun sens). Le script ci-dessous fait ce que je veux (sauf mettre à jour la zone du tableau lorsque certaines données sont supprimées du graphique), mais je comprends que ce n'est pas idéal du point de vue de la programmation.
Merci!,
DS
Names Default To Here( 1 );
lbWidth = 168;
dt = Open( "$SAMPLE_DATA/Boston Housing.jmp" );
// Expression to store the current settings in global variables
recallRolesS1 = Expr(
::dtchosenRecall = dtchosen
);
// Expression to clear all current settings. KW: Clear
clearRoles = Expr(
Try(
colListY << RemoveAll;
ColListX << RemoveAll;
ColListW << RemoveAll;
ColListF << RemoveAll;
colListV << RemoveAll;
)
);
// Expression to store the current settings in global variables
recallRoles = Expr(
::ycolRecall = colListY << GetItems;
::xcolRecall = colListX << GetItems;
::WcolRecall = colListW << GetItems;
::FcolRecall = colListF << GetItems;
::vcolRecall = colListV << GetItems;
::max_spRecall = max_sp_input << get;
::partVPRecall = partVP_input << Get;
);
//Function to choose the data table to model. KW: choose_data
choose_data_table = Function( {},
list = {};
For( i = 1, i <= N Table(), i++,
list[i] = Data Table( i ) << get name
);
win = New Window( "Select a data table",
<<MODAL> Training R².",
<<SET font="" size=""> 1, Throw( "Too many items selected!\!rOnly one selection possible." ),
N Items( SelRows ) == 1,
SelRows = this << Get Selected Rows;
NPCs = SelRows[1];
);
dt_results << Clear Select;
dt_results << Select rows( SelRows );
)
);
);
Part = Expr(
If(
N Items( vcols ) == 1,
dt_results = New Table( "Partition_Results",
Add Rows( max_sp ),
New Column( "Splits", Numeric, Continuous ),
New Column( "Training RSquare", Numeric, Continuous ),
New Column( "Validation RSquare", Numeric, Continuous ),
New Column( "Training RASE", Numeric, Continuous ),
New Column( "Validation RASE", Numeric, Continuous ),
New Column( "RSquare Diff", Numeric, Continuous, Formula( "Training RSquare"n - "Validation RSquare"n ) ),
New Column( "Penalized Valid RSquare", Numeric, Continuous, Formula( "Training RSquare"n - 0.05 * "Splits"n ) ),
New Column( "Training N", Numeric, Continuous ),
New Column( "Validation N", Numeric, Continuous )
),
N Items( vcols ) == 0 & Is Missing( partVP ) == 0,
dt_results = New Table( "Partition_Results",
Add Rows( max_sp ),
New Column( "Splits", Numeric, Continuous ),
New Column( "Training RSquare", Numeric, Continuous ),
New Column( "Validation RSquare", Numeric, Continuous ),
New Column( "Training RASE", Numeric, Continuous ),
New Column( "Validation RASE", Numeric, Continuous ),
New Column( "RSquare Diff", Numeric, Continuous, Formula( "Training RSquare"n - "Validation RSquare"n ) ),
New Column( "Penalized Valid RSquare", Numeric, Continuous, Formula( "Validation RSquare"n - 0.05 * "Splits"n ) ),
New Column( "Training N", Numeric, Continuous ),
New Column( "Validation N", Numeric, Continuous )
),
N Items( vcols ) == 0,
dt_results = New Table( "Partition_Results",
Add Rows( max_sp ),
New Column( "Splits", Numeric, Continuous ),
New Column( "Training RSquare", Numeric, Continuous ),
New Column( "Training RASE", Numeric, Continuous ),
New Column( "Penalized Train RSquare", Numeric, Continuous, Formula( "Training RSquare"n - 0.05 * "Splits"n ) ),
New Column( "Training N", Numeric, Continuous )
)
);
For( i = 1, i <= max_sp, i++,
dt_results:"Splits"n[i] = i
);
dt_name = dtchosen << Get Name;
For( i = 1, i <= N Rows( dt_results ), i++,
Psplit = dt_results:"Splits"n[i];
str = Eval Insert(
"report = (dtchosen<<PARTITION></PARTITION></SET></MODAL>Re : Aide JSL : aide sur la syntaxe de la commande Set Function()Salut @ErraticAttack ,
D’après mon expérience, la portée variable est cohérente. Ce n’est peut-être pas intuitif, mais j’ai toujours trouvé que c’était prévisible.Si vous rencontrez des incohérences, cela vaut peut-être la peine de consulter le guide de script et d'essayer de vérifier la portée des différents symboles.
Dans certaines situations, j'ai eu beaucoup de succès en utilisant les fonctions contextuelles d'appel pour simplifier les appels de fonction, normalement pour une analyse exploratoire ou des scripts exécutés de manière interactive. Par exemple, une fonction de journalisation peut envoyer des messages à un fichier ou une fenêtre et inclure des valeurs d'itérateur ou d'étape sans avoir à les saisir/copier/coller à chaque fois.
Names default to here(1);
lg = function({msg},
write(msg || try(", i=" || char(i), "") || "\!N")
);
lg("starting loop");
local({i},for(i=1, i<= 5, i++, lg("done")))Re : Aide JSL : aide sur la syntaxe de la commande Set Function()Salut @pmroz ,
Merci pour les commentaires. Je cherchais simplement à mettre à jour la fenêtre actuelle et à ne pas en créer une nouvelle. J'ai réussi à faire fonctionner les choses pour le graphique afin qu'il se mette à jour sur l'état coché/décoché de la case à cocher(). Malheureusement, je n'arrive toujours pas à mettre à jour la zone de tableau contenant le tableau de données sous forme de rapport comme je le souhaiterais. Cette partie s’avère bien plus compliquée que je ne l’aurais pensé, mais elle n’est vraiment pas si importante. Mieux vaut que l'affichage graphique fonctionne comme prévu. Pourtant, ce serait bien si la zone de table pouvait être mise à jour.
Merci!,
DS
Re : Aide JSL : aide sur la syntaxe de la commande Set Function()Notez que vous ne pouvez pas compter sur la cohérence de la portée JSL sur les fonctions <
La plupart du temps, JSL n’a pas de contrôle de portée rigoureux comme l’aurait fait Python, par exemple.Pour tout type d'interface utilisateur interactive compliquée, vous devez toujours définir explicitement toutes les variables qui doivent persister sur plusieurs rappels d'éléments d'affichage.
De plus, pour des raisons à la fois esthétiques et pratiques de format de code, vous devez toujours mettre des virgules au niveau de la clôture englobante, comme ci-dessous.Cela permet une compréhension visuelle très rapide et facile des blocs de code.Notez l'indication visuelle simple de l'emplacement des différents blocs de l'instruction IF.
Pour finir, vous devez lier la fermeture de votre fenêtre d'interface utilisateur à la suppression de l'espace de noms en utilisant la méthode <
ns = <RELEVANT namespace="" with="" variables="" defined="" above="">
Eval( Eval Expr(
ns:cb << Set Function( {this},
ns = Namespace( Expr( ns << Get Name ) );
state = this << Get;
If(
state == 1,
ns:dt_results << Select Rows( ns:neg_rows ) << Hide and Exclude;
ns:dt_results << Clear Select;
,
state == 0,
ns:dt_results << Select Rows( ns:neg_rows ) << Unhide;
ns:dt_results << Select Rows( ns:neg_rows ) << Unexclude;
ns:dt_results << Clear Select;
);
);
) );</RELEVANT>Re : Aide JSL : aide sur la syntaxe de la commande Set Function()Essayez quelque chose comme ceci :
nw = new window("Test",
one_cb = checkbox({"Turn On"},
one_value = one_cb << Get;
If (one_value,
dt_results << Select Rows( neg_rows ) << Hide and Exclude;
dt_results << Clear Select;
dt_tb = dt_results << Get As Report();
// else
,
dt_results << Select Rows( neg_rows ) << Unhide;
dt_results << Select Rows( neg_rows ) << Unexclude;
dt_results << Clear Select;
dt_tb = dt_results << Get As Report()
);
)
);