PDA

View Full Version : [C# / solo 2.0]Codice Fiscale


RaouL_BennetH
16-07-2010, 13:45
Ciao a tutti :)

Sto provando ad implementare da me, a scopo didattico, il calcolo del codice fiscale. Sto partendo dall'elemento più semplice da elaborare, ovvero la data di nascita per estrarne il mese e l'anno per poi via via elaborare il resto (per me molto complesso) come cognome, nome e codice di controllo.

Sto cercando di vedere le cose da più punti di vista e sono partito da quella che ritenevo l'implementazione più semplice:


private string EstraiAnnoMese(DateTime dt)
{
string mese = "";
string anno = dt.ToString("yy");

switch(dt.Month)
{
case 1:
mese = "A";
break;

case 2:
mese = "B";
break;

//et...
case 12:
mese = "T";
break;

default:
break;
}
return mese + anno;
}


Un'altra implementazione alla quale stavo pensando è l'utilizzo di un dizionario:


Dictionary<string, int> monthDict = new Dictionary<string, int>();
monthDict.Add("A", 1);
monthDict.Add("B", 2);
////
monthDict.Add("T", 12);

//blabla
foreach(KeyValuePair<string, int> v in monthDict)
{
if(v.Value = dt.Month)
myMonth = v.Key;
}


Vorrei sapere il vostro parere in merito :)

Grazie mille :)

RaouL.

banryu79
16-07-2010, 14:07
Vorrei sapere il vostro parere in merito :)

Visto che il range dei valori validi per i mesi restituiti da DateTime è dato dall'intervallo 1-12, potresti sostituire il dizionario con un array di caratteri e ottenere il carattere del mese N accedendo all'elemento di indice N-1.

RaouL_BennetH
16-07-2010, 14:56
Visto che il range dei valori validi per i mesi restituiti da DateTime è dato dall'intervallo 1-12, potresti sostituire il dizionario con un array di caratteri e ottenere il carattere del mese N accedendo all'elemento di indice N-1.

Ciaooo :)

Dici quindi una cosa tipo:



char[] monthCode = {'A', 'B', 'et.......'};
char c = monthCode[myDateTime.Value.Month - 1];



??

RaouL_BennetH
16-07-2010, 15:13
effettivamente... almeno per anno, mese e giorno sono giunto quindi, al momeno, a:


private string GetAnnoMeseGiorno(DateTime dt, bool isLady)
{
return string.Format("{0:yy"}, dt) + mese[dt.Month - 1] + getGiorno(dt.Day, true);
}


Ora provo a ragionare su come trattare cognome e nome.... :sperem:

Ryuzaki_Eru
16-07-2010, 16:57
Se ti interessa un paio di anni fa ho scritto un programma che calcola il codice fiscale in Python, se vuoi ti linko i sorgenti.

RaouL_BennetH
16-07-2010, 17:53
...mmmm... non pensavo di stare così a zero :(

allora, documentandomi su come operare per il nome e il cognome, leggo che:
(lascio solo il cognome che per il nome ci sono altri casotti....)


L'algoritmo di calcolo


Cognome
Sono necessarie come detto prima 3 caratteri per rappresentare il cognome, e sono la prima la seconda e la terza consonante del cognome.
E' possibile che le consonanti siano meno di tre, in questo caso è possibile aggiungere le vocali nell'ordine in cui compaiono nel cognome.
Per cognomi più corti di 3 caratteri, è possibile sostituire il carattere mancante con la lettera X.
Chiaramente se ci sono cognomi con più parti, è necessario rimuovere gli spazi e considerare tutto come un cognome unico.

Esempi:
(Normale) Cognome : "ROSSI" - Codice Cognome : "RSS"
(Solo due consonanti) Cognome : "RIVA" - Codice Cognome : "RVI"
(Cognome minore di 3 car.) Cognome : "RE" - Codice Cognome : "REX"
(Cognome composto) Cognome : "DE CRESCENZO" - Codice Cognome : "DCR"


Ho pensato allora di implementare un array per le vocali ed uno per le consonanti. Dopodichè dovrei contare quante consonanti ci sono nella stringa e fare i vari casi.

E ho già i primi intoppi al solo scrivere su "carta" :D


foreach(char c in testoCognome)
{
foreach(char consonante in listaConsonanti)
{
if(c == s)
{
//non riesco a pensare a come fargli prendere solo le prime tre consonanti...
}
}
}

astorcas
16-07-2010, 18:02
sai usare le regex? :D

RaouL_BennetH
16-07-2010, 18:09
sai usare le regex? :D

Ehm.. no ... :(

Ma ho tanta buona volontà :)

un piccolo passo però l'ho fatto (almeno per andare avanti)


int count = 0;

if(c == s)
{
count = cognome.IndexOf(s);
//etc..
}


Ma ti sarei davvero grato se mi indirizzassi anche all'uso delle regex. Come ho detto, sto facendo tutto sto casino solo a scopo didattico, dato che di controlli per calcolare, validare, etc.. per il codice fiscale ne esistono a bizzeffe :)

ndakota
16-07-2010, 18:14
L'ho fatto anche io qualche anno fa, in un penoso PHP procedurale. Il tuo sarà sicuramente più bello :)

astorcas
16-07-2010, 18:21
Ehm.. no ... :(

Ma ho tanta buona volontà :)

un piccolo passo però l'ho fatto (almeno per andare avanti)


int count = 0;

if(c == s)
{
count = cognome.IndexOf(s);
//etc..
}


Ma ti sarei davvero grato se mi indirizzassi anche all'uso delle regex. Come ho detto, sto facendo tutto sto casino solo a scopo didattico, dato che di controlli per calcolare, validare, etc.. per il codice fiscale ne esistono a bizzeffe :)

va bene dai allora lasciamo stare per ora le regex, visto che è a scopo didattico non mettere troppa carne al fuoco. Innanzitutto ti darei un paio di consigli:

1) non occorre avere l'array delle consonanti e delle vocali, perché essendo complementari basta averne uno (tipo quello delle vocali) e controllare che una lettera sia presente o meno in questo array.
2) man mano che scorri le lettere ti salvi le consonanti, e 3 vocali casomai il cognome non avesse sufficienti consonanti. Ovviamente se ti sei salvato tre consonanti prima di scorrere tutto il cognome puoi fermarti comunque.
3) Una volta scorso il cognome, prendi tutte le consonanti che hai trovato, se non sono 3 accodi le vocali necessarie, e se non sei ancora a 3 caratteri schiaffa le X.

Spero di averti aiutato :) (ah i consigli a quanto pare sono 3 :fagiano:)

Ryuzaki_Eru
16-07-2010, 18:23
Le regex sono potentissime, quello che devi fare tu in poche righe riesci a scriverlo. Non posso che consigliare a tal proposito il pocket di Marco Beri che è fatto *divinamente*. Tornando a noi, ecco quello che vuoi fare tu, nel modo più semplice possibile e in Python. Ovviamente si può molto migliorare il codice. Quello che hai scritto tu non è molto buono e fai delle iterazioni annidate evitabili. Su carta il concetto è ancora più semplice, in cosa ti blocchi? Il punto è che o in un modo o in un altro per il cognome ti servono tre lettere ok? Basandoti su questo puoi impostare i vari casi. In questo codice ho usato delle funzione esterne per rendere il tutto più pulito.
Queste funzioni non fanno altro che controllare vocali e consonanti per decidere il da farsi.
Ecco:

def calcola_cognome(cognome):
cognome = cognome.upper() #Trasforiamo i caratteri del cognome in maiuscoli
if "'" in cognome:
cognome = cognome.replace("'", "")
if " " in cognome:
cognome = cognome.replace(" ", "")
consonanti = ""
if len(cognome) < 3: #Se la lunghezza del cognome e' minore di 3 aggiungiamo una X
if vocale(cognome[:1]):
cognome = cognome[1:] + cognome[:1]
consonanti = cognome + "X"
else:
for i in cognome:
if controllo_vocali(i) and len(consonanti) < 3:
consonanti += i
if num_consonanti(cognome) < 3:
for i in cognome:
if vocali_in(i):
if len(consonanti) < 3:
consonanti += i
return consonanti

Praticamente è una traduzione "para para" dalla definizione che hai postato, in modo che ti viene più facile capire il concetto ;)

Ad esempio, io non conosco ancora bene C# visto che ho iniziato a studiarlo da pochissimi giorni, ma non hai delle costanti che rappresentano l'alfabeto? Puoi usare quelle ed eviti di costruirti una lista. Oppure ne approfitti per imparare le regex(e ti garantisco che anni fa conoscerle mi avrebbe risparmiato molto lavoro in parecchi progetti) e gestire il tutto con quelle. Insomma, sbizzarrisciti :D

tomminno
17-07-2010, 08:49
sai usare le regex? :D

Come farebbe con le regex a verificare l'ultima cifra?
E come farebbe con i codici comune? Sono almeno 8000 codici più altri 200 per i paesi esteri. Che fai li elenchi uno a uno nella regex? Ci diventa pazzo.

Per non parlare dell'omocodia.

astorcas
17-07-2010, 12:34
Come farebbe con le regex a verificare l'ultima cifra?
E come farebbe con i codici comune? Sono almeno 8000 codici più altri 200 per i paesi esteri. Che fai li elenchi uno a uno nella regex? Ci diventa pazzo.

Per non parlare dell'omocodia.

si parlava del cognome... forse dovresti leggere più a fondo prima di puntare il dito

Ryuzaki_Eru
17-07-2010, 18:48
Come farebbe con le regex a verificare l'ultima cifra?
E come farebbe con i codici comune? Sono almeno 8000 codici più altri 200 per i paesi esteri. Che fai li elenchi uno a uno nella regex? Ci diventa pazzo.

Per non parlare dell'omocodia.

Nessuno ha nominato i comuni. Oltretutto i codici dei comuni vanno letti da un file o da un database e quindi li trovi già pronti all'uso. Usare una regex in quel caso non ha proprio senso.

tomminno
18-07-2010, 11:55
Nessuno ha nominato i comuni. Oltretutto i codici dei comuni vanno letti da un file e da un database e quindi li trovi già pronti all'uso. Usare una regex in quel caso non ha proprio senso.

Non vedo comunque perchè utilizzare le regex che servono a verificare la correttezza sintattica e non semantica del contenuto.
Oltretutto una verifica blanda del codice fiscale si fa in 50 righe di codice.

Ryuzaki_Eru
18-07-2010, 12:02
Non vedo comunque perchè utilizzare le regex che servono a verificare la correttezza sintattica e non semantica del contenuto.

Hai un cognome? Si. Per estrarre i 3 caratteri ci sono delle regole? Si. Questo vuol dire che il cognome, nel codice fiscale, deve seguire un determinato pattern. Non vedo perchè non si possa fare con una regex, dato che servono apposta per manipolare stringhe.

Oltretutto una verifica blanda del codice fiscale si fa in 50 righe di codice.

Non vedo questo cosa c'entri. Lui vuole *calcolare* il codice fiscale, non verificarlo. Per verificarlo bastano anche meno di 50 righe di codice.

tomminno
18-07-2010, 14:32
Hai un cognome? Si. Per estrarre i 3 caratteri ci sono delle regole? Si. Questo vuol dire che il cognome, nel codice fiscale, deve seguire un determinato pattern. Non vedo perchè non si possa fare con una regex, dato che servono apposta per manipolare stringhe.


Ancora non riesco ad immaginare una regex che data la stringa del nome o del cognome mi estragga le 3 lettere del codice fiscale. Probabilmente se c'è, è decisamente più complicata del codice necessario per estrarre le stesse informazioni.


Non vedo questo cosa c'entri. Lui vuole *calcolare* il codice fiscale, non verificarlo. Per verificarlo bastano anche meno di 50 righe di codice.

Verificarlo significa prendere i dati anagrafici e calcolare il codice fiscale per poi conforntarlo con quello fornito. Se togli il confronto, il calcolo saranno 49 righe :)

astorcas
18-07-2010, 17:00
...mmmm... non pensavo di stare così a zero :(

allora, documentandomi su come operare per il nome e il cognome, leggo che:
(lascio solo il cognome che per il nome ci sono altri casotti....)


L'algoritmo di calcolo


Cognome
Sono necessarie come detto prima 3 caratteri per rappresentare il cognome, e sono la prima la seconda e la terza consonante del cognome.
E' possibile che le consonanti siano meno di tre, in questo caso è possibile aggiungere le vocali nell'ordine in cui compaiono nel cognome.
Per cognomi più corti di 3 caratteri, è possibile sostituire il carattere mancante con la lettera X.
Chiaramente se ci sono cognomi con più parti, è necessario rimuovere gli spazi e considerare tutto come un cognome unico.

Esempi:
(Normale) Cognome : "ROSSI" - Codice Cognome : "RSS"
(Solo due consonanti) Cognome : "RIVA" - Codice Cognome : "RVI"
(Cognome minore di 3 car.) Cognome : "RE" - Codice Cognome : "REX"
(Cognome composto) Cognome : "DE CRESCENZO" - Codice Cognome : "DCR"


Ho pensato allora di implementare un array per le vocali ed uno per le consonanti. Dopodichè dovrei contare quante consonanti ci sono nella stringa e fare i vari casi.

E ho già i primi intoppi al solo scrivere su "carta" :D


foreach(char c in testoCognome)
{
foreach(char consonante in listaConsonanti)
{
if(c == s)
{
//non riesco a pensare a come fargli prendere solo le prime tre consonanti...
}
}
}


sai usare le regex? :D

Ehm.. no ... :(

Ma ho tanta buona volontà :)

un piccolo passo però l'ho fatto (almeno per andare avanti)


int count = 0;

if(c == s)
{
count = cognome.IndexOf(s);
//etc..
}


Ma ti sarei davvero grato se mi indirizzassi anche all'uso delle regex. Come ho detto, sto facendo tutto sto casino solo a scopo didattico, dato che di controlli per calcolare, validare, etc.. per il codice fiscale ne esistono a bizzeffe :)

Se leggessi prima di sparare a zero vedresti che l'uso delle regex era stato un suggerimento unicamente mirato a verificare che una lettera sia o meno una vocale. Ci sono modi migliori di farlo? Pazienza! Come vedi questo thread è stato aperto a scopo didattico, e l'invito ad usare le regex era per dare uno spunto ad imparare qualcosa di nuovo. E magari, visto che sei così bravo, oltre a fare critiche inutili a chi cerca solo di dare una mano, suggerisci una valida alternativa. Grazie

Ryuzaki_Eru
18-07-2010, 17:36
Ancora non riesco ad immaginare una regex che data la stringa del nome o del cognome mi estragga le 3 lettere del codice fiscale. Probabilmente se c'è, è decisamente più complicata del codice necessario per estrarre le stesse informazioni.

Chi ha parlato di 3 lettere?



Se leggessi prima di sparare a zero vedresti che l'uso delle regex era stato un suggerimento unicamente mirato a verificare che una lettera sia o meno una vocale.

Come dice astorcas: ci sono modi migliori? Bene. Nessuno ha detto che le regex siano il miglior modo in assoluto, infatti sono state suggerite per altri scopi e non per calcolare il codice fiscale.

Verificarlo significa prendere i dati anagrafici e calcolare il codice fiscale per poi conforntarlo con quello fornito. Se togli il confronto, il calcolo saranno 49 righe :)

Bisogna vedere che tipo di verifica vuoi fare. Comunque sono curioso di vedere queste 49 righe.

Kralizek
18-07-2010, 21:10
ma com'è che da un po' tutti i topic vanno in caccapupù? :(

Ryuzaki_Eru
19-07-2010, 00:28
Sarà il caldo :muro:

RaouL_BennetH
19-07-2010, 08:29
Ue ragassuoli :( Non vi arrabbiate :)

Allora, forse i malintesi nascono da una mia scarsa spiegazione:

Ho iniziato ad estrapolare anno mese e giorno di nascita perchè per me era il passaggio più semplice. Sono passato poi al cognome e al nome che, insieme al codice di controllo finale credo siano le operazioni più complesse.

Questo fine settimana mi sono dedicato alla lettura delle regex, ma, per quello che ho compreso, non sono molto adatte allo scopo (sottolineo.. per quello che ho compreso).

astorcas
19-07-2010, 08:48
Ue ragassuoli :( Non vi arrabbiate :)

Allora, forse i malintesi nascono da una mia scarsa spiegazione:

Ho iniziato ad estrapolare anno mese e giorno di nascita perchè per me era il passaggio più semplice. Sono passato poi al cognome e al nome che, insieme al codice di controllo finale credo siano le operazioni più complesse.

Questo fine settimana mi sono dedicato alla lettura delle regex, ma, per quello che ho compreso, non sono molto adatte allo scopo (sottolineo.. per quello che ho compreso).

No ma figurati, chi si arrabbia! Il mio tentativo di mettere in ballo le regex era semplicemente per farti fare una cosa banale tipo:


if(Regex.IsMatch(cognome[i],"[aeiou]")){
//è una vocale
}


Ma qua bisogna stare attenti! :fagiano:

Kralizek
19-07-2010, 08:51
ma le regex non sono lentucce?

in genere le evito per controlli cosí semplici...

astorcas
19-07-2010, 08:55
ma le regex non sono lentucce?

in genere le evito per controlli cosí semplici...

si si sono sicuramente più lente ma non avevo capito che si cercava l'algoritmo perfetto... :asd:

RaouL_BennetH
19-07-2010, 09:06
Allora, sono arrivato sino a qui :

Cognome (solo consonanti) :D
Anno mese e giorno di nascita (sia per maschio che per femmina)

in questo modo:



private char[] months = { 'A', 'B', 'C', 'D', 'E', 'H', 'L', 'M', 'P', 'R', 'S', 'T' };



private string estraiGiorno(DateTime dt, bool isLady)
{
string giorno = "";

if (isLady)
{
int fDay = dt.Day + 40;
giorno = fDay.ToString();
}
else
{
giorno = string.Format("{0:dd}", dt);
}
return giorno;

}

private string GetAnnoMeseGiorno(DateTime dt)
{
return string.Format("{0:yy}", dt) + months[dt.Month - 1] + estraiGiorno(dt, checkBox1.Checked);
}

private void ProvaCognomeSoloConsonanti(string cognome)
{
int count = 0;
string temp = "";
foreach (char c in cognome)
{
foreach (char s in consonanti)
{
if (c == s)
{
count = cognome.IndexOf(s);
temp += cognome.Substring(count, 1);
}

}

}
if (temp.Length > 3)
{
for (int i = 3; i < temp.Length; i++)
{
temp = temp.Remove(i);
}
}

txtCognome.Text = temp;
}

RaouL_BennetH
19-07-2010, 14:07
Nella mia maniera rudimentale sono riuscito ad arrivare a questo punto:

-> Cognome (sia se di due lettere, sia se con due consonanti, regolare)
-> Anno, mese e giorno

Con il cognome, sto facendo tutte le prove del caso e mi trovo con tutti gli altri "calcolatori" che sto trovando in rete:

private void EstraiCognome(string cognome)
{
int count = 0;
string temp= "";
string voc = "";
cognome.Trim();
foreach (char c in cognome)
{
foreach (char s in consonanti)
{
if (c == s)
{
count = cognome.IndexOf(s);
temp += cognome.Substring(count, 1);
}

}
foreach (char a in vocali)
{
if (c == a)
{
count = cognome.IndexOf(a);
}
}
voc = cognome.Substring(count, 1);

}
if (temp.Length >= 3)
{
for (int i = 3; i < temp.Length; i++)
{
temp = temp.Remove(i);
}
txtCognome.Text = temp;
}
else if (cognome.Length == 2)
{
txtCognome.Text = temp + voc + "X";
}
else
{
txtCognome.Text = temp + voc;
}
}

Ryuzaki_Eru
19-07-2010, 14:19
Appena ho tempo guardo :)

RaouL_BennetH
19-07-2010, 14:21
Appena ho tempo guardo :)

Ti segnalo già un errore :(

Se ho cognomi composti da 3 caratteri, per esempio: DEA, ARA, ADE, etc.. non funziona :(

Ryuzaki_Eru
19-07-2010, 14:22
Ti segnalo già un errore :(

Se ho cognomi composti da 3 caratteri, per esempio: DEA, ARA, ADE, etc.. non funziona :(

Usa come "base" quello che ti ho postato in Python e vedi se ti è sfuggito qualcosa ;)

RaouL_BennetH
19-07-2010, 16:09
Usa come "base" quello che ti ho postato in Python e vedi se ti è sfuggito qualcosa ;)

Ok, prendendolo solo come spunto, sono riuscito a codhorrorare così e sembra funzionare:


private string ProvaCognome(string cognome)
{
int count = 0;
string retConsonanti = "";
string vocali = "";
string cognomeFiscale = "";
cognome.Trim();
foreach (char c in cognome)
{
foreach (char cc in consonanti)
{
if (c == cc)
{
count = cognome.IndexOf(c);
if (cognome.Length < 3 && count == 0)
{
cognomeFiscale = cognome + "X";

}
else if (cognome.Length == 3 && count == 0)
{
cognomeFiscale = cognome;

}
else if (cognome.Length == 3 && count > 0)
{
retConsonanti = cognome.Substring(cognome.IndexOf(c));
cognomeFiscale = retConsonanti + cognome.Remove(cognome.IndexOf(c));

}
else
{
retConsonanti += cognome.Substring(count, 1);
vocali = cognome.Substring(count + 1);
cognomeFiscale = retConsonanti + vocali;
for (int i = 3; i < cognomeFiscale.Length; i++)
{
cognomeFiscale = cognomeFiscale.Remove(i);
}

}
}
}

}
return cognomeFiscale;

}

RaouL_BennetH
19-07-2010, 16:53
Mmm.... ma mi domando e dico:

Sono in un ambiente e in un mondo ad oggetti, e ciò che ho prodotto poco fa è un'accozzaglia simil-procedurale....

idea malsana:

Prima considerazione:

le stringhe sono immutabili... quindi me ne serve sempre una copia dell'originale dove salvarne le modifiche...

a questo punto, se il "cognome" del caso lo immagino come un tipo generico:


List<char> elementiCognome;


Non mi risulta molto più semplice accedere ai vari elementi e scambiarli di posto senza dover fare copie su copie ?

tomminno
19-07-2010, 17:05
Io intanto creerei un metodo IsVowel, così semplifichi e togli un foreach.
I primi 2 if possono tranquillamente stare fuori dai foreach (il primo if è sbagliato nell'improbabile caso di un cognome di 1 carattere), così elimini anche count.
Poi le variabili retConsonanti e vocali secondo me le potresti evitare.
Poi potresti creare un metodo che ti ritorna la prima consonante della stringa passata e fare il metodo ricorsivo, così lo riutilizzi bene anche per il nome.
Poi volendo si potrebbe creare un metodo che accetta in ingresso oltre alla stringa anche un predicato così che risolvi sia per le consonanti che per le vocali.

RaouL_BennetH
19-07-2010, 17:14
Io intanto creerei un metodo IsVowel, così semplifichi e togli un foreach.
I primi 2 if possono tranquillamente stare fuori dai foreach (il primo if è sbagliato nell'improbabile caso di un cognome di 1 carattere), così elimini anche count.
Poi le variabili retConsonanti e vocali secondo me le potresti evitare.
Poi potresti creare un metodo che ti ritorna la prima consonante della stringa passata e fare il metodo ricorsivo, così lo riutilizzi bene anche per il nome.
Poi volendo si potrebbe creare un metodo che accetta in ingresso oltre alla stringa anche un predicato così che risolvi sia per le consonanti che per le vocali.

:D In pratica non ne ho azzeccato uno :sofico:

Scherzi a parte, ora che ho iniziato a ragionare anche per il nome, dove le regole cambiano solo in minima parte, vedendo le ripetizioni alle quali andavo incontro avevo pensato di estrarne dei metodi. Per ora però sono solo su carta :(

Comunque, la cosa alla quale aspiro è proprio questa:
molti di voi riescono già a fare un'ottima analisi ancor prima di scrivere del codice. Io invece perdo un mucchio di tempo prima a scrivere del codice e poi a farne l'analisi ..

Ryuzaki_Eru
19-07-2010, 18:04
Puoi usare anche una lista, ma non lo so quanto ti possa essere utile. Aggiornare una variabile stringa non costa praticamente niente e inoltre quando poi devi "unire" i pezzi ti trovi già le varie parti pronte da concatenare.
Per gli altri metodi, come ti avevo detto, puoi crearne un pò di appoggio, ad esempio per vedere se una lettera è una consonante o una vocale. Puoi sbizzarrirti come meglio credi insomma, il bello è proprio questo ;)
Se ti può essere utile ti linko i sorgenti di quello che ho scritto io, ma il codice fa letteralmente schifo, non so quanto possa esserti utile :D

Kralizek
19-07-2010, 21:20
ma un array da 3 chars?

Ryuzaki_Eru
20-07-2010, 01:17
Per fare cosa?

RaouL_BennetH
20-07-2010, 11:37
Io intanto creerei un metodo IsVowel, così semplifichi e togli un foreach.

Buondì :)

Rileggendo con molta attenzione il tuo post, sono certo che mi sfugge quanto mi suggerisci, ovvero:

i casi particolari per un cognome, possono essere questi:

"DE" "AL" "DEA" "ADE"

per i primi due basta aggiungere la "X" finale, ma devo prima vedere se la prima è una consonante o una vocale, o meglio, dato che le vocali sono la parte meno rilevante, pensavo di fare un controllo solo se è una consonante. In caso la prima lettera sia una vocale, devo scambiare di posto le due lettere e aggiungere la "X" (AL => LAX) , senza un contatore che mi indichi la posizione, come faccio ?

Per il resto aspetto prima di risolvere bene questo e poi vado avanti :)

Ryuzaki_Eru
20-07-2010, 12:09
Basta che controlli se la prima lettera è una vocale o meno. Se non lo è "scambi" le posizioni. C# non ha delle funzionalità per manipolare le stringhe? In Python ad esempio:

>>>a = "AL"
>>>a = a[1:] + a[:1]
>>>a
'LA'

banryu79
20-07-2010, 13:48
Basta che controlli se la prima lettera è una vocale o meno. Se non lo è "scambi" le posizioni. C# non ha delle funzionalità per manipolare le stringhe? In Python ad esempio:

>>>a = "AL"
>>>a = a[1:] + a[:1]
>>>a
'LA'
Dubito che in C# ci siano le 'slice', o qualche feature che a livello di linguaggio supporti roba simile :)
Comunque gli basta mettere le mani sull'array di char sottostante la stringa, e quello è sicuramente possibile farselo restituire (quello che hai fatto qua sopra con le slice, in questo caso, volendo, lo può riprodurre concatenando due substring).

RaouL_BennetH
20-07-2010, 14:40
Ciao :)

Non è un grosso problema scambiare di posto i caratteri, il fatto è che per confrontarli devo per forza ciclare sulla stringa, non vedo come potrei fare un confronto senza ciclare, cioè:


string cognome = "DE";
//ora se voglio confrontare il primo carattere di cognome:
cognome[0] = vocale o consonante
//ma per vedere se è una vocale o una consonante devo per forza ciclare su qualcosa giusto ?

Ryuzaki_Eru
20-07-2010, 14:59
Creati un metodo che controlla se una lettera è una vocale o una consonante no? Poi basta che fai una cosa tipo:

if vocale(cognome[0]):
cognome = cognome[1] + cognome[0]
cognome += "X"

astorcas
20-07-2010, 15:11
Ciao :)

Non è un grosso problema scambiare di posto i caratteri, il fatto è che per confrontarli devo per forza ciclare sulla stringa, non vedo come potrei fare un confronto senza ciclare, cioè:


string cognome = "DE";
//ora se voglio confrontare il primo carattere di cognome:
cognome[0] = vocale o consonante
//ma per vedere se è una vocale o una consonante devo per forza ciclare su qualcosa giusto ?



private static bool IsVowel(char c)
{
char cToUpper=Char.ToUpper(c);
return cToUpper == 'A' || cToUpper == 'E' || cToUpper == 'I' || cToUpper == 'O' || cToUpper == 'U';
}


Credo ti stiano suggerendo una cosa del genere

Ryuzaki_Eru
20-07-2010, 15:20
Si esattamente :) In Python la farei cosi:

def is_vowel(c):
return c.upper() in ["A", "E", "I", "O", "U"]

DanieleC88
21-07-2010, 01:46
def isVowel(c):
return (c.lower() in 'aeiou')
:O

astorcas
21-07-2010, 08:29
def isVowel(c):
return (c.lower() in 'aeiou')
:O

Buhahahah non ti piacciono le lettere uppercase?

vabbé anche col c#


private static bool IsVowel(char c)
{
return "aeiou".Contains(Char.ToLower(c));
}


però è orrendo :D

Kralizek
21-07-2010, 08:48
un poco poco piú carino :P


private static char[] _Vowels = new char[] { 'a', 'e', 'i', 'o', 'u' };

private static bool IsVowel(char c)
{
return _Vowels.Contains(Char.ToLower(c));
}

Ryuzaki_Eru
21-07-2010, 09:22
def isVowel(c):
return (c.lower() in 'aeiou')
:O

La lista fa più figo :O :asd:

lambda x: x.upper() in "AEIOU"

astorcas
21-07-2010, 09:39
Ok tutti sappiamo verificare se un carattere è una vocale :asd:

RaouL_BennetH come va lo sviluppo?

banryu79
21-07-2010, 11:24
Ok tutti sappiamo verificare se un carattere è una vocale :asd:

Siete proprio sicuri? Nessuno dei metodi proposti è corretto :)

astorcas
21-07-2010, 11:29
Siete proprio sicuri? Nessuno dei metodi proposti è corretto :)

se ti riferisci alla Y, viene considerata consonante per il codice fiscale... se non erro :fagiano:

banryu79
21-07-2010, 11:32
se ti riferisci alla Y, viene considerata consonante per il codice fiscale... se non erro :fagiano:
Proprio a quello... :ops2:

RaouL_BennetH
21-07-2010, 11:41
Ok tutti sappiamo verificare se un carattere è una vocale :asd:

RaouL_BennetH come va lo sviluppo?

In maniera più spedita senza dubbio grazie a tutti i vostri contributi :ave:

L'unica cosa che non mi sta piacendo è la piega "procedurale" che ho intrapreso(molti controlli con gli if etc...), mi piacerebbe averne una visione più moderna, ma ci sto lavorando. Entro sera dovrei aver finito il tutto e ve lo sottopongo :)

RaouL.

DanieleC88
21-07-2010, 11:42
Se non ricordo male dai miei studi, "j" ed "y" sono semiconsonanti. :O
Almeno, per la "j" sono sicuro, per la "y" non del tutto.

RaouL_BennetH
21-07-2010, 11:49
un poco poco piú carino :P


private static char[] _Vowels = new char[] { 'a', 'e', 'i', 'o', 'u' };

private static bool IsVowel(char c)
{
return _Vowels.Contains(Char.ToLower(c));
}



Ah, ieri ho dimenticato di chiederti: ma sono io che non lo vedo oppure il tipo char non ha un metodo Contains ?

astorcas
21-07-2010, 11:55
Ah, ieri ho dimenticato di chiederti: ma sono io che non lo vedo oppure il tipo char non ha un metodo Contains ?

Eh no infatti, ha usato un extension method presente nella classe Enumerable dy System.Linq (quindi niente Contains per .NET 2.0)

Comunque è l'array in genere, non char, a non averlo

Kralizek
21-07-2010, 12:13
ops avevo dimenticato che qui era .net 2.0...

non ci sono piú abituato :S

cmq se aggiungi il riferimento a System.Core.dll puoi usare gli extension methods in maniera "classica"

bool isVowel = Enumerable.Contains(_Vowels, 'a');

certo che lavorare in .net < 3.5 per scelta é un po' un suicidio (sempre meglio che 1.1)

RaouL_BennetH
21-07-2010, 12:39
ops avevo dimenticato che qui era .net 2.0...

non ci sono piú abituato :S

cmq se aggiungi il riferimento a System.Core.dll puoi usare gli extension methods in maniera "classica"

bool isVowel = Enumerable.Contains(_Vowels, 'a');

certo che lavorare in .net < 3.5 per scelta é un po' un suicidio (sempre meglio che 1.1)

:D No no.. non è per scelta. Nell'azienda dove lavoro e dove mi stanno formando, l'ambiente di sviluppo con tutti i software sviluppati è basato sul 2.0.
I piani alti prevedono la migrazione verso metà 2011. I servizi e i software sono molto complessi e voi sapete meglio di me che è praticamente impossibile cambiare in maniera veloce :)

Potrei farlo a casa, con qualche versione express, ma mi son detto: la formazione necessaria al mio lavoro per il momento è basata su questo (e già sono molto confuso su diversi aspetti). Se mi metto anche a cercare di capire "il nuovo", va a finire che non ci capisco più niente (ma questo è un atteggiamento personale ovviamente ;) )

Kralizek
21-07-2010, 12:44
mah... il passaggio da 2.0 a 3.5 é immediato perché il 3.5 é solo un addon del 2.0 ed una nuova versione del compilatore.

in compenso noi stiamo lavorando sul passaggio a .net 4 anche se alcuni servizi li stiamo sviluppando giá in .net 4 (wcf4 é qualcosa di fenomenale)

RaouL_BennetH
21-07-2010, 12:47
private static bool IsVowel(char c)
{
char cToUpper=Char.ToUpper(c);
return cToUpper == 'A' || cToUpper == 'E' || cToUpper == 'I' || cToUpper == 'O' || cToUpper == 'U';
}


Credo ti stiano suggerendo una cosa del genere

Anche qui scusa, ma non sarebbe la stessa cosa di:


private static bool IsVowel(char c)
{
foreach(char cc in arrayDiVocali)
return (c == cc);
return false;
}

A prescindere se sia bello o meno non è la stessa cosa ?

RaouL_BennetH
21-07-2010, 12:48
mah... il passaggio da 2.0 a 3.5 é immediato perché il 3.5 é solo un addon del 2.0 ed una nuova versione del compilatore.

Su questo purtroppo non posso risponderti personalmente perchè non ho idea del tipo di analisi che fanno ai piani alti :boh:

RaouL_BennetH
21-07-2010, 13:04
cmq, una prima sintesi è questa:


//esempio solo per un cognome di due caratteri come "AL" o "DE"
private string Prova(string cognome)
{
if (cognome.Length == 2 && IsVowel(cognome[0]))

return string.Format("{0}{1}{2}", cognome[1], cognome[0], "X");

return cognome + "X";
}

Kralizek
21-07-2010, 14:04
ma senza fari tanti casi particolari...

non ti basta ciclare lettera per lettera il cognome.
se é una consonante la aggiungi. se é una vocale la salti.

se arrivi a tre ti fermi e restituisci.

se arrivi a fine cognome, ricominci daccapo e selezioni le vocali.

se arrivi ancora a fine cognome e non trovato le 3 letterine magiche, paddi.

astorcas
21-07-2010, 14:05
Anche qui scusa, ma non sarebbe la stessa cosa di:


private static bool IsVowel(char c)
{
foreach(char cc in arrayDiVocali)
return (c == cc);
return false;
}

A prescindere se sia bello o meno non è la stessa cosa ?


private static bool IsVowel(char c)
{
foreach(char cc in arrayDiVocali)
if(c == cc) return true;
return false;
}


FIXED

Kralizek
21-07-2010, 14:15
qualcosa tipo questo... senza "casi particolari". si puó scrivere molto meglio la parte del padding, ma devo chiudere un progetto prima delle 17 o niente bbq :P


private char[] _Vocali = new char[]{'a', 'e', 'i', 'o', 'u'};

private string GetCognome(string cognome)
{
char[] codice = new char[3];
int pos = 0;

for (int i = 0; i < cognome.Length; i++)
{
if (!IsVocale(cognome[i]))
{
codice[pos] = char.ToLower(cognome[i]);
pos++;

if (pos == 3)
{
return new string(codice);
}
}
}

for (int i = 0; i < cognome.Length; i++)
{
if (IsVocale(cognome[i]))
{
codice[pos] = char.ToLower(cognome[i]);
pos++;

if (pos == 3)
{
return new string(codice);
}
}
}

for (int i = 0; i < 3; i++)
{
if (!char.IsLetter(codice[i]))
codice[i] = 'x';
}

return new string(codice);
}

private bool IsVocale(char lettera)
{
return _Vocali.Contains(char.ToLower(lettera));
}


a voler fare le pulci, si possono unire i due cicli for iniziali a fare tutto ad una prima passata ed usare il for per dividere le lettere tra consonanti e vocali. ma per cognomi (molto ??????) lunghi si avrebbe uno spreco di memoria inutile. ed anche il codice postato paga "dazio" solo su cognomi veramente lunghi (a causa delle 3 passate).

ma seriamente... quanti cognomi conoscete con piú di 10 lettere? ed in 10 lettere non ci sono 3 consonanti per uscire velocemente dalla procedura? forse in finlandia! :P

Dubito che in C# ci siano le 'slice', o qualche feature che a livello di linguaggio supporti roba simile :)
Comunque gli basta mettere le mani sull'array di char sottostante la stringa, e quello è sicuramente possibile farselo restituire (quello che hai fatto qua sopra con le slice, in questo caso, volendo, lo può riprodurre concatenando due substring).

in realtá ci sono gli ArraySegment<T> (http://msdn.microsoft.com/en-us/library/1hsbd92d.aspx) peró non l'ho mai utilizzato e non so come funziona ma sembra molto verboso da usare.

Ryuzaki_Eru
21-07-2010, 15:55
Per me tutto questo codice è inutile, non c'è bisogno di tutti quei for e quegli if.

DanieleC88
21-07-2010, 16:46
Per me tutto questo codice è inutile, non c'è bisogno di tutti quei for e quegli if.

def isVowel(x):
return x[0].lower() in 'aeiou'

def isConsonant(x):
return x[0].lower() in 'bcdfghjklmnpqrstvwxyz'

[...]

def getSurnameCode(surname):
c = filter(isConsonant, surname)
v = filter(isVowel, surname)[:3 - len(c)]
return c.upper() + v.upper() + ('X' * (3 - len(c) - len(v)))

:O

Magari anche con qualche lambda function, evitando qualche def di troppo. :p

RaouL_BennetH
21-07-2010, 17:02
Ragazzi, plz, già ho difficoltà col vetusto ed unico C# (2.0), se vi mettete anche con altri linguaggi ed espressioni che comunque non posso "tradurre" in ciò che uso ... vado ancora più in confusione :(

cmq... sto facendo qualche passo avanti in maniera non proprio "procedurale"


List<char> cList = new List<char>(); //o anche string....

//mi costruisco un predicato

private int VowelPos(char c)
{
return Vowels().FindLastIndex(IsVowel);
}

//Vowels() è una List<T> anzichè un array di char

private void Provola(string cognome)
{
for(int i = 0; i < cognome.Length; i++)
{
if(!IsVowel(cognome[i]))
{
cList.Add(cognome[i]);
}
if(cList.Count == 2) //se ci sono solo due consonanti prendo la prima vocale dopo l'ultima consonante
{
int vocPos = VowelPosition(cognome[i]);
cList.Add(cognome[vocPos]);
}
if(cList.Count > 3) //se ci sono più di tre consonanti
{
cList.RemoveAt(3);
}
}
}

ratman511
21-07-2010, 17:22
def isVowel(x):
return x[0].lower() in 'aeiou'

def isConsonant(x):
return x[0].lower() in 'bcdfghjklmnpqrstvwxyz'

[...]

def getSurnameCode(surname):
c = filter(isConsonant, surname)
v = filter(isVowel, surname)[:3 - len(c)]
return c.upper() + v.upper() + ('X' * (3 - len(c) - len(v)))

:O

Magari anche con qualche lambda function, evitando qualche def di troppo. :p

perchè usi filter e 3-len(c) e poi 'X' * (3 - len(c) - len(v)))? non capisco a che serve

DanieleC88
21-07-2010, 17:31
Te lo spiego in privato che qua siamo OT. E mi sono appena accorto che è pure codice buggato!

PGI-Bis
21-07-2010, 20:32
Ragazzi, plz, già ho difficoltà col vetusto ed unico C# (2.0), se vi mettete anche con altri linguaggi ed espressioni che comunque non posso "tradurre" in ciò che uso ... vado ancora più in confusione :(

E allora, diradiamo le nebbie. Qui il problema non è la conoscenza del linguaggio o dell'algoritmo. Se io ti chiedessi di farmi i conti a mano ci riusciresti senza problemi.

Il problema è il metodo: te ne serve almeno uno.

Ora, tra le teorie del problem solving ce n'è una che quaglia particolarmente bene con i linguaggi che offrono una via preferenziale alla creazione di definizioni proprie. Ti risparmio la filippica, basti dire che la teoria è quella che sta alla base del dividi et impera.

Ad esempio, prendiamo questo codice del cognome.

Prima di tutto, abbiamo un cognome:

Cognome cognome = new Cognome("intrallazzulli");

public class Cognome {

public Cognome(String stringa) {
}
}

Poi abbiamo un CodiceCognome, che qualcosa la cui definizione richiede un Cognome:

public class CodiceCognome {

public CodiceCognome(Cognome cognome) {

}
}

Insomma, banalità: e questa era la parte complicata del procedimento.

Cos'è il codice cognome? Dice l'algoritmo che è la concatenziona delle prime tre consonanti del cognome, seguite dalle prime tre vocali, se le consonanti sono meno di tre, seguite da un numero di X pari a quanto è necessario per arrivare ad avere 3 caratteri.

La domanda è: visto che lo so, posso scriverlo? E come no, guarda qui:

public class CodiceCognome {
public readonly String codice;

public CodiceCognome(Cognome cognome) {
String consonanti = cognome.consonanti.primeTre();
String vocali = cognome.vocali.prime(3 - consonanti.length);
String xxx = "XXX".Substring(0, 3 - (consonanti.Length + vocali.Length));
codice = consonanti + vocali + xxx;
}
}

Ho le consonanti, ho le vocali, ho le x, le incollo e via. Funziona? Ci mancherebbe altro: più esplicito di così.

Naturalmente mancano dei pezzi, mancano delle definizioni che onorano parte del problema nella sua forma risolta.

Vediamo.

Ho detto che Cognome ha delle consonanti e delle vocali. E lo scrivo.

public class Cognome {
public readonly ListaConsonanti consonanti;
public readonly ListaVocali vocali;

public Cognome(String stringa) {
consonanti = new ListaConsonanti(stringa);
vocali = new ListaVocali(stringa);
}
}

Eccolo, bell'è pronto. Cos'è ListaConsonanti? Be' è quello che gli pare, basta che sia in grado di dirmi "primeTre()", dove primeTre mi restituisce una stringa contenente le prime tre consonanti della stringa che gli fornisco, o meno se ce ne sono di meno.

Prima di tutto, ListaConsonanti è una lista delle consonanti contenute in una stringa:

public class ListaConsonanti {
private List<String> lista = new List<String>();

public ListaConsonanti(String stringa) {
for(int i = 0; i < stringa.Length; i++) {
String token = stringa.Substring(i, 1);
if(!"aeiou".Contains(token)) lista.Add(token);
}
}
}

E poi deve dirmi "primeTre()".

public class ListaConsonanti {
private List<String> lista = new List<String>();

public ListaConsonanti(String stringa) {
for(int i = 0; i < stringa.Length; i++) {
String token = stringa.Substring(i, 1);
if(!"aeiou".Contains(token)) lista.Add(token);
}
}

public String primeTre() {
String buffer = "";
for(int i = 0; i < 3 && i < lista.Count; i++) buffer += lista[i];
return buffer;
}
}

La lista di vocali. Piglia una stringa e crea la lista delle sue vocali.

public class ListaVocali {
private List<String> lista = new List<String>();

public ListaVocali(String stringa) {
for(int i = 0; i < stringa.Length; i++) {
String token = stringa.Substring(i, 1);
if("aeiou".Contains(token)) lista.Add(token);
}
}
}

E mi dice "prime(n)":

public class ListaVocali {
private List<String> lista = new List<String>();

public ListaVocali(String stringa) {
for(int i = 0; i < stringa.Length; i++) {
String token = stringa.Substring(i, 1);
if("aeiou".Contains(token)) lista.Add(token);
}
}

public String prime(int limite) {
String buffer = "";
for(int i = 0; i < limite && i < lista.Count; i++) buffer += lista[i];
return buffer;
}
}

Fine. Funziona? A naso, è molto probabile perchè il sistema è fatto di pezzi abbastanza piccoli da essere verificabili su due piedi. Ad essere rigorosi, la possibilità che il procedimento produca una soluzione esatta dipende da certi fattori soporiferi.

Per sommi capi, il punto è questo: prendi il problema e lo rappresenti come un albero di definizioni, dove ogni elemento è composto di altre definizioni, finchè non arrivi a poter scrivere una definizione usando un numero ragionevolmente piccolo di elementi presi dalle librerie del linguaggio. Lì hai una foglia. Quando il tuo albero di definizioni finisce tutto in foglie, il programma è pronto.

RaouL_BennetH
21-07-2010, 20:57
Non c'è niente da fare :cry: è proprio l'approccio giusto che mi manca :muro:

Ok... si ricomincia ...

astorcas
21-07-2010, 21:09
Non c'è niente da fare :cry: è proprio l'approccio giusto che mi manca :muro:

Ok... si ricomincia ...

su forza e coraggio :D, come vedi siamo sempre pronti a dare una mano ;)

ratman511
22-07-2010, 01:31
ti capisco è cosi anche per me.come si fa ad imparare l'approccio corretto?ad esempio quello di PGI-Bis è fantastico, ma come si impara(sempre che si possa imparare una cosa simile)?

banryu79
22-07-2010, 08:24
ti capisco è cosi anche per me.come si fa ad imparare l'approccio corretto?ad esempio quello di PGI-Bis è fantastico, ma come si impara(sempre che si possa imparare una cosa simile)?
Prova a prendere spunto da questo post di PGI: rileggi con calma non tanto il codice, quanto la descrizione del metodo.

Poi prendi un problema da risolvere ragionevolmente alla tua portata.
So che studi Python; anche con quel linguaggio puoi applicare il metodo descritto in quanto puoi creare definizioni.

Tenta di risolvere il problema con lo stesso approccio, sei libero di usare tutte le definizioni che ti sembrano opportune, tralasciando il fatto che ancora non esistono.
Una volta che hai finito di descrivere la soluzione del problema in termini di definizioni, metti mano al codice per realizzare quelle definizioni con il linguaggio che conosci.

Fatto questo, osserva di nuovo tutto il lavoro e vedi se la cosa fila.

ratman511
22-07-2010, 10:46
il problema è che non so quale sia la mia portata. ho provato non sai quante volte a realizzare quello che ho in mente e nemmeno riesco a partire.poi per fare una cosa come questa che si può fare in un unico metodo perchè si devono scrivere cosi tanti metodi?

DanieleC88
22-07-2010, 10:51
poi per fare una cosa come questa che si può fare in un unico metodo perchè si devono scrivere cosi tanti metodi?

:eekk:
Perché tanti metodi grossi sono molto meno "gestibili", non sai mai se sono del tutto corretti, e modificarli spesso è più complicato. Tanti metodi piccoli di cui sei in grado di verificare la correttezza "ad occhio" e sui quali non hai dubbi ti tolgono di mezzo questo problema, e modificarli è immediato.

Comunque stiamo scivolando proprio OT, direi che è quasi il caso di aprire un nuovo thread per l'occasione. :D

astorcas
22-07-2010, 11:36
...

Comunque stiamo scivolando proprio OT, direi che è quasi il caso di aprire un nuovo thread per l'occasione. :D

E come lo chiameresti sto thread? :fagiano:

banryu79
22-07-2010, 11:41
il problema è che non so quale sia la mia portata. ho provato non sai quante volte a realizzare quello che ho in mente e nemmeno riesco a partire.poi per fare una cosa come questa che si può fare in un unico metodo perchè si devono scrivere cosi tanti metodi?
Presumo ti manchi il metodo.

[1] Risolvere un problema significa trovarne la soluzione.
[2] Poi, dato che abbiamo l'ambizione di scrivere programmi, la soluzione va appunto scritta in un linguaggio di programmazione.

A te può sembrare di non riuscire a fare 2, ma magari è perchè non ti cade l'occhio su 1.

Trovare la soluzione di un problema "complesso" è sicuramente meno economico (più difficile) che trovare la soluzione di un problema "semplice".
Dietro c'è sempre una mente umana che lavora, la quale può gestire al massimo un tot di cose in contemporanea.

Se dunque trovi il modo di spezzettare il problema "complesso" in tanti problemi "semplici" rendi più economica l'operazione di risoluzione del problema.

L'esempio che ha postato PGI rende questo anche in termini di organizzazione del codice: hai più metodi, ovvero più unità distinte ovvero più definizioni, ognuna che risolve un problema ben preciso.
Poi ovviamente puoi anche scrivere tutto in un unico metodo, nessuno te lo vieta.
Ma il punto è che PGI, scrivendo tanti piccoli metodi, ha illustrato il metodo con il quale ha risolto mentalmente il problema della codifica del cognome nel codice fiscale:

Parto con il fatto che c'è una roba che si chiama "cognome"... e via, creo la definizione;
Poi vedo che il problema mi chiede di creare un codice per quel "cognome"... dunque ho una roba che chiamo "codice cognome"... e via, creo la definizione;
Ma, aspetta un po', cos'è un "codice cognome"? E' un codice prodotto usando un "cognome" e applicandoci delle regole ben precise... e via, amplio la definizione di "codice cognome";
Nell'amplire la definizione di "codice cognome" ho scritto della "roba nuova"? Della roba ovvero dei nomi per i quali ancora non esistono delle definizioni?... via, creo le definizioni!


E così fino ad aver definito tutta la "roba" usata per scrivere la soluzione del problema. E incidentalmente, ogni nuova "roba" scritta puoi vederla come un problema più piccolo di quello di partenza, e la sua definizione puoi vederla come la sua soluzione.

Che cosa chiede questo metodo alla mente che lo vuole applicare?
[1] Risolvere un problema significa trovarne la soluzione.
Devi analizzare il problema. Non si scappa. Solo durante la sua analisi sarai in grado di fare quello che ha fatto PGI, cioè identificare arbitrariamente delle sotto-parti ovvero dei sotto-problemi più piccoli.

Un sotto-problema che hai individuato ti pare ancora troppo complesso da risolvere?
Bene, non fai altro che analizzarlo per scomporlo a sua volta in due o più sotto-problemi.

DanieleC88
22-07-2010, 11:59
E come lo chiameresti sto thread? :fagiano:

"Strategia top-down: una guida per i novizi". :O

ratman511
22-07-2010, 16:44
banryu la tua spiegazione è chiarissima e sono anche colpito dall'esposizione di pgi-bis, io vorrei solo capire come imparare a ragionare cosi. a pensare che tutti siete partiti da 0 come me non riesco a crederci.
cosa è la strategia top-down e che differenza c'è con la bottom-up?

RaouL_BennetH
22-07-2010, 17:01
Allora... questa è la situazione ad oggi (inutile dire che senza i vostri contributi forse forse avrei introdotto il GOTO anche in .net :D )


//il booleano mi serve a seconda che sto trattando il nome o il cognome
//in caso di nome(true) e che le consonanti siano più di quattro,
//l'algoritmo impone di prendere la prima, la terza e la quarta.

private string GetConsonanti(string s, bool b)
{
List<char> cList = new List<char>();
string strCon = "";
s = Regex.Replace(s, @"\W*", ""); //elimino tutti gli spazi e gli eventuali apici

for (int i = 0; i < s.Length; i++)
{
if (!IsVowel(s[i]))
{
cList.Add(s[i]);
}

}
if (cList.Count > 3 && b)
{
cList.RemoveAt(1);
}
for (int i = 0; i < 3 && i < cList.Count; i++)
{
strCon += cList[i];
}


return strCon;
}

private string GetVocali(string s, int max)
{

List<char> vList = new List<char>();
string strCon = "";
for (int i = 0; i < s.Length; i++)
{
if (IsVowel(s[i]))
{
vList.Add(s[i]);
}
}
for (int i = 0; i < max && i < vList.Count; i++)
{
strCon += vList[i];
}
return strCon;
}

//...blabla
//per il giorno mese e anno è tutto semplice, es.:
private string GetDay(bool isLady)
{
if(isLady)
return string.Format("{0}", (dataNascita.Giorno + 40));
return dataNascita.Giorno.ToString();
}


Finora ottengo quindi:

Cognome, Nome, Anno, Lettera_Mese, Giorno

Per il mese ho usato un dizionario<int, char>, es.:


dizionarioMesi.Add(1, 'A');
........
dizionarioMesi.Add(12, 'T');


e le verifiche fatte confrontandolo con altri programmi che calcolano il codice fiscale sono sempre corrette. Ripeto, tutto questo grazie a voi !!

Ora mi restano gli ultimi due problemi:

Il codice del comune e il codice di controllo.

Per il codice dei comuni, non vorrei assolutamente utilizzare un db con relativa tabella ma provare ad utilizzare un file.

Come ottengo il codice del comune ?
Sapendo comune e provincia.

Mi sono già premunito di un file che contiene la maggior parte dei codici dei comuni.

Considerando che il contenuto del file è fatto così:


A001,PD,ABANO TERME


Ora quindi so cosa DEVO fare ma non so COME farlo :D

Se avessi a che fare con un db, mi basterebbe :


SELECT codice FROM comuni WHERE provincia = "XX" AND comune = "BLABLBA";


Per fare una ricerca ed estrarre una stringa da un file di testo, quali sono le tecniche più efficaci ?

Grazie mille :)

RaouL.

RaouL_BennetH
22-07-2010, 17:23
un primo approccio:


public void ProvaCodiceComune(string fileName)
{
StreamReader sr = new StreamReader(fileName);
string content = sr.ReadToEnd();
if(Regex.IsMatch(content, "PD" + "," + "ABANO BAGNI" ))
{
System.Windows.Forms.MessageBox.Show("TROVATO");
}
sr.Close();
}

Ryuzaki_Eru
22-07-2010, 19:31
Puoi leggere il file riga per riga fin quando non trovi la riga che contiene il comune ed estrarre in questo modo il codice(visto che sai come è formata ogni linea del file). Oppure mettere tutto in un db(molto più semplice) o ancora salvarti tutto il contenuto del file e lavorare sulla struttra dati di appoggio(soluzione brutta e non performante).
Ora ritorno davanti al ventolatore che mi sto sciogliendo :muro:

RaouL_BennetH
22-07-2010, 23:29
Puoi leggere il file riga per riga fin quando non trovi la riga che contiene il comune ed estrarre in questo modo il codice(visto che sai come è formata ogni linea del file). Oppure mettere tutto in un db(molto più semplice) o ancora salvarti tutto il contenuto del file e lavorare sulla struttra dati di appoggio(soluzione brutta e non performante).
Ora ritorno davanti al ventolatore che mi sto sciogliendo :muro:

Partendo dal presupposto che sto "casino" l'ho messo su a scopo didattico, e considerando che sui db sto facendo da un pò di mesi formazione in azienda, preferisco cercare di risolvere lavorando direttamente sul file.

Mi era venuta in mente una soluzione del genere (mi sto innamorando dei dizionari :D ) :

Caricare l'intero file in un dizionario <int, string> e, avendo il valore noto, posso recuperare la chiave che sarebbe il codice del comune.

Però, avendo già utilizzato dei dizionari in questo progetto, tenderei a scartarla come implementazione favorendo l'apprendimento sulle operazioni basilari di IO.

Ryuzaki_Eru
23-07-2010, 00:19
Puoi anche usare una regex che ti identifica il codice dato che sai che è formato da una lettera e tre numeri. In questo modo ti basta fare una cosa tipo:

for line in file:
if comune in line:
regex.group(0)

Se non vuoi usare le regex(ma potrebbe essere l'occasione per darti un'infarinata) puoi, sapendo come è formata ogni riga del file, giocare con le stringhe. Ad esempio, considerando il tuo esempio:

for line in file:
if comune in line:
return ",".split(line)[0]

split non fa altro che creare una lista di stringhe dividendole con la stringa sulla quale lo chiami(spiegazione da cani, ma in questo caso sarebbe la virgola :D), dopodichè ti basta prendere il primo elemento della lista risultante visto che le righe del tuo file sono formate da "CODICE - PROVINCIA - COMUNE". Nel tuo caso, ad esempio, ti ritroveresti con una lista come questa:

["A001", "PD", "ABANO TERME"]

Quindi ti basta restituire il primo elemento, che è il codice :)

Il problema è che non conoscendo ancora bene C# non so se sia presente qualcosa di simile. Ma concettualmente spero si sia capito :)

RaouL_BennetH
23-07-2010, 00:45
Non trovo il modo per recuperare la posizione in cui trovo la corrispondenza:


private void test()
{
StreamReader sr = new StreamReader(@"comuni.txt");
string s = "";
if(sr.ReadLine().Contains("MI,LAMBRATE"))
s = ?!?!?!?!?
}

RaouL_BennetH
23-07-2010, 00:56
è bruttino ma inizio a capire:


public void ProvaCodiceComune(string fileName)
{
StreamReader sr = new StreamReader(fileName);
string line = "";
while ((line = sr.ReadLine()) != null )
{
if(line.Contains("MI,LAMBRATE"))
System.Windows.Forms.MessageBox.Show(line);
}
sr.Close();
}

Kralizek
23-07-2010, 08:21
non é male, ma proverei a testare la linea con le regex (giusto per imparare qualcosa di nuovo). ed "hardcoderei" il nome del file nella routine ed accetterei nome della provincia e del comune come parametro ;)

RaouL_BennetH
23-07-2010, 11:38
Ciao :)

Con le regex ci sto provando ma riesco a farmi restituire solo true o false.
Credo che se non familiarizzo con i pattern da passare come parametri, non ne vengo fuori.

C'è una tonnellata di materiale in rete ma si fa riferimento maggiormente sull'implementazione di verifiche proprio sui pattern e non per ricercare un termine esatto.

Tutto chiaro per i parametri da passare ma non ho capito cosa intendi con "hardcodare" :D

banryu79
23-07-2010, 13:42
Tutto chiaro per i parametri da passare ma non ho capito cosa intendi con "hardcodare" :D
Semplicemente significa che ti stava suggerendo di fissare il nome del file direttamente in una stringa nel codice sorgente :)

RaouL_BennetH
23-07-2010, 14:57
Semplicemente significa che ti stava suggerendo di fissare il nome del file direttamente in una stringa nel codice sorgente :)

:doh:

cmq... ho terminato adesso anche il mio pensiero sull'ultima parte, ovvero il codice di controllo; seguendo ciò che dice l'algoritmo, si devono confrontare i caratteri pari e i caratteri dispari con le relative tabelle di conversione, sommare i loro valori e dividerli per 26 ed il resto della divisione corrisponderà ad uno dei valori presenti nella relativa tabella.

Ho proceduto così:


//le tabelle di conversione le ho rappresentate con dei dizionari
Dictionary<char, int> listaPari;
Dictionary<char, int> listaDispari;
Dictionary<int, char> codiceControllo;

//ho inserito i valori nei dizionari, per es.:
codiceControllo = new Dictionary<int, char>();
for(int i = 0, j = 65; i < 26; i++, j++)
{
codiceControllo.Add(i, (char)j);
}

....
//prendo i caratteri nelle posizioni pari
string pari = "";
for(int i = 1; i < 15; i +=2)
{
pari += codiceFinoraOttenuto[i];
}

//allo stesso modo prendo i dispari
string dispari = "";
for(int i = 0; i <= 15; i +=2)
{
dispari += codiceFinoraOttenuto[i];
}

//ora devo convertire questi caratteri nei valori presenti nelle tabelle
int nPari = 0;
int nDispari = 0;
int resto = 0;
string codice = "";

//pari
foreach(KeyValuePair<char, int> p in listaPari)
{
foreach(char c in pari)
{
if(p.Key == c)
nPari += p.Value;
}
}

//stessa cosa per dispari
foreach(KeyValuePair<char, int> d in listaDispari)
{
foreach(char c in dispari)
{
if(d.Key == c)
nDispari += d.Value;
}
}

//ora non mi resta che sommarli, dividerli e prendere il valore del resto
resto = (nPari + nDispari) % 26;

foreach(KeyValuePair<int, char> kk in codiceControllo)
{
if(kk.Key == resto)
codice = kk.Value;
}

string codiceFiscale = codiceFinoraOttenuto + codice;


Spero di aver fatto qualche passo in avanti...

banryu79
23-07-2010, 15:04
//prendo i caratteri nelle posizioni pari
...

//allo stesso modo prendo i dispari
...

//pari
...

//stessa cosa per dispari
...

Puoi prendere i caratteri pari e i dispari in un unico ciclo ;)
Suggerimento: un numero è pari se il resto della sua divisione per 2 è zero, altrimenti è dispari.

RaouL_BennetH
23-07-2010, 15:24
Puoi prendere i caratteri pari e i dispari in un unico ciclo ;)
Suggerimento: un numero è pari se il resto della sua divisione per 2 è zero, altrimenti è dispari.


mmm.... non ho ben capito:


string codiceFinoraOttenuto = "ABCDDD79C06A002";
string s = "";
for(int i = 0; i < 15; i++)
{
if(i % 2 ==0)
{
s +=[i];
}
}

provando così mi restituisce solo i dispari :what: (ammetto che sono molto stanco però....)

astorcas
23-07-2010, 15:29
mmm.... non ho ben capito:


string codiceFinoraOttenuto = "ABCDDD79C06A002";
string s = "";
for(int i = 0; i < 15; i++)
{
if(i % 2 ==0)
{
s +=[i];
}
}

provando così mi restituisce solo i dispari :what: (ammetto che sono molto stanco però....)


string codiceFinoraOttenuto = "ABCDDD79C06A002";
string s = "";
for(int i = 0; i < 15; i++)
{
if(i % 2 ==0)
{
//i è un numero pari (quindi posizioni 0-2-4... in pratica dispari)
aggiungo alla lista pari
}
else //quindi i % 2 == 1
{
//i è un numero dispari (quindi posizioni 1-3-5... in pratica pari)
aggiungo alla lista dispari
}
}


in questo modo fai tutto con un ciclo

RaouL_BennetH
23-07-2010, 15:35
ah ecco, sono incappato nel classico errore di contare da 1 :muro:

RaouL_BennetH
26-07-2010, 11:47
Ciao a tutti :)

Mi è rimasta un'ultima cosa da fare e vorrei cercare di farla con le regex.

Il codice fiscale che ottengo è una stringa senza nessuno spazio:

ABCDFF66G06F892D

come creo un pattern che mi inserisca uno spazio ogni 3 caratteri ?

O meglio, avreste una guida chiara per principianti sulle regex ?

Grazie mille :)

RaouL.

astorcas
26-07-2010, 11:58
Ciao a tutti :)

Mi è rimasta un'ultima cosa da fare e vorrei cercare di farla con le regex.

Il codice fiscale che ottengo è una stringa senza nessuno spazio:

ABCDFF66G06F892D

come creo un pattern che mi inserisca uno spazio ogni 3 caratteri ?

O meglio, avreste una guida chiara per principianti sulle regex ?

Grazie mille :)

RaouL.

Io non scomoderei le regex in questo caso, basterebbe infatti, man mano che costruisci la stringa, aggiungere lo spazio a mano.
Ti posto comunque un po' di codice:


static void Main(string[] args)
{
string cod_fis = "ABCDFF66G06F892D";

Console.Write(Regex.Replace(cod_fis, @"\w{3}", AddSpaces));
Console.Read();
}

private static string AddSpaces(Match m)
{
return m.Value + " ";
}


In pratica sostituisce ad ogni match con il pattern \w{3} (tre lettere), il match più uno spazio.

Per farti un'idea: link (www.regular-expressions.info/)

Kralizek
26-07-2010, 15:32
riguardo questo snippet:


foreach(KeyValuePair<int, char> kk in codiceControllo)
{
if(kk.Key == resto)
codice = kk.Value;
}


non puoi semplicemente fare qualcosa tipo


char codice = codiceControllo[resto];


??

RaouL_BennetH
28-07-2010, 11:08
riguardo questo snippet:


foreach(KeyValuePair<int, char> kk in codiceControllo)
{
if(kk.Key == resto)
codice = kk.Value;
}


non puoi semplicemente fare qualcosa tipo


char codice = codiceControllo[resto];


??

Si, infatti dopo aver provato con i dizionari, sempre a scopo didattico, ora stavo studiando come fare le stesse cose sia con una lista unica, sia scorrendo un array.

Se volessi vedere le differenze in termini di tempo di esecuzione, c'è una libreria apposita o devo provare da me ?


Altra domanda che riguarda invece la formattazione:

Il pattern che vorrei avere è:

"XXX XXX XXXXX XXXX X"


Anche dal link postatomi da astorcas, non sono riuscito a capire come inserire degli spazi in quelle posizioni con le regex.

Ma è normale che sono così ottuso o ci siete passati anche voi ?

astorcas
28-07-2010, 11:48
Si, infatti dopo aver provato con i dizionari, sempre a scopo didattico, ora stavo studiando come fare le stesse cose sia con una lista unica, sia scorrendo un array.

Se volessi vedere le differenze in termini di tempo di esecuzione, c'è una libreria apposita o devo provare da me ?


Altra domanda che riguarda invece la formattazione:

Il pattern che vorrei avere è:

"XXX XXX XXXXX XXXX X"


Anche dal link postatomi da astorcas, non sono riuscito a capire come inserire degli spazi in quelle posizioni con le regex.

Ma è normale che sono così ottuso o ci siete passati anche voi ?

Tranquillo le regex sono ostiche all'inizio :).
Però provo a spiegarti meglio.
\w{3} identifica 3 lettere quindi la regex scorre la stringa e ogni 3 caratteri ti dà un cosiddetto match. Col metodo regex.replace dico che ad ogni match aggiungo uno spazio. Ora, siccome lo scorrere del testo era regolare (3 per volta), scrivere una regex adatta in poche righe è risultato facile, se la cosa diventa così "a caso", le cose si complicano. Ti suggerirei di aggiungere gli spazi man mano che crei il codice, altrimenti fammelo sapere che ti faccio vedere che roba brutta viene fuori con le regex :)

RaouL_BennetH
28-07-2010, 12:29
Tranquillo le regex sono ostiche all'inizio :).
Però provo a spiegarti meglio.
\w{3} identifica 3 lettere quindi la regex scorre la stringa e ogni 3 caratteri ti dà un cosiddetto match. Col metodo regex.replace dico che ad ogni match aggiungo uno spazio. Ora, siccome lo scorrere del testo era regolare (3 per volta), scrivere una regex adatta in poche righe è risultato facile, se la cosa diventa così "a caso", le cose si complicano. Ti suggerirei di aggiungere gli spazi man mano che crei il codice, altrimenti fammelo sapere che ti faccio vedere che roba brutta viene fuori con le regex :)

Ciao :)

Più che altro le posizioni sono fisse: 3 7 13 18

Ma il bello è che neanche con string.format riesco a capire come inserire degli spazi in quelle posizioni :muro:

tutto ciò che sono riuscito a produrre è un orripilante sequenza di string.insert :mc:

Ryuzaki_Eru
28-07-2010, 12:32
Puoi farlo benissimo usando una regex come ti hanno già mostrato. Al limite inserisci gli spazi man mano che crei i vari "pezzi" di codice.

Dove ti blocchi con le regex? Ti ridò il consiglio dell'altra volta: compra il libro di Marco Beri, costa pochissimo ed è chiaro come il sole.

ratman511
28-07-2010, 12:34
Ma è normale che sono così ottuso o ci siete passati anche voi ?

io sono come te e non so se ci sono passati gli altri.a volte mi sento un idiota ma almeno tu un lavoro l'hai trovato