Vorrei condividere un pò della mia conoscenza di EasyUO con il resto della comunità di UODreams. Invito altri scripters a fare altrettanto.
Sia chiaro che questo non è un thread per assoluti principianti: si suppone che i lettori abbiano già una buona conoscenza delle caratteristiche più comuni del linguaggio.
Iniziamo con un argomento che mi sta molto caro: la sub "safecall", e tutto ciò che gira intorno ad essa.
Safecall
Problema:
Si vorrebbe fare in modo che ogni sub abbia un proprio namespace locale, nel quale poter allocare variabili in assoluta sicurezza (come succede in TUTTI i linguaggi di programmazione moderni).
Tuttavia, la gestione dei namespace di EasyUO è ORRIBILE. E presto non se ne può più di scrivere
Oltre che essere noioso, tale approccio impedisce l'uso delle return anticipate, che sono estremamente comode.
Per chi non fosse pratico di namespaces: un namespace permette di utilizzare variabili locali. Le variabili locali sono variabili scritte con il punto esclamativo (esempio: !variabile). Una variabile locale esiste solo all'interno del suo namespace, e viene distrutta quando si richiama "namespace clear". Questo serve ad evitare conflitti di nomi fra le variabili (da qui il termine "namespace", ovvero "spazio dei nomi").
Soluzione:
Anziché richiamare direttamente la sub interessata...
...si potrebbe richiamare una sub intermedia, che si occupi di settare correttamente il namespace e che, successivamente, richiami la sub interessata. Il nome della sub interessata diventerà il primo argomento della sub intermedia, dando luogo ad un codice di questo tipo:
safecall è la sub intermedia di supporto. Come è scritta questa sub? Eccovi un esempio:
Utilizzando questa sub per richiamare indirettamente tutte le altre, vi sarà possibile EVITARE COMPLETAMENTE di utilizzare in namespace in maniera esplicita all'interno delle altre sub. Dovete solo prendere l'abitudine di scrivere sempre "safecall" subito dopo "gosub".
Quando non funziona
Purtroppo, per come è fatto EasyUO, non c'è modo di scrivere una safecall che accetti un numero illimitato di argomenti. Personalmente posso dirvi che il 99% delle sub non supera i 10 argomenti di input. E quell'1% potete sempre gestirlo "alla vecchia maniera".
Un vantaggio curioso
Come "effetto collaterale" di questo sistema, le vostre ricerche all'interno del codice saranno più semplificate!
Il più diffuso dei miei script, per esempio, è lungo circa 10.000 righe! L'unico modo per spostarsi da una sub all'altra è usare Ctrl+F (Edit->Find). Ma se cerco "sub miaSub", trovo anche tutte le righe del tipo "gosub miaSub"!!!
Da quando ho cominciato ad usare "safecall", questo problema è sparito da solo. Adesso, le righe con "gosub safecall miaSub" non vengono selezionate quando cerco "sub miaSub", così trovo il corpo della sub immediatamente!
Codice Completo
Il codice che segue costituisce l'inizio di tutti i miei script. Oltre a dichiarare la sub safecall, rende anche lo script una libreria. Chiunque voglia richiamare dall'esterno una sub all'interno dello script, può usare:
Non c'è bisogno di inserire "safecall" prima di "nomeSub" ma, anche se lo si fà, non fa differenza. Affronteremo l'argomento della scrittura di librerie nel prossimo post, al quale vi rimando per la comprensione di certe sezioni del codice seguente:
Se non volete mal di testa, fate semplicemente copia-ed-incolla di questo codice all'INIZIO del vostro script, ed usate "safecall" esattamente come ho spiegato.
Sia chiaro che questo non è un thread per assoluti principianti: si suppone che i lettori abbiano già una buona conoscenza delle caratteristiche più comuni del linguaggio.
Iniziamo con un argomento che mi sta molto caro: la sub "safecall", e tutto ciò che gira intorno ad essa.
Safecall
Problema:
Si vorrebbe fare in modo che ogni sub abbia un proprio namespace locale, nel quale poter allocare variabili in assoluta sicurezza (come succede in TUTTI i linguaggi di programmazione moderni).
Tuttavia, la gestione dei namespace di EasyUO è ORRIBILE. E presto non se ne può più di scrivere
codice:
sub mySub namespace push namespace local mySub , #random , #systime [...] set #result xyz [...] namespace clear namespace pop return #result
Per chi non fosse pratico di namespaces: un namespace permette di utilizzare variabili locali. Le variabili locali sono variabili scritte con il punto esclamativo (esempio: !variabile). Una variabile locale esiste solo all'interno del suo namespace, e viene distrutta quando si richiama "namespace clear". Questo serve ad evitare conflitti di nomi fra le variabili (da qui il termine "namespace", ovvero "spazio dei nomi").
Soluzione:
Anziché richiamare direttamente la sub interessata...
codice:
gosub mySub arg1 arg2 arg3
codice:
gosub safecall mySub arg1 arg2 arg3
codice:
sub safecall ; %0: numero_TOTALE_argomenti; %1: nome_sub; %2: argomento1; %3: argomento2; namespace push namespace local safecall , #systime , #random ; genera in namespace con un nome casuale set %0 %0 - 1 ; converte dal numero di argomenti passati a safecall ; al numero di argomenti della sub da richiamare goto _safecall_ , %0 ; si sposta nella label contrassegnata da _safecall_<numero_argomenti_sub_da_richiamare> ; si arriva qui solo se la label non è riconosciuta (numero args errato o eccessivo) event exmsg #charid 3 0 ERRORE! goto _safecall_out _safecall_0: ; la sub viene richiamata con 0 argomenti gosub %1 goto _safecall_out _safecall_1: ; la sub viene richiamata con 1 argomento, situato in %2 gosub %1 %2 goto _safecall_out _safecall_2: ; la sub viene richiamata con 2 argomenti, situati in %2 e %3 gosub %1 %2 %3 goto _safecall_out [...] _safecall_10: ; la sub viene richiamata con 10 argomenti, da %2 a %11 gosub %1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 goto _safecall_out _safecall_out: ; dopo che la sub è stata richiamata, provvedi a rimuovere il namespace namespace clear namespace pop return #result ; restituisci lo stesso #result della sub richiamata
Quando non funziona
Purtroppo, per come è fatto EasyUO, non c'è modo di scrivere una safecall che accetti un numero illimitato di argomenti. Personalmente posso dirvi che il 99% delle sub non supera i 10 argomenti di input. E quell'1% potete sempre gestirlo "alla vecchia maniera".
Un vantaggio curioso
Come "effetto collaterale" di questo sistema, le vostre ricerche all'interno del codice saranno più semplificate!
Il più diffuso dei miei script, per esempio, è lungo circa 10.000 righe! L'unico modo per spostarsi da una sub all'altra è usare Ctrl+F (Edit->Find). Ma se cerco "sub miaSub", trovo anche tutte le righe del tipo "gosub miaSub"!!!
Da quando ho cominciato ad usare "safecall", questo problema è sparito da solo. Adesso, le righe con "gosub safecall miaSub" non vengono selezionate quando cerco "sub miaSub", così trovo il corpo della sub immediatamente!
Codice Completo
Il codice che segue costituisce l'inizio di tutti i miei script. Oltre a dichiarare la sub safecall, rende anche lo script una libreria. Chiunque voglia richiamare dall'esterno una sub all'interno dello script, può usare:
codice:
call NomeScript.txt nomeSub arg1 arg2 arg3
codice:
;=========================================================================================== ;* @name Intestazione standard di inizio script. ;* @purpose Si assicura che lo script possa essere richiamato anche dall'esterno, ;* e che configuri correttamente i namespaces. ;* ;* @example call NomeScript.txt ; richiama lo script in modalità "standalone" ;* call NomeScript.txt main ; richiama la procedura principale dello script ;* call NomeScript.txt nomesub ; richiama una particolare sub dello script ;* ; (per finalità di testing o di libreria) ;* ;* @warning E' importante terminare lo script con gosub quit, anzichè con halt o exit! ;* if %0 = !null { namespace push namespace local ScriptName_standalone set !lpc #lpc set #lpc 500 gosub _indirectInvokeEx 0 main } else { namespace push namespace local ScriptName_reentrant set !lpc #lpc set #lpc 500 if %0 = 0 { gosub _indirectInvokeEx 0 main } else { set %0 ( %0 - 1 ) gosub _indirectInvokeEx %0 %1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 } } gosub quit sub quit while ( #nsname <> ScriptName_standalone ) && ( #nsname <> ScriptName_reentrant ) { namespace clear namespace pop } if #nsname = ScriptName_standalone { halt } set #lpc !lpc namespace clear namespace pop exit #result ;=============================================================================== ;* @name _indirectInvokeEx ;* @ver 1.0 (09/04/2009) ;* @author AG ;* @purpose Indirectly calls a specified sub, creating a namespace for it. ;* ;* @params %1 req number of arguments passed to the given sub ;* %2 req name of the sub to call ;* %3...%12 opt arguments to pass to the given sub (maximum of 10) ;* @returns #result of the sub ;* ;* @example gosub _indirectInvokeEx 3 mysub arg1 arg2 arg3 ;* @status Tested and working. ;* sub _indirectInvokeEx ; %argc(of given sub) %subname %args[10] namespace push namespace local indirectInvoke_ , #systime , #random goto _indirectInvokeEx_ , %1 ; si arriva qui solo se la label non è riconosciuta (numero args errato) event exmsg #charid 3 0 ATTENZIONE! Il numero degli argomenti di _indirectInvokeEx è errato ( %1 )! Lo script andrà in pausa. pause goto _indirectInvokeEx_out _indirectInvokeEx_0: gosub %2 goto _indirectInvokeEx_out _indirectInvokeEx_1: gosub %2 %3 goto _indirectInvokeEx_out _indirectInvokeEx_2: gosub %2 %3 %4 goto _indirectInvokeEx_out _indirectInvokeEx_3: gosub %2 %3 %4 %5 goto _indirectInvokeEx_out _indirectInvokeEx_4: gosub %2 %3 %4 %5 %6 goto _indirectInvokeEx_out _indirectInvokeEx_5: gosub %2 %3 %4 %5 %6 %7 goto _indirectInvokeEx_out _indirectInvokeEx_6: gosub %2 %3 %4 %5 %6 %7 %8 goto _indirectInvokeEx_out _indirectInvokeEx_7: gosub %2 %3 %4 %5 %6 %7 %8 %9 goto _indirectInvokeEx_out _indirectInvokeEx_8: gosub %2 %3 %4 %5 %6 %7 %8 %9 %10 goto _indirectInvokeEx_out _indirectInvokeEx_9: gosub %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 goto _indirectInvokeEx_out _indirectInvokeEx_10: gosub %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12 goto _indirectInvokeEx_out _indirectInvokeEx_out: namespace clear namespace pop return #result ;================================================================================== ;* @name safecall ;* @author AG ;* @purpose Simplified version of _indirectInvokeEx, for internal use by the script. ;* Wraps a namespace around the given sub. ;* ;* @params %1 req name of the sub to call ;* %2...%11 opt arguments to pass to the given sub (maximum of 10) ;* ;* @returns #result of the sub ;* ;* @example gosub safecall mysub arg1 arg2 arg3 ;* @status Tested and working. sub safecall set %0 %0 - 1 gosub _indirectInvokeEx %0 %1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 return #result ;================================================================================== ;* @name main ;* @purpose Application entry-point. sub main ; Questo è il punto d'ingresso dello script. return
Commenta