lucifugo ha scritto:
Ho fatto un altro semplice esercizio (simile al precedente) in cui ho cercato di seguire il tuo consiglio migliorando la parte logica:
/* Esercizio 1.12 Scrivete un programma che stampi il suo input con
una parola per linea. */
#include <stdio.h>
#define IN 1
#define OUT 0
int main() {
int c, parola;
parola = OUT;
while((c=getchar()) != EOF) {
if(c!=' ' && c!='\n' && c!='\t') {
parola=IN;
putchar(c); }
else {
if(parola==IN) {
putchar(10);
parola=OUT; }
}
}
return 0;
}
Che ne pensi?
L'esercizio è decisamente semplice e la soluzione è didatticamente accettabile.
Certo sarebbe meglio utilizzare una enum per le due costanti IN e OUT che rappresentano i due stati della basilare FSM che implementa l'intera logica del programma.
Inoltre è tassativamente proibito usare costanti numeriche manifeste come in putchar(10): il simbolo '\n' è sempre da preferisi perché portabile anche su sistemi non-ASCII, e più in generale per non creare alcun problema di refactoring e decodifica in casi realistici di applicazione.
Allo stesso modo, è sempre bene anteporre le costanti (tutto ciò che non può essere un lvalue valido) all'operatore di comparazione, in modo da scaricare sul compilatore come errore sintattico quello che è uno degli errori semantici più comuni.
if (IN == parola)
{
...
}
Riguardo al codice della prima if(), la logica di valutazione in short circuit del C la rende comunque accettabile prestazionalmente. In generale, tuttavia, è sempre meglio evitare di affastellare troppe condizioni logiche nel medesimo statement, per questioni di leggibilità e mantenibilità del codice: in casi di questo genere, ad esempio, è sempre un'ottima idea usare la funzione strchr() controllando se il carattere in input appartiene alla stringa dei separatori, come in
strchr(" \n\r\t", c)
Una collezione organica di queste raccomandazioni è contenuta nei testi avanzati indicati nella seconda parte di
questa nota bibliografica, convalidata da oltre due decenni di didattica del linguaggio C ai massimi livelli applicativi.
sebaldar ha scritto:
...ci sono una infinità di modi per implementare lo stesso algoritmo e ciascuno sarà portato a supporre che la propria via sia la migliore.
L'informatica applicativa è anche un'arte (soprattutto nel senso sennettiano di "attività artigianale"), ma è soprattutto una scienza e una disciplina ingegneristica. Esistono delle
metriche ben precise (function points, McCabe, complessità ciclomatica, profiling a runtime e dozzine di altre tecniche miste) che consentono di valutare in modo oggettivo e impersonale se un codice è "migliore" o "peggiore" di altri in un senso tecnico estremamente ben definito, confrontando varie implementazioni sensate sulla base di una specifica applicativa il più possibile precisa e aderente alla realtà.
Il software engineering si occupa appunto di studiare e creare programmi oggettivamente "migliori" secondo parametri ben definiti, lasciando a margine i personalismi e misurando invece i costi, i tempi, la complessità, la manutenibilità: stimando poi a parte tutto ciò che risulta purtroppo non quantificabile, come la "leggibilità" o la "comprensibilità" che dipendono strettamente dal programmatore e dalla sua esperienza, e si rivelano unicamente tramite misure indirette (un codice poco comprensibile e mal documentato causerà costi di manutenzione ovviamente maggiori e avrà maggior probabilità di causare ulteriori bug, con aggravio dei costi...).
In ogni caso, esiste un complesso e completo quadro oggettivo e intersoggettivo di riferimento al quale implicitamente ci si riconduce quando un professionista con la mia esperienza spende un giudizio di merito sulla qualità complessiva di un codice, sia pure un banale esercizio. Non stiamo vagamente cercando di confrontare Kandinski con Giotto.