Introduzione
Dopo aver studiato alcuni concetti base (il quoting, l’uso delle parentesi, il significato di simbolo, cosa sono le funzioni, etc…) passiamo ad esplorare le funzionalita` che influenzano il flusso di un software scritto in newLISP.
Inizieremo analizzando le funzioni if, switch/case, loop, etc… indispensabili per produrre un codice non lineare, in grado cioe` di rispondere in modo diverso a seconda degli input ricevuti.
Le condizioni: if, cond, case
Sviluppando un software troverete senz’altro la necessita` di operare delle scelte, in base all’input dell’utente, oppure in base al risultato di un calcolo, etc… Alcune volte le scelte si limitano a bivi del tipo CONDIZIONE -> Se vera allora… -> Se falsa allora… ma in altri casi potremmo avere la necessita` di controlli multipli (se la variabile X vale 1 allora… se la variabile X vale 2 allora… se la variabile X vale 3 allora… ).
newLISP mette a disposizione diversi strumenti adatti allo scopo, uno dei quali e` stato inserito proprio in una delle piu` recenti versioni (la funzione when).
La funzione ( if )
La funzione ha la seguente sintassi: ( if condizione codice_se_vero codice_se_falso )
L’argomento codice_se_falso e` opzionale.
Vediamo un esempio pratico (leggetelo con attenzione, perche` include una particolarità che vi svelerò subito dopo):
(if (= "newlisp" "NEWLISP") (println "Stringhe UGUALI!") (println "Stringhe DIVERSE!") );if >> Stringhe DIVERSE!
Come potete notare, la if accetta 3 argomenti, di cui il primo e` la condizione, il secondo e` valutato se il risultato della condizione e` vera, mentre il terzo e` valutato se il risultato della condizione e` falso.
Vediamo altri due brevi esempi, e poi analizziamo la funzione:
(if 12 "Ok" nil );if
In questo caso, la condizione e` il numero 12. Cio` e` possibile perche` in newLISP ogni valore diverso da nil e` considerato vero, TRUE. Quindi… (leggeteli con attenzione: queste definizioni hanno un grande potenziale!):
* “pippo” -> vero;
* (1 2 3) -> vero;
* 0 (il numero zero) -> vero;
* nil -> falso;
* (list nil) -> vero;
* (= nil ‘(nil) ) -> falso;
* (= nil (list nil) ) -> falso;
* (= nil ‘() ) -> falso (la lista vuota e` diversa da nil);
La condizione vera (nell’esempio precedente) e` “Ok” (questo valore verra` restituito nello “stack” di newLISP, vedremo in seguito cosa e` lo stack come sfruttarlo!), invece la condizione falsa e` nil.
Vediamo quest’ultimo esempio (e poi formalizzeremo alcune definizioni):
(setq valore 10)
(if (= valore 10)
(begin
(inc 'valore)
(println (string "VERO: " valore) )
)
(begin
(dec 'valore)
(println (string "FALSO: " valore) )
)
)
Osservando il piccolo programma, potete notare che, dovendo eseguire piu` di una funzione nella condizione vera/falsa, ho dovuto usare la funzione (begin). Questo perche` l’istruzione if accetta solo tre argomenti… quindi il (begin) e` necessario per raggruppare tutto il blocco di funzioni e passarlo alla if come un unico argomento. Molto simile al (begin) esiste anche (silent). La differenza consiste nel fatto che il secondo “uccide” tutti i valori scritti nello stack dalle funzioni all’interno del blocco stesso. Tanto per esser chiari, ecco un breve esempio:
> (begin 10) 10 > (silent 10) >
Il begin ritorna il valore 10 (messo nello stack), mentre il silent non ritorna nulla.
La funzione if vi permette di gestire condizioni vere e false, ma ovviamente potete scrivere anche solo la condizione vera:
(if 1
(begin
(print "CONDIZIONE ")
(println "VERA")
);begin
);if
>> CONDIZIONE VERA
Se dovete usare la sola condizione vera, potete usare anche la funzione when, che vi evitera` la noia dell’uso del begin, in caso di funzioni multiple. Ecco un esempio:
;;; USANDO LA IF...
(if 1
(begin
(println "riga 1")
(println "riga 2")
);begin
);if
;;; USANDO LA WHEN...
(when 1
(println "riga 1")
(println "riga 2")
)
Ecco alcuni brevi ma significativi esempi presi direttamente dal manuale “Introduction to newLISP” (utili per comprendere al meglio come newLISP valuta le espressioni):
(if x 1) ; if x is true, return the value 1 (if 1 (launch-missile)) ; missiles are launched, because 1 is true (if 0 (launch-missile)) ; missiles are launched, because 0 is true (if nil (launch-missile)) ;-> nil, there’s no launch, because nil is false (if ’() (launch-missile)) ;-> (), and the missiles aren’t launched (if (> 4 3) (launch-missile)) ;-> it’s true that 4 > 3, so the missiles are launched (if (> 4 3) (println "4 is bigger than 3")) "4 is bigger than 3" (if snark (launch-missile)) ;-> nil ; symbol has no value that I can find (if boojum (launch-missile)) ;-> nil ; can’t find a value for this symbol (if untrue (launch-missile)) ;-> nil ; can’t find a value for this symbol either (if false (launch-missile)) ;-> nil ; never heard of it, and it doesn’t have a value anyway
La if accetta anche condizioni multiple, in modo simile alla switch/case:
(if (< x 0) (set ’a "impossible") (< x 10) (set ’a "small") (< x 20) (set ’a "medium") (>= x 20) (set ’a "large") );if
TIPS & TRICKS: io adoro newLISP per la flessibilità della sintassi, e per le possibilita` che offre. Ogni giorno, pur conoscendo il linguaggio discretamente bene, scopro un nuovo trucco, un nuovo modo di risolvere un problema. Siamo di fronte ad uno di questi… Osservate il seguente codice:
> (set ’x 1) > ( (if (< x 5) + *) 3 4 ) 7
Che cosa e` successo?! Come abbiamo anticipato nelle precedenti sessioni, newLISP considera il primo valore di una lista come speciale, in quanto, se valutato, verra` interpretato come il nome di una funzione. Nell’esempio, quindi, la if ritorna nello stack il simbolo della funzione somma + oppure del prodotto *. Questo verra` usato come primo elemento della lista. I parametri successivi sono 3 e 4. Quindi, se x<5 viene creata la lista (+ 3 4) altrimenti (se la condizione fosse falsa) viene creata la lista (* 3 4). Geniale!
Questo e` uno splendido esempio di come sfruttare la dualità simbolo<->funzione che puo` essere creata nei linguaggi funzionali come newLISP (funzionalita` simili sono applicabili con Rebol).
La funzione ( cond )
E` un “incrocio” tra una if estesa. La sua sintassi e` la seguente:
(cond (test action1 action2 ...) (test action1 action2 ...) (test action1 action2 ...) ... )
La funzione non prevede la condizione else, ma la cond e` molto utile al posto della if onde evitare l’uso intensivo di (begin) oppure (silent).
Ecco un esempio:
(cond ( (= x 0) (set ’a "X vale 0") (println "ZERO") ) ( (= x 1) (set ’a "X vale 1") (println "UNO") ) ( (> x 1) (set ’a "X e` maggiore di 1") (println "> 1") ) );cond
La funzione ( case )
Questa funzione e` molto simile alle “tradizionali” funzioni switch/case.
Cio` che la differenzia dalla cond e` che la condizione non viene valutata, ma deve essere solo un elemento di confronto.
Esempio:
(case 2 (1 (print "Valore: ")(println 1) ) (2 (print "Valore: ")(println 2) ) (3 (print "Valore: ")(println 3) ) )
Il primo elemento (il numero 1) rappresenta il valore da confrontare con il “campione” (che nel nostro caso vale 2). Se il confronto e` positivo, allora vengono eseguite le funzioni restanti.
Non potrete pero` usare una espressione per il confronto (come invece abbiamo fatto per la cond). Quindi il 2 non puo` essere sostituito con (+ 1 1) in quanto il numero 2 e` diverso da ‘(+ 1 1). L’apice mette in evidenza che la lista non viene valutata, quindi il confronto non avviene con il risultato della funzione (+) ma con la lista stessa!
La condizione else (cioe` la condizione che verra` eseguita se nessuna delle condizioni precedenti venisse soddisfatta), si scrive come:
(true (println "Quando tutte le altre sono false..."))
Quindi avremo:
(case 2 (1 (print "Valore: ")(println 1) ) (2 (print "Valore: ")(println 2) ) (3 (print "Valore: ")(println 3) ) (true (println "Quando tutte le altre sono false...")) )
Cio` e` possibile perche` la case termina il confronto non appena incontra una condizione vera. Se nessuna delle condizioni precedenti l’ultima (true) e` verificata, allora verra` eseguita certamente quest’ultima (perche` sicuramente vera!).
Ok, anche stavolta abbiamo finito. Abbiamo ancora moltissimi argomenti da affrontare, ma pian piano stiamo imparando ad usare tutti gli strumenti messi a disposizione da questo linguaggio.
Una nota soprattutto per coloro che provengono dai linguaggi imperativi (come me!): usando newLISP, non limitatevi ad imparare nuovi token, nuove funzioni per scrivere un programma in questo linguaggio, ma imparate a pensare in modo diverso! Un linguaggio funzionale, rispetto ad un linguaggio imperativo, non vi mette a disposizione solamente un nuovo set di funzioni, ma dischiude un mondo nuovo, e cio` richiede un approccio differente. Se riuscirete in questa impresa, allora si che sarete in grado di vedere MATRIX!
Il vero spirito di un linguaggio funzionale. Come dice qualcuno… think different!
Translate To English!
Commenti Recenti