2010-10-28 7 views
10

Ich habe Schwierigkeiten, eine generische 'Karte' Funktion über Arrays zu implementieren. ich mit dem folgenden Entwurf begonnen:Implementieren einer generischen 'Karten' Funktion über Arrays 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); 
} 

Ich verstehe, warum es nicht richtig - ich bin Gießen (char *), bevor es zu ‚f‘ zu geben - aber ich bin jetzt demotiviert und kann nicht kommen mit einer Lösung. (Ich mache das während des Lernens. C)

Mein Grundprinzip war, das Ergebnis von 'f' zu erhalten und Byte für Byte in dest [i] zu kopieren.

Können Sie mir einen Hinweis geben?

+0

für das, was Sie brauchen, um diesen Zeiger zu funktionieren und welche Karte, die Sie implementieren möchten, ich glaube nicht, Sie wollen einige zum Beispiel Kartencontainer von sgi? – Svisstack

+0

Es ist die typische 'Karten'-Anwendung, die Sie in praktisch jeder funktionalen Sprache finden können. Sie senden eine Liste, eine Funktion und sie gibt die als solche zusammengesetzte Liste zurück: (f (l [1]), ..., f (l [n])). – Lasirc

Antwort

18

Ihr erstes Problem zu tun, ist, dass Sie‘ zu viel in einigen Ausdrücken. Sie müssen es aufschlüsseln.

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); 
} 

Jetzt Ihr zweites Problem. Sie malloc einen Puffer, dann Sie .. diesem Zeiger zuweisen? Wiederholt? Dann frei nur das Ergebnis des letzten f-Anrufs? Das ist völlig unnötig.

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]; 
     } 
    } 
} 

Jetzt Ihr drittes Problem. Sie übergeben einen Zeiger in - aber nur zum char. Du gehst nicht in eine Leere *. Dies bedeutet, dass Ihre Funktion nicht generisch sein kann - f kann nicht auf irgendetwas angewendet werden. Wir brauchen ein Array von void * s, damit die Funktion einen beliebigen Typ als Argument annehmen kann. Wir müssen auch die Größe des Typs als Argument nehmen, damit wir wissen, wie weit wir uns entlang des Ziels bewegen müssen.

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; 
    } 
} 

Wir haben noch ein anderes Problem - der Speicher von temp. Wir befreien es nicht. Wir übergeben auch kein Benutzerdatenargument in f, das es ermöglichen würde, Heap-allokierten Speicher zurückzugeben, den wir nicht frei haben müssen. Der einzige Weg, auf dem f funktionieren kann, ist, wenn es einen statischen Puffer zurückgibt.

Jetzt kann f auf so ziemlich alles, was es mag, arbeiten und halten, was auch immer es braucht. Aber wir befreien den Puffer immer noch nicht. Nun gibt f eine einfache Struktur zurück, die uns sagt, ob wir den Puffer freigeben müssen. Dies erlaubt uns auch, den Puffer bei verschiedenen Aufrufen von f freizugeben oder nicht freizugeben.

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); 
    } 
} 

Allerdings verstehe ich immer noch nicht den Zweck dieser Funktion. All dies um eine einfache for-Schleife zu ersetzen? Der Code, den Sie ersetzen möchten, ist einfacher als der Code zum Aufrufen Ihrer Funktion und wahrscheinlich effizienter und definitiv leistungsfähiger (sie können beispielsweise continue/break verwenden).

Mehr als das, C wirklich saugt für diese Art von Arbeit. C++ ist viel besser. Es ist dort ziemlich trivial, zum Beispiel eine Funktion auf jedes Mitglied eines Arrays anzuwenden.

+0

Viele saftige Informationen! Vielen Dank. Wenn Sie nur wissen könnten, wie viel ich mit Ihrer Antwort gelernt habe! – Lasirc

1
  • Überprüfen Sie, ob die Funktion f nicht einen Zeiger auf eine lokale Variable Rückkehr
  • Ich bin nicht sicher, was die Schleife über j soll
+0

Die Schleife über j war mein Versuch, Byte für Byte die Ausgabe einer Anwendung 'f' zum Eintrag 'i' von 'dest' zu kopieren. Ich tat dies, weil ich möchte, dass mein 'f' Daten variabler Größe zurückgibt. – Lasirc

+0

Sie verwenden 'i' innerhalb der Schleife obwohl – srean

+0

@Lasirc: Aber im Moment kopiert diese Schleife nur das gleiche Byte an die gleiche Stelle' elem' mal. Du brauchst '* (((char *) dest) + i * elem + j) = * (((char *) temp) + i * elem + j); –

2

Es gibt einen Grund, warum so etwas nicht in der C-Standardbibliothek ist - es ist fast unmöglich, es in C gut zu machen. Sie können das Ergebnis nicht "byteweise" kopieren nach dest[i] - seit Sie hat dest zu einem char * umgewandelt, es zeigt nur auf einen char (Byte).

Vermutlich ist elem bestimmt die Größe wie auch immer geartete f kehrt zu sein, und n ist die Anzahl der Elemente in src und dest. In diesem Fall ist Ihr Code nicht zu weit entfernt, aber (wie Sie anscheinend vermutet haben) wird die Art und Weise, wie Sie die Zeiger manipulieren (insbesondere die Umwandlung in char *), nicht ausreichen.

Auch wenn Sie das beheben, haben Sie noch ein anderes Problem: die Zuweisung des Rückgabetyps von f ohne zu wissen, der Typ wird wirklich (wirklich) schwierig sein. In der Tat, über die einzige Art, wie ich denken kann ist, diesen Code wrap up in ein Makro statt:

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

Sie diese so etwas wie dieses verwenden würde:

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; 
} 

Beachten Sie: Ich m nicht Empfehlung. Diese ist ein Weg, um zu tun, was Sie gefragt werden, aber wie gesagt, um damit zu beginnen, ist dies fast unmöglich, in C gut zu machen. Dieser Code funktioniert zu einem gewissen Grad, aber IMO qualifiziert es nicht als tun Arbeit gut.

Verwandte Themen