2010-10-28 8 views
10

Ho difficoltà ad implementare una funzione di 'mappa' generica sugli array. Ho iniziato con il seguente progetto:Implementazione di una funzione di 'mappa' generica su array in C

void MapArray(void * src, void * dest, void * (f)(void *), size_t n, size_t elem) 
{ 
    unsigned int i = 0, j = 0; 
    void * temp = malloc(elem); 

    for(i = 0; i<n, i++) 
    { 
     temp = (f)((char *) src) + i)); 
     for(j = 0; j < elem; j++) 
     { 
     *(((char *) dest) + i) = *(((char *) temp) + i); 
     } 
    } 
    free(temp); 
} 

capisco perché non è corretto - sto fusione a (char *) prima di darlo a 'f' - ma ora sono demotivati ​​e non posso venire con una soluzione. (Sto facendo questo nel processo di apprendimento C)

La mia motivazione era ottenere il risultato di "f" e, byte per byte, copiarlo su dest [i].

Potete darmi qualche suggerimento?

+0

per quello che serve a questo puntatore per funzionare e quale mappa si desidera implementare, non penso che ne volete alcuni ad esempio il contenitore di mappe di sgi? – Svisstack

+0

È la tipica applicazione "mappa" che puoi trovare praticamente in ogni lingua funzionale. Si invia una lista, una funzione e si restituisce la lista composta come tale: (f (l [1]), ..., f (l [n])). – Lasirc

risposta

18

Il tuo primo problema è che si' fare troppo MODO in poche espressioni. Devi abbatterlo.

void MapArray(void * src, void * dest, void * (f)(void *), size_t n, size_t elem) 
{ 
    unsigned int i = 0, j = 0; 
    void * temp = malloc(elem); 

    char* csrc = (char*)src; 
    char* cdest = (char*)dest; 
    char* ctemp = (char*)temp; 
    for(i = 0; i<n; i++) 
    { 
     csrc++; 
     cdest++; 
     ctemp++; 
     temp = f(csrc); 
     for(j = 0; j < elem; j++) 
     { 
      cdest[i] = ctemp[i]; 
     } 
    } 
    free(temp); 
} 

Ora il tuo secondo problema. Hai malloc un buffer, quindi tu ... assegni a quel puntatore? Ripetutamente? Quindi liberare solo il risultato dell'ultima f call? Questo è assolutamente inutile.

void MapArray(void * src, void * dest, void * (f)(void *), size_t n, size_t elem) 
{ 
    unsigned int i = 0, j = 0; 

    char* csrc = (char*)src; 
    char* cdest = (char*)dest; 
    for(i = 0; i<n; i++) 
    { 
     csrc++; 
     cdest++; 
     char* ctemp = (char*)f(csrc); 
     for(j = 0; j < elem; j++) 
     { 
      cdest[i] = ctemp[i]; 
     } 
    } 
} 

Ora il tuo terzo problema. Passi un puntatore in - ma solo per char. Non si passa nel vuoto *. Ciò significa che la tua funzione non può essere generica - f non può essere applicata a nulla. Abbiamo bisogno di una matrice di void * s, in modo che la funzione possa assumere qualsiasi tipo come argomento. Abbiamo anche bisogno di prendere la dimensione del tipo come argomento in modo da sapere quanto lontano spostarsi lungo dest.

void MapArray(void ** src, void * dest, void * (f)(void *), size_t n, size_t sizeofT) 
{ 
    for(unsigned int i = 0; i < n; i++) { 
     void* temp = f(src[n]); 
     memcpy(dest, temp, sizeofT); 
     dest = (char*)dest + sizeofT; 
    } 
} 

Abbiamo ancora un altro problema: il ricordo della temperatura. Non lo liberiamo. Né passiamo un argomento di dati utente in f, che gli consentirebbe di restituire memoria allocata su heap che non abbiamo bisogno di liberare. L'unico modo in cui f può funzionare è se restituisce un buffer statico.

void MapArray(void ** src, void * dest, void * (f)(void *, void*), void* userdata, size_t n, size_t sizeofT) 
{ 
    for(unsigned int i = 0; i < n; i++) { 
     void* temp = f(src[n], userdata); 
     memcpy(dest, temp, sizeofT); 
     dest = (char*)dest + sizeofT; 
    } 
} 

Ora f può operare praticamente su qualsiasi cosa voglia e mantenere lo stato di cui ha bisogno. Ma ancora non liberiamo il buffer. Ora, f restituisce una semplice struttura che ci dice se è necessario liberare il buffer. Questo ci permette anche di liberare o meno il buffer su diverse chiamate di f.

typedef struct { 
    void* data; 
    int free; 
} freturn; 

void MapArray(void ** src, void * dest, freturn (f)(void *, void*), void* userdata, size_t n, size_t sizeofT) 
{ 
    for(unsigned int i = 0; i < n; i++) { 
     freturn thisreturn = f(src[n], userdata); 
     void* temp = thisreturn.data; 
     memcpy(dest, temp, sizeofT); 
     dest = (char*)dest + sizeofT; 
     if (thisreturn.free) 
      free(temp); 
    } 
} 

Tuttavia, non riesco ancora a capire lo scopo di questa funzione. Tutto questo per sostituire un ciclo for semplice? Il codice che stai cercando di sostituire è più semplice del codice per chiamare la tua funzione, e probabilmente più efficiente e decisamente più potente (possono usare continue/break, per esempio).

Più di questo, C fa davvero schifo per questo tipo di lavoro. Il C++ è di gran lunga migliore. Ad esempio, è piuttosto banale applicare una funzione a ciascun membro di un array.

+0

Un sacco di informazioni succose! Grazie mille. Se potessi sapere quanto ho imparato con la tua risposta! – Lasirc

1
  • Verificare che la funzione f non restituisce un puntatore ad una variabile locale
  • io non sono sicuro di quello che si suppone che il ciclo su j di fare
+0

Il ciclo su j è il mio tentativo di copiare, byte per byte, l'output di un'applicazione 'f' alla voce 'i' di 'dest'. L'ho fatto perché voglio che la mia 'f' restituisca dati di dimensioni variabili. – Lasirc

+0

Si sta usando 'i' all'interno del ciclo anche se – srean

+0

@Lasirc: Ma al momento quel ciclo copia semplicemente lo stesso byte nello stesso punto' elem' volte. Hai bisogno di '* (((char *) dest) + i * elem + j) = * (((char *) temp) + i * elem + j);' al suo interno. –

2

C'è un motivo per cui non c'è niente di simile nella libreria standard C: è quasi impossibile farlo bene in C. Non è possibile copiare il risultato, "byte per byte" su dest[i] - poiché tu? ve cast dest a char *, punta solo a uno char (byte).

Presumibilmente, elem è destinato ad essere la dimensione di qualsiasi tipo f ritorni e n è il numero di elementi in src e dest. In tal caso, il tuo codice non è troppo lontano, ma (come hai apparentemente supposto) il modo in cui stai manipolando i puntatori (in particolare il cast su char *) non lo taglierà.

Anche se lo risolvi, tuttavia, avrai un altro problema: assegnare il tipo di ritorno da f senza conoscere il tipo sarà davvero (davvero) difficile. In realtà, l'unico modo che posso pensare è quello di avvolgere questo codice su in una macro invece:

#define MapArray(s, d, f, n) \ 
do {       \ 
    unsigned i;    \ 
    for (i = 0; i<n; i++)  \ 
     d[i] = f(s[i]);  \ 
} while (0) 

usereste questo qualcosa di simile: nota

int f(int a) { return a + 100; } 

#define elements(array) (sizeof(array)/sizeof(array[0])) 

int main() { 
    unsigned i; 
    int x[] = { 0, 1, 2, 3}; 
    int y[elements(x)]; 

    MapArray(x, y, f, elements(x)); 

    for (i=0; i<elements(x); i++) 
     printf("%d\n", y[i]); 
    return 0; 
} 

Prendere: I' m non raccomandando questo. Questo è un modo per fare ciò che ti viene chiesto, ma come ho detto all'inizio, questo è quasi impossibile da fare bene in C. Questo codice funziona in una certa misura, ma IMO non si qualifica come facendo il lavoro bene.

Problemi correlati