Successivo: , Su: Ordinamento di vettori   [Contenuti][Indice]


12.2.1 Controllare visita vettori

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.


Note a piè di pagina

(89)

Questo è il motivo per cui gli ordinamenti predefiniti iniziano con il carattere ‘@’, che non può essere usato in un identificativo.


Successivo: , Su: Ordinamento di vettori   [Contenuti][Indice]