[prog-it] Re: holaz :)

  • From: "Seeman79" <Seeman79@xxxxxxxxx>
  • To: <prog-it@xxxxxxxxxxxxx>
  • Date: Sun, 12 May 2002 19:06:39 +0100


From:"www.xxxxwebmaster.cjb.net" <xxxxwebmaster@xxxxxxxxxxxxxxxxxxxxx>
Subject: Re: holaz :)
Date: Wed, 8 May 2002 13:56:43 +0200

---
La seguente mail e' in riferimento al thread che si trova
all'indirizzo:

http://www.alground.com/forum/viewthread.php?tid=229

La domanda mi era stata rivolta anche in privato, ma posto
qui perche' interessa anche il forum di Alground che se lo
ritiene utile potra' pubblicare questa risposta nel suo
forum.
La mia mail e' in prima persona rivolta ad XXXX perche'
e' quello che ha iniziato il thread.
---

Mi fa piacere che tu ti ricordi quello che ti ho detto un
po' di tempo fa, ossia che la cosa piu' importante per un
listato e' che ALMENO faccia quello che vuoi, ma un minimo
di "dignita'" nel codice bisognerebbe sempre mettercela :)
Vediamo di focalizzare alcuni punti che, secondo me, sono
il fattor comune di tutti i tuoi listati.

0.      Se guardiamo con attenzione il tuo listato, ci accorgiamo
                che tu hai bisogno di 7 variabili, di cui 6 dello stesso
                tipo (float).
                Come mai tu ne utilizzi 27? :)
                Una possibile struttura dati da utilizzare potrebbe essere:
                
                #define MAX_LEN 50 /* il tuo 389 mi pare esagerato :) */
    #define VOCI                 6 

    typedef enum {Reddito_Imponibile, Oneri_Deducibili, Detrazioni_Imposta,
                                                            Imposta_Netta, 
Ritenute_Subite, Imposta_Dovuta
                                                     } Nome_Voce;
                                                 
    typedef float Importo[VOCI];

    typedef struct Info {
                                                                                
        char nome[MAX_LEN];
                                                                                
        Importo valore;
                                                                                
} Param;
    
                typedef struct Info *Options;

                In modo che poi accedi ai vari campi con la "coscienza" di 
quello che
                stai facendo; ad esempio:
                
                Options cliente = NULL;

    /* allochi la struttura */
                cliente->nome;
                cliente->valore[Reddito_Imponibile];
                /* Indicano campi ben precisi, facili ed intuitivi da capire */
                
                Almeno elimini tutta la strage di variabili che hai fatto tu :)

1.      Perche' aprire il file all'inizio se poi ci scrivi solo
                alla fine? In questo modo se l'apertura fallisse, ti
                toglieresti anche la possibilita' di vedere a video i 
                risultati.
                
2.      Il logo celebrativo del programmatore, mettilo in una
                procedura       cosi' lo richiami ogni volta che serve; fai
                si che la procedura accetti in input un "FILE *" e se
                vuoi stamparlo sullo schermo gli passi "stdin", mentre
                se ti serve stamparlo su file gli passi il puntatore
                alla struttura di tipo "FILE *" appunto.
                
3.      Quando utilizzi la scanf, per leggere una stringa non
                necessiti dell'operatore '&' perche' gli operandi
                '&*' si annullano a vicenda (se hai un minimo di
                famigliarita' con i puntatori ti e' facile capire
                perche').
                
4.      Consideriamo questo pezzo del tuo codice:
                ---
    if(med <= 129.11) med1=0; else med1 = med-129.11;
                ---
                Tutto cio' si puo' risolvere con una macro e, visto
                che esegui una sottrazione ed un controllo sul medesimo
                valore, restituendo eventualmente zero, il controllo puo'
                essere tranquillamente un minore stretto.
                Nel senso: se il primo controllo fosse un minore stretto
                e "med" fosse proprio "129.11", il controllo fallisce
                e tu ritorni "med1 = med-129.11" che e' esattamente
                zero (ossia quello che volevi tu).
                Assicurati che i tuoi controlli non siano ridondanti.
                Potresti usare una cosa del tipo:
                
                #define LIMITE 129.11
                #define MEDICHE(x) ( (x < LIMITE) ? 0 : (x - LIMITE) ) 
                
5.      Ragioniamo ora sul calcolo delle varie aliquote per
                scaglioni. Cosi' come hai scritto il codice tu saltano
                agli occhi subito un po' di difetti:
                
                a) Utilizzi costanti all'interno del codice senza assegnarle
                                dei nomi; cio' significa che se un giorno 
dovrai modificare
                                il codice, dovrai spulciarlo dall'inizio alla 
fine ricercando
                                tutte le occorrenze e cambiarle.
                
                b) Molte parti del tuo codice sono ripetitive; occorre 
automatizzare
                                con funzioni matematiche ben definite.
                                
                c) Fai uso di un numero di variabili spropositato, del tutto
                                inutili; a parte il puro folclore non c'e' 
nessuna spiegazione
                                (razionale) a tutto cio'.
                
                d) ecc. ecc.
                
                Esaminiamo questo frammento del tuo codice:
                ---
                if(redr <= 10329.14) irpef1= redr*18/100;
                else irpef1=1859.25;
                ---
                Questa procedura e quella per il calcolo dell'aliquota piu' alta
                sono diverse da tutte le altre che risultano, invece, avere 
tutte
                una matrice comune. Tenendo presente quello che ti ho detto 
prima
                questo problema si puo' risolvere (tra i vari modi) con questa 
macro:
                
                #define ALIQUOTA_MIN 18
    #define ALFA_MIN 10329.14
    #define BETA_MIN 1859.25
                #define IRPEF_MIN(x)    ( (x <= ALFA_MIN) ? (x * ALIQUOTA_MIN / 
100) : BETA_MIN )

                Sembra essere abbastanza Chiara (o Marta se preferisci): no?
                Se e quando cambieranno i "limiti" ma non il modo di calcolare 
l'irpef
                ti bastera' agire sulle aliquote e su ALFA_MIN e BETA_MIN senza 
toccare
                una riga di codice.

                Esaminiamo ora le altre procedure, esclusa quella per il 
calcolo dell'aliquota
                massima; sono tutte del tipo:
                ---
                ali(n)=redr-alfa(n);
                if (ali(n) <beta(n)) ali(n)=redr-alfa(n);
                if (ali(n) >=beta(n)) ali(n) = beta(n);
                if (ali(n) <=0) ali(n)=0;
                irpef(n)=ali(n)*aliq/100;
                ---
                Le variabili della serie "ali(n)" non servono assolutamente a 
nulla ed ora
                vediamo di dimostrarlo :)
                Esaminiamo un generico caso; tu imponi che:
                
                ali = redr - alfa;
                
                E poi ti chiedi:
                
                Se "ali < beta" allora "ali = redr - alfa"
                
                Allora io mi chiedo: chi e' "ali"? Beh "ali" e' "redr - alfa" a 
detta di XXXX;
                allora sostituendo, la condizione logica diventa:
                
                Se "redr - alfa < beta" allora /* bla bla bla */
                
                Con un misero quanto banale passaggio matematico, si conclude 
che la condizione
                corretta e':
                
                Se "redr < (alfa + beta)" allora /* bla bla bla */
                
                Continuando nel ragionamento, ci accorgiamo che il tuo secondo 
"if" e' in realta'
                un "else" del primo "if"... Chiaro no? Se non e' minore di una 
cosa allora puo'
                essere solo maggiore/uguale (i saggi, che di analisi matematica 
non sapevano
                nulla, dicevano "se non e' zuppa e' pan bagnato") :)
                
                Quindi lo tralasciamo perche' non ci serve per ora; l'ultimo 
"if" invece;
                
                Se "ali <= 0" allora /* bla bla bla */
                
                Solita cosa... ma chi e' "ali"? Sempre lui, per cui sostituendo:
                "redr - alfa <= 0" implica che "redr <= alfa"
                (piu' tardi ti faro' notare che questa condizione e' 
ridondante).
                
                Le condizioni logiche che abbiamo fino ad ora sono:
                Se "redr < (alfa + beta)" allora /* bla bla bla */
                else /* bla bla bla */
                Se "redr <= alfa" allora /* bla bla bla */
                
                Che cosa puoi notare? L'ordine delle condizioni non e' corretto 
a livello logico,
                infatti la prima condizione ci dice se "redr" e' minore di 
"alfa+beta", ma non ci
                assicura che sia anche minore di "alfa" (in pratica ci fornisce 
una condizione
                necessaria ma non sufficiente, perche' la seconda e' piu' 
restrittiva). Per tanto
                il flusso logico corretto sarebbe:
                
                Se "redr <= alfa" allora "zero"
                altrimenti Se "redr < (alfa + beta)" allora "redr - alfa"
                altrimenti "beta"
                
                Come detto ora ti faccio notare che il primo controllo logico 
e' ridondante,
                in quanto definito gia' nel caso 2; immagina di mettere un 
minore stretto al
                primo controllo e che "redr" sia proprio uguale ad "alfa". Cio' 
che succede e'
                che il primo controllo fallisce, mentre il secondo e' 
verificato (per ogni beta
                maggiore di zero; ma se fosse zero si andrebbe al terzo 
controllo che restiruisce
                beta, cioe' zero e cioe' giusto :P), per cui si ritorna "redr - 
alfa"; ma "redr"
                e' uguale ad "alfa" per ipotesi, per cui restituisce "zero" :)
                Concludendo il flusso corretto risulta: 
                
                Se "redr < alfa" allora "zero"
                altrimenti Se "redr < (alfa + beta)" allora "redr - alfa"
                altrimenti "beta"
                 
                In sistanza il tutto si puo' risolvere con una macro, del tipo:
                
                #define IRPEF_MED(x,y,z,w) ( (x < y) ? 0 : ( (x < ( y + z)) ? 
((x - y)* w / 100) : (z * w / 100)) )
   
          Dove passerai, al posto della 'x' il "redr", 'y' ed 'z' i rispettivi 
"alfa" e "beta"
                ed al posto di 'w' l'aliquota relativa allo scaglione.
                In sostanza potresti definire una cosa del tipo:
                
                #define ALIQUOTA_1 24
    #define ALFA_1 10329.14
    #define BETA_1 5164.56

    #define ALIQUOTA_2  32
    #define ALFA_2 15493.72
    #define BETA_2 15493.69

    #define ALIQUOTA_3  39
    #define ALFA_3 30987.41
    #define BETA_3 38734.27 

                Questa macro e' definita per tutti questi scaglioni (e per 
eventuali futuri strutturati
                nella stessa maniera).
                
                Ultimo caso da analizzare e' il calcolo dell'aliquota massima; 
il tuo codice recita:
                ---
                ali5=redr-69721.41;
                if (ali5 <= 0) ali5=0;
                if (ali5 >69721.41) ali5 = redr - 69721.41;
                irpef5=ali5*45/100;
                ---
                Questo caso e' analogo al calcolo dell'aliquota piu' bassa (ma 
non uguale purtroppo).
                Pero' vorrei farti notare una cosa: a che cosa ti serve il 
secondo "if"?
                Assegni ad un "ali5" un valore; poi dici che se e' maggiore di 
una data quantita'
                gli riassegni lo stesso valore; perche' prima che valore c'era? 
:)
                L'unico "if" che ha senso e' il primo; per tanto potresti 
risolvere il problema
                nella seguente maniera:
                
                #define ALIQUOTA_MAX    45
    #define ALFA_MAX 69721.41
    #define IRPEF_MAX(x) ( ( (x - ALFA_MAX) < 0) ? 0 : ((x - ALFA_MAX) * 
ALIQUOTA_MAX / 100) )
                
                Anche questa mi sembra molto chiara e non necessita di 
ulteriori commenti.
                
6.      Risulta inutile dichiarare 300.000 variabili per i sub-totali, quando 
te ne
                bastano, esagerando, 2 per ottenere lo stesso risultato. La 
variabile che
                contiene il totale, puo' anche contenere i sub-totali, poiche' 
una sommatoria
                come la tua e' cosi' fatta:
                x1 + x2 + x3 + ... +xn
                Se io scrivessi che (x1 + x2) = y e dicessi:
                y + x3 + ... + xn
                sei d'accordo che il risultato non cambia?
                Ho detto esagerando 2 perche' se non ti serve printare a video 
i sub-totali
                (o ti serve ma non vuoi richiamare 2 volte la stessa macro), 
utilizzi una
                variabile temporanea che usi per printare a video e per 
sommarla al totale.
                Nella fattispecie, per il tuo codice, una cosa del tipo:
                ---
                temp = IRPEF_MIN(redr); tot += temp;
                printf("\nIrpef 18: %.2f", temp);
                
                temp = IRPEF_MED(redr, ALFA_1, BETA_1, ALIQUOTA_1); tot += temp;
                printf("\nIrpef 24: %.2f", temp);
                
                temp = IRPEF_MED(redr, ALFA_2, BETA_2, ALIQUOTA_2); tot += temp;
                printf("\nIrpef 32: %.2f", temp);

                temp = IRPEF_MED(redr, ALFA_3, BETA_3, ALIQUOTA_3); tot += temp;
                printf("\nIrpef 39: %.2f", temp);

                temp = IRPEF_MAX(redr); tot += temp;
                printf("\nIrpef 45: %.2f", temp);
                
                printf("\nIrpef totale: %.0f\n\n", tot);
                ---
                Ottieni lo stesso risultato con un terzo delle righe che hai 
scritto tu :)
                Se poi addirittura non ti servisse neanche avere i risultati 
intermedi
                (per fare una cosa cosi', ricordati di inizializzare a zero 
"tot"), puoi
                tranquillamente eliminare "temp", e sommare tutto direttamente 
a "tot".
                
7.      Poiche' utilizzi la "conio.h", la chiamata a:
                system("PAUSE");
                La puoi rimpiazzare con una "getch();"
                mentre la:
                system("CLS");
                con una clrscr();
                Il Borland ti dava dei warning per le chiamate "system" perche' 
essa e'
                definita in "stdlib.h" che tu NON hai incluso :)
                
8.      Il devC++ ti da quei problemi, come ti ho gia' detto, perche' la 
"conio.h",
                non e' una libreria "standard", per cui un compilatore non e' 
tenuto ad
                averla e linkarla. Inoltre, da specifica, non deve essere 
utilizzata per
                applicazioni a 32-bit, per via del fatto che utilizza chiamate 
a basso
                livello e OS a 32-bit in modalita' protetta non amano cio' 
(niente
                interrupt... a priori; anche se, ad esempio, Linux ha un 
interrupt INT80h
                con svariate funzioni da poter essere usato), con risultato il 
crash del
                programma. Di fatto sospetto, che non sia neanche presente.
                
                Per ovviare a cio' potresti utilizzare la codifica ANSI dei 
colori, a patto
                che l'utilizzatore del tuo programma carichi nel suo config.sys 
il driver
                "ansi.sys".

9.      Quando chiami la cprintf("\r"); solo per questo, e' uno spreco; 
inserisci
                uno '\r' in piu' nella prossima riga che stampi a video ed 
ottieni lo stesso
                risultato con una chiamata a funzione in meno.

Questo e' quanto e scusa se ti ho scritto solo ora, ma come sai durante la
settimana sono molto impegnato.

Alla prossima.
Ciao.


-- 
Nulla contribuisce alla serenita' dell'anima quanto il non avere nessuna 
opinione.




Other related posts: