Successivo: Funzioni di ordinamento di vettori, Su: Ordinamento di vettori [Contenuti][Indice]
Per default, l’ordine secondo il quale un ciclo ‘for (indice in
vettore)’ scorre un vettore non è definito; in genere si basa
sull’implementazione interna dei vettori all’interno di awk
.
Spesso, tuttavia, si vorrebbe poter eseguire un ciclo sugli elementi in un
determinato ordine scelto dall’utente programmatore. Con gawk
si può fare.
Visita di vettori in ordine predefinito con gawk
parla di come si possono assegnare valori speciali
predefiniti a PROCINFO["sorted_in"]
per controllare l’ordine secondo il
quale gawk
attraversa un vettore
durante un ciclo for
.
Inoltre, il valore di PROCINFO["sorted_in"]
può essere un nome di
funzione.89
Questo consente di scorrere un vettore sulla base di un qualsiasi criterio
personalizzato. Gli elementi del vettore vengono ordinati in accordo col valore
ritornato da questa funzione. La funzione che fa il confronto dovrebbe essere
definita con almeno quattro argomenti:
function confronta(i1, v1, i2, v2) { confronta gli elementi 1 e 2 in qualche modo return < 0; 0; o > 0 }
Qui, i1
e i2
sono gli indici, e v1
e v2
sono i corrispondenti valori dei due elementi che si stanno confrontando.
v1
oppure v2
, o entrambi, possono essere vettori se il vettore
che si sta visitando contiene sottovettori come valori.
(Vedi la sezione Vettori di vettori per maggiori informazioni sui sottovettori.)
I tre possibili codici di ritorno sono interpretati nel seguente modo:
confronta(i1, v1, i2, v2) < 0
L’indice i1
viene prima dell’indice i2
durante l’avanzamento
del ciclo.
confronta(i1, v1, i2, v2) == 0
Gli indici i1
e i2
sono equivalenti, ma l’ordine tra loro non è definito.
confronta(i1, v1, i2, v2) > 0
L’indice i1
viene dopo l’indice i2
durante l’avanzamento del
ciclo.
La prima funzione di confronto può essere usata per scorrere un vettore secondo l’ordine numerico degli indici:
function cfr_ind_num(i1, v1, i2, v2) { # confronto di indici numerici, ordine crescente return (i1 - i2) }
La seconda funzione scorre un vettore secondo l’ordine delle stringhe dei valori degli elementi piuttosto che secondo gli indici:
function cfr_val_str(i1, v1, i2, v2) { # confronto di valori di stringa, ordine crescente v1 = v1 "" v2 = v2 "" if (v1 < v2) return -1 return (v1 != v2) }
La terza funzione di confronto restituisce dapprima tutti i numeri, e dopo questi le stringhe numeriche senza spazi iniziali o finali, durante l’avanzamento del ciclo:
function cfr_val_num_str(i1, v1, i2, v2, n1, n2) { # confronto mettendo i numeri prima dei valori di stringa, # ordine crescente n1 = v1 + 0 n2 = v2 + 0 if (n1 == v1) return (n2 == v2) ? (n1 - n2) : -1 else if (n2 == v2) return 1 return (v1 < v2) ? -1 : (v1 != v2) }
Qui vediamo un programma principale che mostra come gawk
si comporta usando ciascuna delle funzioni precedenti:
BEGIN { data["uno"] = 10 data["due"] = 20 data[10] = "uno" data[100] = 100 data[20] = "due" f[1] = "cfr_ind_num" f[2] = "cfr_val_str" f[3] = "cfr_val_num_str" for (i = 1; i <= 3; i++) { printf("Funzione di ordinamento: %s\n", f[i]) PROCINFO["sorted_in"] = f[i] for (j in data) printf("\tdata[%s] = %s\n", j, data[j]) print "" } }
I risultati dell’esecuzione del programma sono questi:
$ gawk -f compdemo.awk -| Funzione di ordinamento: cfr_ind_num Ordinamento per indice numerico -| data[uno] = 10 -| data[due] = 20 Entrambe le stringhe sono numericamente zero -| data[10] = uno -| data[20] = due -| data[100] = 100 -| -| Funzione di ordinamento: cfr_val_str Ordinamento per valore degli -| elementi come stringhe -| data[uno] = 10 -| data[100] = 100 La stringa 100 è minore della stringa 20 -| data[due] = 20 -| data[20] = due -| data[10] = uno -| -| Funzione di ordinamento: cfr_val_num_str Ordinamento con tutti i -| valori numerici prima di tutte le stringhe -| data[uno] = 10 -| data[due] = 20 -| data[100] = 100 -| data[20] = due -| data[10] = uno
Si provi a ordinare gli elementi di un file delle password del sistema GNU/Linux in base al nome d’accesso dell’utente. Il seguente programma ordina i record secondo una specifica posizione del campo e può essere usato per questo scopo:
# passwd-sort.awk --- semplice programma per ordinare in base alla # posizione del campo # la posizione del campo è specificata dalla variabile globale POS function per_campo(i1, v1, i2, v2) { # confronto per valore, come stringa, e in ordine crescente return v1[POS] < v2[POS] ? -1 : (v1[POS] != v2[POS]) } { for (i = 1; i <= NF; i++) a[NR][i] = $i }
END { PROCINFO["sorted_in"] = "per_campo"
if (POS < 1 || POS > NF) POS = 1 for (i in a) { for (j = 1; j <= NF; j++) printf("%s%c", a[i][j], j < NF ? ":" : "") print "" } }
Il primo campo di ogni elemento del file delle password è il nome d’accesso
dell’utente, e i campi sono separati tra loro da due punti (:
).
Ogni record definisce un sottovettore,
con ogni campo come elemento nel sottovettore.
L’esecuzione del programma produce
il seguente output:
$ gawk -v POS=1 -F: -f sort.awk /etc/passwd -| adm:x:3:4:adm:/var/adm:/sbin/nologin -| apache:x:48:48:Apache:/var/www:/sbin/nologin -| avahi:x:70:70:Avahi daemon:/:/sbin/nologin …
Il confronto normalmente dovrebbe restituire sempre lo stesso valore quando vien dato come argomento un preciso paio di elementi del vettore. Se viene restituito un risultato non coerente, l’ordine è indefinito. Questo comportamento può essere sfruttato per introdurre un ordinamento casuale in dati apparentemente ordinati:
function ordina_a_caso(i1, v1, i2, v2) { # ordine casuale (attenzione: potrebbe non finire mai!) return (2 - 4 * rand()) }
Come già accennato, l’ordine degli indici è arbitrario se due elementi risultano uguali. Normalmente questo non è un problema, ma lasciare che elementi di uguale valore compaiano in ordine arbitrario può essere un problema, specialmente quando si confrontano valori di elementi di un elenco. L’ordine parziale di elementi uguali può cambiare quando il vettore viene visitato di nuovo, se nel vettore vengono aggiunti o rimossi elementi. Un modo per superare l’ostacolo quando si confrontano elementi con valori uguali è quello di includere gli indici nelle regole di confronto. Si noti che questo potrebbe rendere meno efficiente l’attraversamento del ciclo, per cui si consiglia di farlo solo se necessario. Le seguenti funzioni di confronto impongono un ordine deterministico, e si basano sul fatto che gli indici (di stringa) di due elementi non sono mai uguali:
function per_numero(i1, v1, i2, v2) { # confronto di valori numerici (e indici), ordine decrescente return (v1 != v2) ? (v2 - v1) : (i2 - i1) }
function per_stringa(i1, v1, i2, v2) { # confronto di valori di stringa (e indici), ordine decrescente v1 = v1 i1 v2 = v2 i2 return (v1 > v2) ? -1 : (v1 != v2) }
Una funzione di confronto personalizzata spesso può semplificare l’attraversamento del ciclo ordinato, e il solo limite è il cielo, quando si va a progettare una funzione di questo tipo.
Quando i confronti tra stringhe son fatti durante un’operazione di ordinamento,
per valori di elementi che, uno o entrambi, non sono numeri, o per
indici di elementi gestiti come stringhe, il valore di IGNORECASE
(vedi la sezione Variabili predefinite) controlla se
i confronti trattano corrispondenti lettere maiuscole e minuscole
come equivalenti o come distinte.
Un’altra cosa da tenere a mente è che nel caso di sottovettori, i valori degli
elementi possono essere a loro volta dei vettori; una funzione di confronto in
produzione dovrebbe usare la funzione isarray()
(vedi la sezione Funzioni per conoscere il tipo di una variabile)
per controllare ciò, e scegliere un ordinamento preciso per i sottovettori.
Tutti gli ordinamenti basati su PROCINFO["sorted_in"]
sono disabilitati in modalità POSIX,
perché il vettore PROCINFO
in questo caso non è speciale.
Come nota a margine, si è visto che ordinare gli indici del vettore prima di
scorrere il vettore porta a un incremento variabile dal 15% al 20% del tempo di
esecuzione dei programmi awk
. Per questo motivo l’attraversamento
ordinato di vettori non è il default.
Questo è il motivo per cui gli ordinamenti predefiniti iniziano con il carattere ‘@’, che non può essere usato in un identificativo.
Successivo: Funzioni di ordinamento di vettori, Su: Ordinamento di vettori [Contenuti][Indice]