Sat Nov 2 17:55:37 CET 2013

Introduzione a Supplier 0.2

Qualche parola sul perché, secondo me, il supplier è veramente utile e non solo comodo come "scorciatoia sintattica".

Supponiamo di scrivere una funzione che, dato in input un array di stringhe, restituisce un altro array di stringhe contenente le stesse oppure, nel caso in cui soddisfino una qualche condizione, altre. Per esempio, se le stringhe sono più lunghe di 10 caratteri (per semplicità consideriamo solo stringhe ASCII per non gestire il problema della codifica) le raddoppia.

Dovendo implementare un metodo Java su queste specifiche, al di là delle variazioni sintattiche che potrebbero rendere il programma più bello, si potrebbe scrivere qualcosa di questo tipo.

static String[] transform(String[] inputa) {
    String[] outputa = new String[inputa.length];
    for (int i = 0; i<inputa.length; ++i) {
        String s = inputa[i];
        if (10<s.length())
            outputa[i] = s + s;
        else
            outputa[i] = s;
    }
    return (outputa);
}

Quello che voglio sottolineare è che non tutte le stringhe vengono ricopiate. Per le stringhe corte, che magari sono la maggioranza, viene semplicemente condiviso il riferimento.

Se dovessi scrivere una funzione equivalente in C potrei avere dei problemi! dovrei STRcopiare tutto con grande spreco di memoria e questo per un motivo molto semplice: perché è la struttura stessa che ci fornisce informazioni su come disallocare.

Ho evidenziato l'affermazione precedente perché è quello che io considero "il male"! Quello che voglio risolvere per ritrovare il piacere di programmare in C!

Con il supplier la scriverei nel seguente modo.

char **transform(void *supplier, char **inputv, int inputc) {
    char **outputv;
    int i, j;
    char *cp;
    outputv = (char **) s_supply(supplier, (int) (inputc*sizeof (char *)));
    if (!outputv)
        return (NULL);
    for (i = 0; i<inputc; ++i) {
        j = (int) strlen(inputv[i]);
        if (10<j) {
            cp = (char *) s_supply(supplier, 2*j+1);
            if (!cp)
                return (NULL);
            strncpy(cp, inputv[i], j);
            strcpy(cp+j, inputv[i]);
            outputv[i] = cp;
        }
        else
            outputv[i] = inputv[i];
    }
    return (outputv);
}

Posso non restituire la dimensione perché è uguale a quella dell'input.

Il secondo array esiste fino a quando il supplier non viene distrutto, non necessariamente il primo, a meno che non venga costruito nel medesimo supplier in tutto oppure in parte... e notare che nelle condizioni di errore non bisogna fare nulla! non male!

La logica è esattamente la stessa del primo esempio. Si vede ancora di più se uso le funzioni si supporto presenti nella libreria.

 char **transform(void *supplier, char **inputv, int inputc) {
    void *output;
    int i;
    char *cp;
    output = v_new(supplier, 0);
    if (!output)
        return (NULL);
    for (i = 0; i<inputc; ++i) {
        cp = inputv[i];
        if (10<strlen(cp)) {
            cp = buffer_strconcat(supplier, cp, cp);
            if (!cp)
                return (NULL);
        }
        if (!v_add(output, cp))
            return (NULL);
    }
    return ((char**) v_elements(output));
}

Qui uso anche memoria per l'array dinamico che è comunque contenuto nel supplier e quindi potrei anche restituire (invece dell'array dei suoi valori). Per comodità, in quanto contiene anche l'informazione della propria dimensione.

Notare che con il supplier posso anche impacchettare più risultati e restituirli (come potrei fare in Java) senza utilizzare quindi parametri per i valori di ritorno.

In poche parole, gestendo uno o più supplier possiamo programmare in C (quasi) con la stessa disinvoltura del Python... un bel risultato, no?


Posted by athos | Permanent link | File under: supplier