ioProgrammo N°20, Dicembre 1998 ©Copyright DIEMME Editori
if {condizione1} { corpo1 } elseif {condizione2} { corpo2 } else { corpo3 }Il comando if controlla il valore ritornato da condizione1. Condizione1 deve essere una espressione valida e come tale deve restituire un valore booleano: 0 (falso) o un qualsiasi numero diverso da 0 (vero). Può restituire anche una stringa ed in questo caso sarà true/yes (vero) o false/no (falso). Se condizione1 ritorna vero, verrà eseguito corpo1 (per corpo si intende uno o più comandi in successione). E' anche permessa la presenza o meno della clausola elseif che verifica una nuova condizione ed esegue un nuovo corpo. Elseif può essere ripetuto più volte a seconda di come dovrà essere controllato il programma. La clausola else finale è facoltativa e serve per eseguire l'ultimo blocco di comandi se la verifica di tutte le altre condizioni precedenti ha avuto esito negativo. Qualche esempio?
puts a= set a [gets stdin] # attende un input da tastiera puts b= set b [gets stdin] if {$a == $b} { puts "a e b sono uguali" } elseif {$a > $b} { puts "a e' maggiore di b" } else { puts "b e' maggiore di a" }Il comando switch invece è usato per controllare l'esecuzione dello script in base al risultato (sempre booleano) del confronto di due stringhe o di una stringa ed una porzione dell'altra che chiameremo modello. Switch è spesso usato in combinazione con le espressioni regolari che consentono la creazione di modelli di confronto molto flessibili. A qualcuno potrà ricordare il case del Pascal o lo switch del C, ma in realtà è abbastanza diverso. Le forme generali di questo comando sono:
switch ?opzioni? stringa modello1 corpo1 ?modello2 corpo2 ...?oppure
switch ?opzioni? stringa {modello1 corpo1 ?modello2 corpo2 ...?}Le opzioni disponibili sono quattro: -exact, -glob , -regex e -- La prima, -exact, significa che il confronto avrà esito positivo solo se stringa e modello coincidono perfettamente. -glob è usata per eseguire confronti particolari usando gli stessi modelli costruiti con il comando "string match" illustrato nel numero precedente. L'opzione -regex permette l'uso delle espressioni regolari, mentre i due trattini -- segnano la fine delle opzioni in modo da impedire che un' eventuale stringa da confrontare venga scambiata per un'opzione del comando.
puts -nonewline "Scrivi un valore booleano: " gets stdin boole # attende un input da tastiera # e lo assegna alla variabile boole switch -exact -- $boole { 1 {puts vero} yes {puts vero} true {puts vero} # se immetto 1 yes o true stampa vero 0 {puts falso} no {puts falso} false {puts falso} # se immetto 0 no o false stampa falso defalut {puts "Non booleano"} # se scrivo qualsiasi altra cosa stampa non booleano } switch -glob pippo { p* {puts 1} ?ippo {puts 1} [1-0] {puts 0} } # stampa 1 switch -regexp aaab { ^a.*b$ - b {puts 1} a* {puts 2} default {puts 3} } # stampa 1 switch xyz { a - b {puts 1} a* {puts 2} default {puts 3} } # stampa 3Vi sarete accorti che negli esempi compare la clausola default. Ebbene, ciò significa che verrà eseguito il blocco di comandi preceduto da default se tutti i confronti precedenti falliscono. Inoltre se un corpo è costituto da un singolo trattino - ad esso verrà sostituito il corpo successivo. In questo modo, quindi, è possibile condividere un unico corpo di codice per più confronti.
for {inizializzazione} {condizione} {incremento} { corpo }
set somma 0 for {set i 0} {$i <= 10} {incr i} { set somma [expr $somma+$i] } puts "La somma e': $somma" # stampa La somma e' 55Lo stesso risultato può essere ottenuto con il comando while
while {condizione} { corpo }
set i 0 set somma 0 while {$i <= 10} { set somma [expr $somma+$i] incr i } puts "La somma e': $somma" # stampa La somma e' 55ma anche con foreach, usato soprattutto per eseguire uno o più comandi per ogni elemento di una lista
foreach variabile lista { corpo }
set somma 0 foreach i {1 2 3 4 5 6 7 8 9 10} { set somma [expr $somma+$i] } puts "La somma e': $somma" # stampa La somma e' 55Vi ricordate gli array associativi del Perl? In tcl, una simile implementazione può essere fatta con le liste ed il comando foreach.
set lista {rosso #ff0000 verde #00ff00 blu #0000ff} foreach {colore valore} $lista { puts "$colore: $valore" } # stampa\ rosso: ff0000\ verde: 00ff00\ blu: 0000ff
regexp ?opzioni? espressione stringa ?var? ?subvar subvar ...?dove un possibile valore per opzioni può essere -nocase (effettua il matching senza distinguere minuscole e maiuscole, espressione è l'espressione regolare, stringa è la variabile o il letterale su cui si esegue il confronto, var è il nome di una variabile in cui memorizzare la stringa riscontrata dall'espressione regolare mentre in subvar sono contenute le parti della stringa riscontrate da ogni singolo "atomo" (vedremo tra poco cosa si intende per atomo). In tcl una regular expression è una sequenza di caratteri racchiusa tra parentesi graffe. Al loro interno possiamo definire uno più atomi seguiti (opzionalmente) da uno dei caratteri * + ?. L'asterisco riscontra zero o più volte il contenuto dell'atomo, il + riscontra una o più volte, mentre il ? riscontra un'unica volta l'atomo oppure la stringa nulla. Il contenuto di un atomo, che deve essere racchiuso tra parentesi tonde, può essere formato da:
set url http://www.sun.com:80/index.html regexp {([^:]+)://([^:/]+)(:([0-9]+))?(/.*)} $url \ match protocol server x port path puts $match # http://www.sun.com:80/index.html puts $protocol # http puts $server # www.sun.com puts $x # :80 puts $port # 80 puts $path # /index.html
proc nomeprocedura {} { comandi }Abbiamo definito una semplicissima procedura che non riceve nessun parametro e non restituisce alcun valore. Se fosse necessario usare all'interno della procedura una variabile globale definita nel corpo principale dello script, si deve ricorrere al comando global. Per defaul, infatti, una procedura "vede" solo le variabili definite all'interno di essa a meno che non le vengano passate in modo esplicito.
set a 3 set b 5 proc somma1 {} { set a 1 set b 9 set somma [expr $a+$b] puts $somma } proc somma2 {} { global a b set somma [expr $a+$b] puts $somma } proc somma3 {a b} { set somma [expr $a+$b] puts $somma } somma1; # stampa 10 somma2; # stampa 8 somma3 $a $b; # stampa 8Per default una procedura restituisce il valore ritornato dall'ultimo comando. E' tuttavia possibile forzare la restituzione di un determinato valore usando il comando return. Vediamone un'applicazione per il calcolo del fattoriale di un numero
proc fattoriale {x} { set i 1; set prodotto 1 while {$i <= $x} { set prodotto [expr $prodotto * $i] incr i } return $prodotto } puts [fattoriale 10] # stampa 3628800Se in fase di dichiarazione della procedura uso l'argomento args, tale procedura potrà ricevere un numero variabile di argomenti che verranno memorizzati all'interno della lista args
proc prova {a b args} { puts $a puts $b puts $args } prova 1 2 3 4 # stampa \ 1\ 2\ 3 4A conclusione di questo paragrafo è utile ricordare che il passaggio dei parametri ad una procedura avviene per valore. Se fosse necessario modificare il valore di una variabile il cui nome viene passato come argomento ad una procedura, si deve far ricorso al comando upvar abilitando il cosiddetto passaggio per riferimento
set a 5 proc rif {} { upvar a b set b 9 } rif puts $a; # stampa 9 set x 0 proc cambio {x} { upvar x y set y 1 } cambio $x puts $x; # stampa 1
set idfile [open "/etc/passwd" r] while {[gets $idfile riga] != -1} { set item [split $riga ":"] puts [lindex $item 0] } close $idfilePer scrivere un file si utilizza sempre open ma in questo caso l'opzione fondamentale è w. Vediamo
set idfile [open "./prova.txt" w] puts $idfile "Prima riga del file" close $idfileE se avessimo bisogno di leggere dei file binari? In questo caso si usa il comando read che permette la lettura del file in blocchi chiamati buffer. Un buffer è in sostanza una porzione del file espressa in byte. Cambia anche la condizione di fine file, ottenuta con il comando eof che ritorna 1 se si è raggiunto l'end of file.
set idfile [open "./prova" r] while {![eof $idfile]} { set buffer [read $idfile 1024] . . . } close $idfileUn altro comando molto utile, usato per compiere operazioni su un gruppo di file, è glob, il quale restituisce una lista i cui elementi sono i nomi dei file individuati dallo stesso glob. Per esempio, se si volesse elaborare tutti i file html della directory corrente si dovrebbe usare il seguente codice
foreach html [glob *.html] { set idfile [open $html r] while {[gets $idfile riga] != -1} { puts $riga } close $idfile }Per concludere vediamo come tcl offra al programmatore la possibilità di eseguire comandi del sistema operativo. Un primo approccio consiste nell'aprire dei pipe con il comando open
set input [open "|sort /etc/passwd" r] set contents [split [read $input] \n] close $input foreach item $contents { puts $item }Il secondo metodo, forse più naturale, consiste nel ricorrere al comando exec. In questo caso però ci si deve ricordare che exec non espande i caratteri jolli * ? sottomessi al comando di sistema
exec df; # esegue il comando df exec ls -l *.tcl; # errore eval exec ls -l [glob *.tcl]; # ok # esegue ls -l *.tcl
Data creazione HTML: Marzo 1999
Autore: Francesco Munaretto
E-mail: NoSpam@thank.you