[C] Generalizzare il campo di un albero

di il
10 risposte

[C] Generalizzare il campo di un albero

Salve mi sto esercitando in un programma utilizzando delle strutture ad albero in C e mi trovo bloccato in un punto.
Ho una struttura ad albero con 8 campi differenti tra cui stringhe, double ed int. Adesso per quanto riguarda i campi numerici vorrei effettuare una media tra tutti i nodi presenti di un campo preciso.
Nel concreto ad esempio, il mio albero possiede un campo che indica la velocità e vorrei calcolare la media della velocità per tutti i nodi dell'albero, dove il campo lo decide l'utente. La domanda quindi è la seguente:
Come posso scrivere una funzione generalizzata che calcola la media? Ovvero una funzione unica che a seconda del campo scelto dall'utente è in grado di centrarsi direttamente sul campo scelto invece di dover invocare/scrivere la funzione per ogni campo che tutto mi sembra tranne che decente? (Penso si debba fare qualcosa che mi sta sfuggendo nella definizione dei parametri effettivi alla funzione) Spero di essermi spiegato

10 Risposte

  • Re: [C] Generalizzare il campo di un albero

    Purtroppo non è possibile indicizzare i campi una struct. Un modo può essere fare una sola funzione con degli if o case per scegliere il campo.
  • Re: [C] Generalizzare il campo di un albero

    Cioè se devo fare la media di N grandezze fisiche (campi dell' albero) una per volta scelte a discrezione dell'utente dovrei chiamare la funzione N volte gestendo con switch? Se questo N inizia a crescere risulta molto macchinoso
  • Re: [C] Generalizzare il campo di un albero

    Ti stai ponendo il problema in modo sbagliato.
    La struct non puo' essere indicizzata, MA un vettore si.
    OPPURE una mappa stringa->valore
    Ma si possono trovare altre n-mila soluzioni.

    Questo per dire che c'e' una STRETTISSIMA relazione tra ALGORITMO e STRUTTURA DATI.

    un algo applicato alla struttura dati sbagliata sara' sempre 'complicatissimo'.
    Se invece lo applichi alla struttura dati corretta, sara' di immediata implementazione.
  • Re: [C] Generalizzare il campo di un albero

    È richiesto esplicitamente dalla traccia di utilizzare una struttura dati di tipo albero ecco perché
  • Re: [C] Generalizzare il campo di un albero

    Questo non vuol dire che non ci sia 'spazio di manovra'.
    Anzi, c'e' SICURAMENTE proprio perche' e' un esercizio che ha il compito di 'far ragionare'.

    Se invece pensi che non ci sia e la tua soluzione sia l'unica possibile, allora vuol dire che non si puo' fare di meglio.
  • Re: [C] Generalizzare il campo di un albero

    Questo è quello che sono riuscito a fare ma il risultato non mi piace e non mi viene altro metodo
    case minimo:
                {
                    switch (comando)
                    {
                        case 1:
                            trova_minimo(ris, (int)nodo->giri_m, n_val);    
                            break;
                        case 2:
                            trova_minimo(ris, nodo->temp_m, n_val);    
                            break;
                        case 3:
                            trova_minimo(ris, nodo->temp_c, n_val);    
                            break;
                        case 4:
                            trova_minimo(ris, nodo->vel, n_val);    
                            break;
                        case 5:
                            trova_minimo(ris, nodo->acc_x, n_val);    
                            break;
                        case 6:
                            trova_minimo(ris, nodo->acc_y, n_val);    
                            break;
                        case 7:
                            trova_minimo(ris, nodo->acc_z, n_val);    
                            break;
                    }
                        break;
                }
                    break;
    questo switch è in una visita posticipata per alberi binari di ricerca (non è un albero bilanciato e non è ordinato per nessuna di quelle grandezze).
    minimo è un tipo enum dato dalla funzione chiamante, mentre comando indica per quale dato del campo trovare il minimo ecc... Dato che questo switch si ripete anche per il massimo e per altri calcoli avere queste lunghe righe di codice mi irrita parecchio e volevo trovare un modo per snellirlo
  • Re: [C] Generalizzare il campo di un albero

    Aggiorno su quello che sono riuscito a modificare. Vi chiedo come potrebbe essere ulteriormente migliorato

    Sempre in visita per albero binario
    case minimo:
                    trova_minimo(ris, seleziona_grandezza(nodo, comando), n_val);
    dove la funzione seleziona_grandezza:
    switch (comando)
        {
            case 1:
                grandezza = nodo->giri_m;
                break;
            case 2:
                grandezza = nodo->temp_m;
                break;
            case 3:
                grandezza = nodo->temp_c;
                break;
            case 4:
                grandezza = nodo->vel;    
                break;
            case 5:
                grandezza = nodo->acc_x;    
                break;
            case 6:
                grandezza = nodo->acc_y;    
                break;
            case 7:
                grandezza = nodo->acc_z;    
                break;
        }
        return(grandezza);
    In questo modo sono riuscito a eliminare molte righe di codice superflue però pecco di efficienza perchè ogni volta che c'è un nodo dell'albero da visitare si fa rifermento a questa funzione. Vorrei sapere se esiste un modo per cui si fa riferimento una e una sola volta alla funzione così che si procura il dato su cui fare i calcoli una volta per tutte.
    Alla fine sono poche le istruzioni della funzione di riferimento per la grandezza però se esiste un altro modo ben venga.
  • Re: [C] Generalizzare il campo di un albero

    Il tuo problema è uno dei motivi per cui hanno inventato la programmazione funzionale e ad oggetti.
    Si tratta di chiamare un algoritmo su una collezione di dati e "dargli qualcosa da fare" su ogni dato. Quel qualcosa da fare glielo si passa tramite un puntatore a funzione in C, o tramite un oggetto funzione in linguaggi come C++ (che poi contiene sempre un puntatore a funzione).
    Un altro problema ancora è quello del tipo di dato su cui lavorare, che nel tuo caso non è sempre lo stesso e il C non fa conversioni facili.

    Ho provato a fare una piccola implementazione in cui implemento tramite struct in C un piccolo oggetto funzione chiamato Car_functor, che oltre al puntatore a funzione, ha anche un campo in cui memorizzare il risultato.
    Il tipo Car_data è una union, che sembra una struct, ma in realtà ha un solo campo che ha la dimensione del tipo più grande (double), quindi può memorizzare dati di tipi diversi (ma sempre uno per volta!).

    Questo ti permette di chiamare gli if / case una sola volta all'inizio e non per ogni valore dell'insieme, ma non di eliminarli.

    Non mi sono messo a fare alberi, ma ho usato un banale array.
    La funzione WorkArrayCars è quella che nel tuo caso dovrebbe visitare l'albero in profondità e poi "fare qualcosa" sui nodi visitati.

    Te la posto a scopo illustrativo, non prenderla come LA soluzione migliore e comunque ti sconsiglio di portarla nella consegna, anche perché ne hai già una.
    
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef union{
        int intval;
        double doubleval;
    } Car_data;
    
    typedef struct{
        int x;
        double y;
    } Car;
    
    typedef struct Car_functor Car_functor;
    typedef void (*Fun)(Car*, Car_functor*);
    
    struct Car_functor {
        Car_data result;
        Fun function;
    };
    
    
    void cumulate_y (Car* car, Car_functor* fctr){
        fctr->result.doubleval += car->y;
    }
    
    void cumulate_x (Car* car, Car_functor* fctr){
        fctr->result.intval += car->x;
    }
    
    void workArrayCars (Car cars[], int dim, Car_functor* fctr)
    {
        for (int i=0;i<dim;i++)
            fctr->function(&cars[i], fctr);
    }
    
    int main()
    {
        Car cars[5];
        int ch;
        Car_functor fctr;
        
        for(int i=0;i<5;i++){
            cars[i].x = i;
            cars[i].y = 0.0 + i;
        }
        
        for(int i=0;i<5;i++){
            printf("x: %d\t", cars[i].x);
            printf("y: %.1f\n", cars[i].y);
        }
        
        printf ("\nInserire: \n1: media x\n2: media y\n");
        scanf("%d",&ch);
        
        Car_data media;
        switch(ch)
        {
        case 1:
            
            fctr.result.intval = 0;
            fctr.function = cumulate_x;
            workArrayCars(cars, 5, &fctr);
            media.intval = fctr.result.intval / 5;
            printf("\nMedia: %d", media.intval);
            break;
        case 2:
            fctr.result.doubleval = 0;
            fctr.function = cumulate_y;
            workArrayCars(cars, 5, &fctr);
            media.doubleval = fctr.result.doubleval / 5;
            printf("\nMedia: %.1f", media.doubleval);
            break;
        }
        
        return 0;
    }
    
  • Re: [C] Generalizzare il campo di un albero

    Grazie Alex, mi dispiace dirti però che ci ho provato e fallito. Non riesco a capire come devo fare nel mio caso specifico a indirizzare una volta per tutte il campo. Se saresti gentile da farmi vedere un qualcosa nel concreto così da poter assimilare bene ne sarei felice
  • Re: [C] Generalizzare il campo di un albero

    Puoi fare un esempio di come hai implementato la trova_minimo?
Devi accedere o registrarti per scrivere nel forum
10 risposte