iteratori complessi in C

3

nota: questo è stato inizialmente chiesto su SO .

Parte del mio progetto attuale riguarda l'iterazione su sequenze che non esistono necessariamente. Ad esempio, ho in memoria qualcosa di simile a un database relazionale, quindi diciamo che ho tre array

typedef struct { size_t car_index, size_t part_index } car_part_t;

car_ * cars;
part_t * parts;
car_part * car_parts;

Voglio essere in grado di scorrere tutte le parti di una determinata macchina, per esempio. Voglio in particolare part e non car_part . La mia soluzione attuale è

typedef struct {
    size_t car_index;
    size_t part_index;
    size_t m_car_part_index; // keeps progress
} car_part_iterator_t;

e per avere due funzioni

car_part_iterator_t car_part_first(size_t car_index);

che restituisce un iteratore impostato in modo che it.part_index sia l'indice della parte in parts , it.car_index è l'auto selezionata dal primo parametro e it.m_car_part_index è una variabile 'privata' usata per tracciare progresso nella lista car_parts .

Ho quindi una funzione simile per ottenere il prossimo

bool car_part_next(car_part_iterator_t * it);

che passa alla prossima parte della macchina e torna se l'iteratore è ancora valido. Finalmente per completezza ho

bool car_part_valid(car_part_iterator it);

per verificare se l'iteratore corrente è ancora valido. Quindi può essere usato in un ciclo for come

for (car_part_iterator_t it = car_part_first(car_index);
    car_part_valid(it); car_part_next(&it))
{
    // use it.part_index ...
}

Per me, questa sembra una buona soluzione alla quale sono arrivato dopo alcune prove ed errori. Ho anche sperimentato una funzione che restituisce un iteratore prima del primo risultato, quindi la prossima 'funzione restituirebbe il primo risultato, da utilizzare nei cicli while.

Questo è un buon modello? Presumo uno conosciuto. ha un nome? Ci sono insidie che non vedo?

note:

Su SO la questione è stata messa in dubbio sulla generalità (cioè può essere resa più generale), ma così com'è non sono troppo preoccupato per questo.

È stato anche sottolineato che la funzione valid non è strettamente necessaria per il ciclo, ma consente un ciclo for più naturale ed è talvolta utile anche al di fuori dei loop.

    
posta Gage 15.06.2015 - 10:22
fonte

2 risposte

3

Is this a good pattern?

Sì, in generale.

Non riesco a vedere come interagisce con i tuoi dati in dettaglio, ma funziona bene in molti posti, e probabilmente anche qui va bene.

I assume a known one. Does it have a name?

È solo lo schema iteratore . Può essere più complesso di una lista collegata o di un iteratore di array, ma non è così ovviamente diverso da un iteratore di alberi.

Are there pitfalls I'm not seeing?

Bene, la tua attuale implementazione sembra dipendere dallo stato globale. Generalizzare questo aspetto è probabilmente una buona idea.

Infine, quando hai detto che la sequenza potrebbe non esistere, mi aspettavo di vedere un iteratore che generasse pigramente i valori. Dal momento che non è questo il caso, cosa intendi in realtà?

    
risposta data 17.06.2015 - 15:36
fonte
2

Sì. Questo è un buon modello, ma puoi fare qualcosa di simile:

typedef struct {
   part_t *i, *end;
} car_part_iterator_t;

Quindi avere funzioni

car_part_iterator_t car_part_iterator_first(size_t car_index);
void car_part_next(car_part_iterator* iter); // because I don't know why you need bool there
bool car_part_valid(car_part_iterator iter);

Dopo puoi usarlo in questo modo:

for(car_part_iterator_it it = car_part_iterator_first(index);
    car_part_valid(it); car_part_next(&it)) {
   doSomething(*(it.i));
}

E puoi effettivamente eseguire iterazioni come questa se vuoi:

for(part_t *it = car_part_iterator_first(index); it != car_part_iterator_end(index); it++) {
   // do something..
}

Quindi non devi creare un'altra struttura iteratrice, ma devi comunque creare delle funzioni, anche se puoi omettere quelle funzioni se vuoi ..

Modifica: Poiché questo sito Web non mi consente di commentare, devo incorporare la mia risposta qui. Penso che la * fine in car_part_iterator_t struct dovrebbe controllare solo quello. Impostiamo la * fine per indicare la parte dopo l'ultima parte della macchina corrente. In C, possiamo scorrere su array come questo:

int *array = calloc(sizeof(int), 10);
// we want to iterate over the first 5 elements
int *first = array;
int *end   = array + 5; // the element after the 5th element
for(int *it = first; it != end; it++)
    printf("Hello, %d\n", *it);
free(array);
    
risposta data 16.06.2015 - 10:59
fonte

Leggi altre domande sui tag