Controllo del flusso: if, cond, case

19 02 2008

google_translate.gif Translate To English!

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!






Getters & setters

13 02 2008

google_translate.gif Translate To English!

Introduzione

Nella programmazione OOP (Object Oriented Programming), per “isolare” il codice, creare un layer (strato) di astrazione della classe stessa, etc… si usa generare i famosi getters/setters (le parole vengono dai prefissi che si usano dare alle funzioni in oggetto: get… e set… ). Di cosa si tratta? E perche` ne parliamo qui, in una rubrica dedicata a newLISP, che e` un linguaggio funzionale e non ad oggetti?

Di cosa si tratta?

Un esempio vale piu` di mille parole, quindi scriviamo un “pezzetto” di codice che illustri l’uso dei getters/setter (lo scriviamo in un generico linguaggio ad oggetti):

class MiaClass {
  private miaVariabile = 0;

  // Getters/setters
  public getMiaVariabile() {
    return miaVariabile;
  }

  public setMiaVariabile(argVar) {
    miaVariabile = uppercase(argVar);
  }
}

L’esempio e` banale, ma rende bene l’idea dell’importanza di questi due metodi: getMiaVariabile() e setMiaVariabile(). Il loro scopo e` “isolare” la variabile miaVariabile, cosi` da non renderla accessibile direttamente. Normalmente i getters/setters si occupano di fare controlli, verifiche, conversioni, etc… prima di leggere/scrivere la variabile stessa (nel nostro caso convertiamo in maiuscolo ogni stringa assegnata alla variabile). Sono una sorta di filtro, che impedisce (o dovrebbero impedire…) che nella variabile in questione finiscano dati impropri. Inoltre permettono una “lavorazione” del dato prima che venga restituito all’utilizzatore (usando il getter). Inoltre, questo livello di isolamento, permette di fare dei cambiamenti interni alla classe che verranno poi “nascosti” dai getter/setters. Risultato? Si migliora il concetto di “black-box”, tanto caro alla programmazione ad oggetti.

… e newLISP cosa c’entra?!

L’idea di poter isolare il codice scritto in un contesto (context) e` abbastanza attraente, inoltre scrivere un getter/setter per newLISP e` una operazione terribilmente semplice! Non solo, ma newLISP permette di implementare, in modo semplice ed efficace, qualcosa di piu`: realizzare una sola funzione che faccia, in modo trasparente, da getter e da setter! Questo grazie all’uso degli argomenti opzionali delle funzioni, e dei parametri di default. Vediamo come :-)

(context 'ContestoProva)

(setq _miaVariabile 0) 

(define (mia-variabile (argVar nil) )
   (if argVar
      (setq _miaVariabile argVar) ; SETTER
      _miaVariabile               ; GETTER
   );if
);define

(context MAIN)

Il funzionamento e` molto semplice: creo un nuovo context (chiamato ContestoProva), poi creo una variabile interna (la variabile non e` privata, ma interna al contesto. Potete decidere di assegnare un prefisso, per convenzione, a tutte le variabili/funzioni che per voi sono private: io ho usato “_”).
La funzione mia-variabile e` il nostro getter/setter. Grazie alla possibilita` di creare argomenti opzionali e con parametri di default (argVar nil) posso gestire molto facilmente il suo uso. Quindi, se l’utente non passa i parametri richiesti, significa che vuole “usare” il GETTER, altrimenti sta attivando il SETTER.

In questo modo potete gestire la logica interna del contesto in modo piu` flessibile, diminuendo le probabilita` di modifica del codice all’esterno del contesto stesso, cioe` a coloro che useranno il contesto da voi creato. Questo e` quindi un modo molto efficace per scrivere moduli.

Questa tecnica puo` essere usata non solo per fare il wrapping di variabili, ma anche di funzioni. Ecco un esempio:

(context 'CalcolaSomme)

(setq _sommaFinale 0)

(define (_somma val1 val2)
   (setq _sommaFinale (+ val1 val2))
   (println (string "La somma e`: " _sommaFinale ))
);define

(define (somma (argValore1 nil) (argValore2 nil) )
   (if (and argValore1 argValore2)
      (_somma argValore1 argValore2) ; SETTER
      _sommaFinale                   ; GETTER
   )
);define

(context MAIN)

In questo esempio abbiamo creato un context che fa` somme. Richiamando la funzione (somma 10 15) stiamo chiedendo al setter di effettuare la somma dei valori 10 e 15. Usando invece (somma), senza argomenti (quindi un getter), otterremo il risultato dell’ultima somma effettuata.

Creare moduli con questa tecnica permette, a chi userà` tali moduli, di essere molto piu` sicuro di non dover modificare il proprio codice ad ogni piccola modifica effettuata al modulo stesso ;-)

A presto!





La nuova Fiera di Roma

13 02 2008

google_translate.gif Translate To English!

Io abito a Roma fin dalla nascita, e ho quindi visto nel tempo l’evolversi di questa meravigliosa e caotica città.
Tra le tante cose che ho sempre apprezzato/odiato e` la famosa Fiera di Roma, un’area espositiva per fiere, mostre, mercatini, etc…
Da qualche anno questa fiera e` stata spostata dalla zona dell’EUR, per ricrearla nelle vicinanze dell’autostrada Roma-Fiumicino (zona Ostiense).

Sono andato a vedere due mostre, una l’anno scorso ed una quest’anno. L’Italia (e gli italiani), con questa nuova immensa struttura, ha perso un’altra occasione per dimostrare quanto valiamo. Perche`? Ora io vi dico i problemi che ho incontrato, poi il giudizio potrete darlo voi.

Io ho un bimbo di un anno di eta` circa (alla precedente fiera a cui sono andato aveva meno di sei mesi).
Arrivato alla Fiera, ho avuto il bisogno di cambiare mio figlio (non con un altro migliore, anche perche` lui e` il piu` bello del mondo! Ovviamente!) ma perche` sembrava gli fosse scoppiata una bomba a mano nel pannolino (i risultati li potete ben immaginare!). Vado quindi ai bagni e, con mio stupore, non esiste un fasciatoio! Una struttura avveniristica, in cui si fanno anche fiere che vengono visitate da centinaia di famiglie (con bimbi al seguito) non ha un fasciatoio! Tra me e me ho pensato (l’anno scorso): <<be` forse stanno ancora completando la struttura (c’erano molti lavori in corso qui` e la`) e probabilmente dovranno ancora farli…>>.
Inutile dirvi che, alla visita di quest’anno nulla e` cambiato: i fasciatoi non esistono (e non c’è` un cartello che indica la loro presenza)!
A tutto cio`, come aggravante, aggiungete che non ci sono sedie disponibili! A cosa servono? Be`, provate ad allattare un bimbo al seno, in piedi!
Quest’anno ho assistito a questa scena: siamo tornati ai bagni per cambiare il piccolo, e ci “consigliano” di cambiarlo nei bagni per i diversi abili. Apriamo la porta e troviamo una ragazza, con suo marito, in piedi, ad allattare il loro piccolo. Lei con le braccia indolenzite, e lui tutto preso a “fare il tifo” :-) a suo figlio perche` completasse la pappa! Mia moglie quindi entra in bagno, dove non c’è` un posto su cui sdraiare mio figlio per fare “il cambio gomme”, mentre il marito di lei, molto gentilmente, esce dal bagno per far posto a mia moglie. Ora mi domando: e se nel frattempo fosse arrivato qualcuno diversamente abile e avesse avuto necessita` di usufruire del bagno? Boh…

All’ora di pranzo, decidiamo di mangiare qualcosa, e qui arriva la seconda sorpresa: quasi tutti i bar non sono dotati di sedie! Nulla di problematico, ma le sedie, in tutta la fiera ce ne sono veramente poche. Cosa e` questa? Una selezione per il pubblico? All’ingresso dovrebbero scrivere “vietato l’ingresso alle persone anziane, a coloro che soffrono di problemi alle gambe, a quelli che hanno bambini piccoli, etc…”.
Comunque, mangiamo in piedi, e poi proseguiamo.

Al ritorno, prendiamo l’ascensore per salire su una fantascientifica passerella sopraelevata (abbiamo il passeggino di nostro figlio completo di borsone per il suo cibo, i pannolini, e tutto l’indispensabile per lui), necessaria per raggiungere i parcheggi. Bene… dopo essere saliti, dovremmo anche scendere… e quindi ci rechiamo agli ascensori che, purtroppo, erano entrambi guasti (ma la mattina funzionavano regolarmente!). Allora andiamo verso le scale mobili e… ferme! Anche quelle sono ferme!!! Come scendiamo?! Meno male che oltre a mia moglie, c’erano anche mio padre e mia madre, e grazie anche al loro aiuto portiamo il passeggino, a mano, per le scale, fino in fondo alla rampa. Ma una tale struttura non ha un pronto intervento per questo genere di problemi? Sembra di no…

Infine, vado a prendere la macchina e, ovviamente, devo pagare pure il parcheggio. Ho gia` pagato 7 euro per l’ingresso. Non era sufficiente? Sembra di no. Ho dovuto pagare altri 5 (dico cinque!) euro per il parcheggio. Mi chiedo: il luogo della fiera non ha certo problemi di spazio, perche` far pagare anche il parcheggio? Invece di invogliare le persone ad andare a vedere una mostra di prodotti, ho l’impressione che siano facendo di tutto per mandare via la gente! E la cosa scandalosa e` che e` una struttura nuovissima!

L’ho visitata due volte, e ho avuto molti problemi e difficoltà`. Mi sono ripromesso di non tornarci piu`.
E` un peccato pero`. Un’altra occasione sprecata per far vedere quanto noi italiano valiamo veramente.

A presto!