Successivo: Sommario delle funzioni, Precedente: Funzioni definite dall'utente, Su: Funzioni [Contenuti][Indice]
Questa sezione descrive un’estensione avanzata, specifica di gawk
.
Spesso può essere utile ritardare la scelta della funzione da chiamare fino al momento in cui il programma viene eseguito. Per esempio, potrebbero esserci diversi tipi di record in input, ciascuno dei quali dovrebbe essere elaborato in maniera differente.
Solitamente, si userebbe una serie di istruzioni if
-else
per decidere quale funzione chiamare. Usando la chiamata indiretta
a una funzione, si può assegnare il nome della funzione da chiamare a
una variabile di tipo stringa, e usarla per chiamare la funzione.
Vediamo un esempio.
Si supponga di avere un file con i punteggi ottenuti negli esami per i corsi che si stanno seguendo, e che si desideri ottenere la somma e la media dei punteggi ottenuti. Il primo campo è il nome del corso. I campi seguenti sono i nomi delle funzioni da chiamare per elaborare i dati, fino a un campo “separatore” ‘dati:’. Dopo il separatore, fino alla fine del record, ci sono i vari risultati numerici di ogni test.
Ecco il file iniziale:
Biologia_101 somma media dati: 87.0 92.4 78.5 94.9 Chimica_305 somma media dati: 75.2 98.3 94.7 88.2 Inglese_401 somma media dati: 100.0 95.6 87.1 93.4
Per elaborare i dati, si potrebbe iniziare a scrivere:
{ corso = $1 for (i = 2; $i != "dati:"; i++) { if ($i == "somma") somma() # elabora l'intero record else if ($i == "media") media() … # e così via } }
Questo stile di programmazione funziona, ma può essere scomodo.
Con la chiamata indiretta di funzione, si può richiedere a gawk
di usare il valore di una variabile come nome della funzione da
chiamare.
La sintassi è simile a quella di una normale chiamata di funzione: un identificativo, seguito immediatamente da una parentesi aperta, qualche argomento, e una parentesi chiusa, con l’aggiunta di un carattere ‘@’ all’inizio:
quale_funzione = "somma" risultato = @quale_funzione() # chiamata della funzione somma()
Ecco un intero programma che elabora i dati mostrati sopra, usando la chiamata indiretta di funzioni:
# chiamataindiretta.awk --- esempio di chiamata indiretta di funzioni # media --- calcola la media dei valori dei campi $primo - $ultimo function media(primo, ultimo, somma, i) { somma = 0; for (i = primo; i <= ultimo; i++) somma += $i return somma / (ultimo - primo + 1) } # somma --- restituisce la somma dei valori dei campi $primo - $ultimo function somma(primo, ultimo, totale, i) { max = 0; for (i = primo; i <= ultimo; i++) totale += $i return totale }
Queste due funzioni presuppongono che si lavori con dei campi; quindi,
i parametri primo
e ultimo
indicano da quale campo iniziare
e fino a quale arrivare.
Per il resto, eseguono i calcoli richiesti, che sono i soliti:
# Per ogni record, # stampa il nome del corso e le statistiche richieste { nome_corso = $1 gsub(/_/, " ", nome_corso) # Rimpiazza _ con spazi # trova campo da cui iniziare for (i = 1; i <= NF; i++) { if ($i == "dati:") { inizio = i + 1 break } } printf("%s:\n", nome_corso) for (i = 2; $i != "dati:"; i++) { quale_funzione = $i printf("\t%s: <%s>\n", $i, @quale_funzione(inizio, NF) "") } print "" }
Questo è il ciclo principale eseguito per ogni record.
Stampa il nome del corso (con le
lineette basse sostituite da spazi). Trova poi l’inizio dei dati veri
e propri, salvandolo in inizio
.
L’ultima parte del codice esegue un ciclo per ogni nome di funzione
(da $2
fino al separatore, ‘dati:’), chiamando la funzione
il cui nome è specificato nel campo. La chiamata di funzione indiretta
compare come parametro nella chiamata a printf
.
(La stringa di formattazione di printf
usa ‘%s’ come
specificatore di formato, affinché sia possibile usare funzioni
che restituiscano sia stringhe che numeri. Si noti che il risultato
della chiamata indiretta è concatenato con la stringa nulla, in modo da
farlo considerare un valore di tipo stringa).
Ecco il risultato dell’esecuzione del programma:
$ gawk -f chiamataindiretta.awk dati_dei_corsi -| Biologia 101: -| somma: <352.8> -| media: <88.2> -| -| Chimica 305: -| somma: <356.4> -| media: <89.1> -| -| Inglese 401: -| somma: <376.1> -| media: <94.025>
La possibilità di usare la chiamata indiretta di funzioni è più potente
di quel che si possa pensare inizialmente.
I linguaggi C e C++ forniscono “puntatori di funzione” che
sono un metodo per chiamare una funzione scelta al momento dell’esecuzione.
Uno dei più noti usi di questa funzionalità è
la funzione C qsort()
, che ordina un vettore usando il famoso
algoritmo noto come “quicksort”
(si veda l’articolo di Wikipedia
per ulteriori informazioni). Per usare questa funzione, si specifica un
puntatore a una funzione di confronto. Questo meccanismo consente
di ordinare dei dati arbitrari in una maniera arbitraria.
Si può fare qualcosa di simile usando gawk
, così:
# quicksort.awk --- Algoritmo di quicksort, con funzione di confronto # fornita dall'utente # quicksort --- Algoritmo di quicksort di C.A.R. Hoare. # Si veda Wikipedia o quasi ogni libro # che tratta di algoritmi o di informatica. function quicksort(dati, sinistra, destra, minore_di, i, ultimo) { if (sinistra >= destra) # non fa nulla se il vettore contiene return # meno di due elementi quicksort_scambia(dati, sinistra, int((sinistra + destra) / 2)) ultimo = sinistra for (i = sinistra + 1; i <= destra; i++) if (@minore_di(dati[i], dati[sinistra])) quicksort_scambia(dati, ++ultimo, i) quicksort_scambia(dati, sinistra, ultimo) quicksort(dati, sinistra, ultimo - 1, minore_di) quicksort(dati, ultimo + 1, destra, minore_di) } # quicksort_scambia --- funzione ausiliaria per quicksort, # sarebbe meglio fosse nel programma principale function quicksort_scambia(dati, i, j, salva) { salva = dati[i] dati[i] = dati[j] dati[j] = salva }
La funzione quicksort()
riceve il vettore dati
, gli
indici iniziali e finali da ordinare
(sinistra
e destra
), e il nome di una funzione che
esegue un confronto “minore di”. Viene quindi eseguito
l’algoritmo di quicksort.
Per fare uso della funzione di ordinamento, torniamo all’esempio precedente. La prima cosa da fare è di scrivere qualche funzione di confronto:
# num_min --- confronto numerico per minore di function num_min(sinistra, destra) { return ((sinistra + 0) < (destra + 0)) }
# num_magg_o_ug --- confronto numerico per maggiore o uguale function num_magg_o_ug(sinistra, destra) { return ((sinistra + 0) >= (destra + 0)) }
La funzione num_magg_o_ug()
serve per ottenere un ordinamento
decrescente (dal numero più alto al più basso); quando è usato
per eseguire un test per “minore di”, in realtà fa l’opposto
(maggiore o uguale a), il che conduce a ottenere dati ordinati
in ordine decrescente.
Poi serve una funzione di ordinamento.
Come parametri ha i numeri del campo iniziale e di quello finale,
e il nome della funzione di confronto.
Costruisce un vettore con
i dati e richiama appropriatamente quicksort()
; quindi formatta i
risultati mettendoli in un’unica stringa:
# ordina --- ordina i dati a seconda di `confronta' # e li restituisce come un'unica stringa function ordina(primo, ultimo, confronta, dati, i, risultato) { delete dati for (i = 1; primo <= ultimo; primo++) { dati[i] = $primo i++ } quicksort(dati, 1, i-1, confronta) risultato = dati[1] for (i = 2; i in dati; i++) risultato = risultato " " dati[i] return risultato }
Per finire, le due funzioni di ordinamento chiamano la funzione
ordina()
, passandole i nomi delle due funzioni di confronto:
# ascendente --- ordina i dati in ordine crescente # e li restituisce sotto forma di stringa function ascendente(primo, ultimo) { return ordina(primo, ultimo, "num_min") }
# discendente --- ordina i dati in ordine decrescente # e li restituisce sotto forma di stringa function discendente(primo, ultimo) { return ordina(primo, ultimo, "num_magg_o_ug") }
Ecco una versione estesa del file-dati:
Biologia_101 somma media ordina discendente dati: 87.0 92.4 78.5 94.9 Chimica_305 somma media ordina discendente dati: 75.2 98.3 94.7 88.2 Inglese_401 somma media ordina discendente dati: 100.0 95.6 87.1 93.4
Per finire, questi sono i risultati quando si esegue il programma in questa versione migliorata:
$ gawk -f quicksort.awk -f indirettacall.awk class_data2 -| Biologia 101: -| somma: <352.8> -| media: <88.2> -| ascendente: <78.5 87.0 92.4 94.9> -| discendente: <94.9 92.4 87.0 78.5> -| -| Chimica 305: -| somma: <356.4> -| media: <89.1> -| ascendente: <75.2 88.2 94.7 98.3> -| discendente: <98.3 94.7 88.2 75.2> -| -| Inglese 401: -| somma: <376.1> -| media: <94.025> -| ascendente: <87.1 93.4 95.6 100.0> -| discendente: <100.0 95.6 93.4 87.1>
Un altro esempio in cui le chiamate indirette di funzione sono utili è costituito dall’elaborazione di vettori. La descrizione si può trovare Attraversare vettori di vettori.
Occorre ricordarsi di anteporre il carattere ‘@’ prima di una chiamata indiretta di funzione.
A partire dalla versione 4.1.2 di gawk
, le chiamate
indirette di funzione
possono anche essere usate per chiamare funzioni predefinite e con
funzioni di estensione
(vedi la sezione Scrivere estensioni per gawk
). Ci sono alcune limitazioni nel richiamare
in maniera indiretta delle funzioni predefinite, come qui dettagliato:
sub()
, gsub()
, gensub()
, match()
,
split()
e patsplit()
.
sub()
o gsub()
, sono accettati solo due argomenti,
poiché queste funzioni sono atipiche, in quanto aggiornano il loro terzo
argomento. Questo significa che verrà sempre aggiornato l’argomento di
default, $0
.
gawk
fa del suo meglio per rendere efficiente la chiamata indiretta
di funzioni. Per esempio, nel ciclo seguente:
for (i = 1; i <= n; i++) @quale_funzione()
gawk
ricerca solo una volta quale funzione chiamare.
Questa
limitazione potrebbe cambiare in una futura versione;
per appurarlo, si controlli la documentazione che accompagna
la versione in uso di gawk
.
Successivo: Sommario delle funzioni, Precedente: Funzioni definite dall'utente, Su: Funzioni [Contenuti][Indice]