Guida JSL: guida alla sintassi del comando Set Function()Ciao a tutti,
Torniamo con un'altra domanda sulla programmazione JSL. È correlato agli altri post tramite, ad es Qui .
Mi piacerebbe avere una funzionalità Check Box() che quando la casella di controllo è selezionata (stato=1), le righe selezionate (si tratta di righe in cui RSquare è negativo) vengono selezionate e quindi nascoste e rimosse in modo che il generatore di grafici aggiornato mostra l'output desiderato. Quindi, se la casella è deselezionata (stato=0), mostra e deesclude quelle righe. La chiamata Set Function() che sto utilizzando è riportata di seguito e funziona se eseguo manualmente lo "script" di Set Function(), ma non lo fa automaticamente. Non riesco a capire cosa mi manca affinché si aggiorni automaticamente. Ho trovato un post carino Qui , e sto cercando di farlo qualcosa come quello che ha fatto questa persona, ma per l'aggiornamento del generatore di grafici.
Chiamata della funzione Set JSL:
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;
);
);Dove cb è indicato in precedenza nel codice come:
cb = Check Box( "Remove bad points (*'s)?", <<SET></SET>
Avvio dell'output o ogni volta che la casella di controllo è stato = 0.:

Output desiderato ogni volta che la casella di controllo è stato = 1:

Sarebbe bello aggiornare anche la tabella dati come Report sulla destra in modo che tutte le righe escluse vengano rimosse dalla vista Report sulla destra, ma penso che potrebbe essere molto più difficile di quanto sembri. Ho provato ad aggiungere un ulteriore aggiornamento Ottieni come report nel comando Set Function(), ma non fa nulla.
Qualsiasi feedback è apprezzato, grazie!,
DS
Combinazione e pulizia dei datiOggetto: Guida JSL: guida alla sintassi del comando Set Function()Nel mio lavoro quotidiano, mi trovo ad affrontare più il problema opposto:
Non: 2 finestre che condividono lo stesso spazio dei nomi
ma: 2 script che solo a volte *) condividono un comune Qui Spazio dei nomi.
Quando includo un file JSL in un altro, in fase di esecuzione , lo script incluso condivide il file Qui spazio dei nomi del file jsl principale.
Ma quando voglio eseguire il debug di alcune righe di codice ed eseguirle dal secondo script, come posso fornire il file incluso script_B accesso al Qui spazio dei nomi dello script principale? 2 secondi fa, quando ho eseguito il codice di script_B, aveva semplicemente accesso a quello spazio dei nomi.
https://community.jmp.com/t5/Discussions/Debugging-Jmp-applications/mp/701121/highlight/true#M88566
Re: JSL Help: Guida alla sintassi del comando Set Function()Grazie, @ErraticAttack, per il bellissimo esempio di come funziona lo scoping in Jmp.
Mi ha aiutato molto ad avvicinarmi alla comprensione...
Finora non sono riuscito a capire cosa intendesse @drewfoglia con

in Scripting essenziale per efficienza e riproducibilità: fare meno per fare di più (2019-US-TUT-290)
Il tuo esempio dimostra chiaramente che ciò non è vero.
Perché la nostra esperienza qui differisce dall'affermazione di Drew?
Penso che ciò sia dovuto al fatto che le finestre vengono create dalla stessa finestra dell'editor JSL.
Dopo aver aperto la prima finestra TEST, è molto utile che la finestra dello script abbia ancora accesso agli oggetti referenziati teb e tb .
Dopo aver aperto la seconda finestra TEST, teb e tb fanno ora riferimento agli oggetti nella seconda finestra, il che apparentemente compromette la funzionalità della prima finestra (il testo immesso viene visualizzato in tb , cioè nella seconda finestra).
Quindi, collegando entrambe le affermazioni, cosa succede se entrambe le Windows non condividono un editor JSL comune?
-> Entrambi avranno un NameSpace indipendente, come annunciato da Drew nel suo intervento.
Opzioni per farlo:
a) chiudi lo script e riaprilo, quindi crea la seconda finestra
b) richiamare lo script da un menu:

Oggetto: Guida JSL: guida alla sintassi del comando Set Function()CIAO @ErraticAttack ,
Grazie per la risposta e il post dettagliato. Mi ci vorrà sicuramente un po' di tempo per risolverlo, dato che non sono un programmatore professionista, gran parte di questo è ancora nuovo per me.
Grazie!,
DS
Oggetto: Guida JSL: guida alla sintassi del comando Set Function()@ih , come esempio di JMP che non esegue l'ambito come ci si potrebbe aspettare (specialmente se si proviene da Python, ad esempio), è sufficiente avviare due finestre da questo script: i nomi si collegano solo alla seconda finestra, non alla prima.Questo è il problema più grande che ho riscontrato che le persone hanno con l'ambito (ma non l'unico problema).JMP fornisce alcuni pseudo-spazi dei nomi utili, come lo spazio dei nomi window:.Tuttavia, queste non sono variabili dello spazio dei nomi corrette e non funzionano completamente come previsto negli script di grandi dimensioni.
Names Default to Here( 1 );
New Window( "TEST",
V List box(
teb = Text Edit Box( "", <<SET hint=""></SET>
@SDF1 , per l'ambito esplicito dello spazio dei nomi quello che ho fatto è creare una libreria che esegue tutte le noiose operazioni di iniezione dell'ambito tramite meta-programmazione.Ciò ha l'ulteriore vantaggio di poter definire le classi quasi esattamente come funzionano in Python (meno le comode sostituzioni di ricerca).Non posso condividere la libreria poiché fa parte del mio lavoro aziendale, ma essenzialmente questo è ciò che sta accadendo:
Supponiamo che tu abbia uno script semplice come questo
Names Default to Here( 1 );
/*
Doing something initialization here!!
*/
dt = Current Data Table();
New Window( "Super Useful Script",
,
<<ON close=""></ON>
Questo script ha due elementi dell'interfaccia utente interattivi, alcune inizializzazioni e potenzialmente alcune operazioni di pulizia alla chiusura della finestra.Si noti che concettualmente l'albero di visualizzazione e la logica sono mescolati e ci sono blocchi molto profondi.Il codice ha un aspetto migliore (ed è più facile da mantenere) quando elementi concettualmente distinti sono distinti anche all'interno del codice.Sarebbe carino creare semplicemente una casella di pulsanti con un titolo e un nome di funzione da chiamare.La funzione che verrà chiamata sarà quindi una funzione di primo livello (che potrà essere facilmente chiamata altrimenti da qualsiasi parte dello script) e sarà separata dall'albero di visualizzazione.
Puoi trasformarlo in qualcosa di _molto_ più facile da digerire e con cui lavorare, simile al seguente:
/* 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" )
);Ora chiaramente questo script non farà molto se si preme il normale pulsante di esecuzione dello script JSL.Ciò che devi fare è definire prima il meta namespace (che contiene il wrapper ButtonBox e TextBox in modo che possano collegarsi bene con il resto dello script) e definire una funzione wrapper di meta-programmazione che farà quanto segue:
Prendi uno script prototipo come questo
<SOME script="" is="" above="" me="">
do someting = Function( {},
...
);
finalize = Function( {},
...
);
<MORE script="" is="" below="" me=""></MORE></SOME>Trasformalo in questo prototipo:
<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>
Questa funzione di meta-programmazione definirà "self" semplicemente come "self = New Namespace()" e quindi inserirà la definizione di sé in ciascuna funzione come sopra (il "#434" ovviamente sarà diverso ogni volta).Dopo che lo script è stato riscritto, procedi semplicemente come segue (è preso quasi esattamente dalla mia libreria)
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>
Ecco un esempio reale che ho nella mia sceneggiatura.L'input prima della meta-programmazione è questo:
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;appena prima di valutare lo script (dopo la fase di metaprogrammazione) appare così
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;Ricapitolando: la metaprogrammazione ne accetta qualsiasi uscopato variabile di livello superiore e ne definisce esplicitamente l'ambito "self".Quindi prende qualsiasi funzione di livello superiore e aggiunge una prima riga (che è `self = Namespace( )`).Questo è piuttosto semplice concettualmente e risulta che con JSL è abbastanza facile da implementare.Sfortunatamente ci sono diversi rami a seconda della forma della funzione `Function()` (se si tratta di 2 o 3 argomenti, se il 2° argomento è {Default Local} o semplicemente un elenco di variabili locali)
Allora, cosa ti porta tutta questa meta-programmazione?Semplicemente per evitare di fare qualcosa di equivalente, come di seguito.Si noti che lo script seguente è per lo più racchiuso nelle istruzioni Eval( Eval Expr( ... ) ).questo impedisce di poter utilizzare tali istruzioni all'interno di qualsiasi funzione e quindi non è l'ideale per progetti di grandi dimensioni.Inoltre, l'albero di visualizzazione ha ancora dei brutti parametri (il blocco < ) ... ) ) è grande e brutto)
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>Questo script contiene molte premesse ma è necessario quando si desidera avere un controllo dell'ambito più esplicito.In particolare, quando non vuoi dire agli utenti "Apri solo una finestra alla volta!".Creare la fase di metaprogrammazione è un po' noioso e richiede sicuramente molto apprendimento, ma una volta che lo hai capito è molto gratificante e consente anche un codice molto più conciso e gestibile.
Ciò che non ho mostrato qui è la capacità di creare classi (completamente separate dalle classi JMP14+ NewObject) che funzionano molto bene e consentono la generazione dell'interfaccia utente interattiva un gioco da ragazzi.
So che questo è molto (e più precisamente, lontano da qualsiasi approccio iniziale alla creazione di uno script dell'interfaccia utente), ma se stai creando più di uno o due script dell'interfaccia utente di piccole dimensioni, il tempo sprecato creando la meta-programmazione wrapper (che devi fare solo una volta) varrà presto la pena.
Oggetto: Guida JSL: guida alla sintassi del comando Set Function()CIAO @ErraticAttack ,
Grazie per il tuo feedback Ho notato che questa è la seconda volta che mi consigli Namespace() ;-).
Di conseguenza, ho passato l'ultimo giorno e mezzo a documentarmi sull'uso di NameSpace nella mia guida allo scripting, che è migliore dello Scripting Index, ma non fornisce ancora i migliori esempi di come lo stai utilizzando.Hai qualche guida migliore della Guida allo scripting di JMP 14 o dell'indice di scripting su come utilizzare lo spazio dei nomi nel contesto in cui lo stai utilizzando?
Devo ammettere che non seguo del tutto quello che stai facendo e ho bisogno di impararlo meglio. Tuttavia, in base al tuo post qui e al codice di esempio che hai fornito in uno dei miei altri post Qui , sono stato in grado di riscrivere la mia sezione di script che chiama la prima finestra utilizzando Namespace(). Ma non sono ancora del tutto sicuro di come scriverei nelle finestre e nei display successivi che riesco a far funzionare grazie alle mie non così eccezionali capacità di scripting. Penso che ci vorrà del tempo per imparare come utilizzare correttamente Namespace prima di poter passare a quel metodo programmatico, cosa che mi piacerebbe fare.
Sono riuscito a far aggiornare il generatore di grafici del display selezionando/deselezionando la casella per nascondere/escludere i dati negativi (in questo caso privi di significato). Lo script seguente fa quello che voglio (tranne l'aggiornamento del riquadro della tabella quando determinati dati vengono rimossi dal grafico), ma capisco che non è l'ideale dal punto di vista della programmazione.
Grazie!,
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>Oggetto: Guida JSL: guida alla sintassi del comando Set Function()CIAO @ErraticAttack ,
Dalla mia esperienza, l'ambito variabile è coerente. Potrebbe non essere intuitivo, ma l'ho sempre trovato prevedibile.Se riscontri incoerenze, potrebbe valere la pena approfondire la guida allo scripting e provare a effettuare un controllo incrociato sull'ambito dei diversi simboli.
In alcune situazioni, ho avuto molto successo utilizzando le funzioni del contesto chiamante per semplificare le chiamate di funzione, normalmente per analisi esplorative o script eseguiti in modo interattivo. Ad esempio, una funzione di registro potrebbe inviare messaggi a un file o a una finestra e includere valori di iteratori o passaggi senza doverli digitare/copiare/incollare ogni volta.
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")))Oggetto: Guida JSL: guida alla sintassi del comando Set Function()CIAO @pmroz ,
Grazie per il feedback. Stavo cercando semplicemente di aggiornare la finestra corrente e di non crearne una nuova. Sono riuscito a far funzionare le cose per il grafico in modo che si aggiorni sullo stato di selezione/deselezionazione della casella di controllo(). Sfortunatamente, non riesco ancora a ottenere la casella della tabella che contiene la tabella dati come report da aggiornare come vorrei. Questa parte si sta rivelando molto più complicata di quanto pensassi, ma in realtà non è poi così importante. È meglio che il display grafico funzioni come previsto. Tuttavia, sarebbe bello se la casella della tabella potesse essere aggiornata.
Grazie!,
DS
Oggetto: Guida JSL: guida alla sintassi del comando Set Function()Tieni presente che non puoi fare affidamento sul fatto che l'ambito JSL sia coerente con le funzioni <
Principalmente è che JSL non ha un controllo rigoroso dell'ambito come, ad esempio, avrebbe Python.Per qualsiasi tipo di interfaccia utente interattiva complessa è necessario sempre definire esplicitamente l'ambito di tutte le variabili che devono essere persistenti tra più callback di elementi di visualizzazione.
Inoltre, per una questione sia estetica che pratica del formato del codice, dovresti sempre inserire delle virgole a livello della recinzione che lo racchiude, come di seguito.Ciò consente una comprensione visiva molto rapida e semplice dei blocchi di codice.Notare la semplice indicazione visiva di dove si trovano i diversi blocchi dell'istruzione IF.
Come nota finale: dovresti collegare la chiusura della finestra dell'interfaccia utente con l'eliminazione dello spazio dei nomi utilizzando il metodo <
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>Oggetto: Guida JSL: guida alla sintassi del comando Set Function()Prova qualcosa del genere:
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()
);
)
);