Forse sarebbe stato opportuno trattare questo argomento moooolto prima nella guida piuttosto che alla fine della sezione; non per niente, la comprensione degli algoritmi è la prima cosa che viene richiesta in un qualsiasi corso di informatica. Tuttavia, è pur vero che, per risolvere un problema, bisogna disporre degli strumenti giusti e, preferibilmente, conoscere tutti gli strumenti a propria disposizione. Ecco perchè, prima di giungere a questo punto, ho voluto spiegare tutti i concetti di base e la sintassi del linguaggio, poiché questi sono solo strumenti nelle mani del programmatore, la persona che deve occuparsi della stesura del codice.
Iniziamo col dare una classica definizione di algoritmo, mutuata da Wikipedia:
Vorrei innanzitutto porre l'attenzione sulle prime parole: "insieme di istruzioni elementari" (io aggiungere "insieme finito", per essere rigorosi, ma mi sembra abbastanza difficile fornire un insieme infinito di istruzioni :P). Sottolineiamo elementari: anche se i linguaggi .NET si trovano ad un livello molto distante dal processore, che è in grado di eseguire un numero molto limitato di istruzioni - fra cui And, Or, Not, +, Shift, lettura e scrittura - la gamma di "comandi" disponibile è altrettanto esigua. Dopotutto, cosa possiamo scrivere che sia una vera e propria istruzione? Assegnamenti, operazioni matematiche e verifia di condizioni. Punto. I cicli? Non fanno altro che ripetere istruzioni. I metodi? Non fanno altro che richiamare altro codice in cui si cono istruzioni elementari. Dobbiamo imparare, quindi, a fare il meglio possibile con ciò ci cui disponiamo. Vi sarà utile, a questo proposito, disegnare dei diagrammi di flusso per i vostri algoritmi.
Inoltre, requsitio essenziale, è che ogni istruzione sia unvicamente interpretabile, ossia che non possa esserci ambiguità con qualsiasi altra istruzione. Dopotutto, questa condizione viene soddisfatta dall'elaboratore stesso, per come è costruito. Altro aspetto importante è l'ordine: eseguire prima A e poi B o viceversa è differente; molto spesso questa inversione può causare errori anche gravi. Infine, è necessario che l'algoritmo restituisca un risultato in un numero finito di passi: e questo è abbastanza ovvio, ma se ne perde di vista l'importanza, ad esempio, nelle funzioni ricorsive.
In genere, il problema più grande di un programmatore che deve scrivere un algoritmo è rendersi conto di come l'uomo pensa. Ad esempio: dovete scrivere un programma che controlla se due stringhe sono l'una l'anagramma dell'altra. A occhio è facile pensare a come fare: basta qualche tentativo per vedere se riusciamo a formare la prima con le lettere della seconda o viceversa, ma, domanda classica, "come si traduce in codice"? Ossia, dobbiamo domandarci come abbiamo fatto a giungere alla conclusione, scomponendo le istruzioni che il nostro cervello ha eseguito. Infatti, quasi sempre, per istinto o abitudine, siamo portati ad eseguire molti passi logici alla volta, senza rendercene conto: questo il computer non è in grado di farlo, e per istruirlo a dovere dobbiamo abbassarci al suo livello. Ecco una semplice lista di punti in cui espongo come passerei dal "linguaggio umano" al "linguaggio macchina":
- Definizione di anagramma da Wikipedia: "Un anagramma è il risultato della permutazione delle lettere di una o più parole compiuta in modo tale da creare altre parole o eventualmente frasi di senso compiuto." ;
- Bisogna controllare se, spostando le lettere, è possibile formare l'altra parola;
- Ma questo è possibile solo se ogni lettera compare lo stesso numero di volte in entrambe le parole;
- Per verificare quest'ultimo dato è necessario contare ogni lettera di entrambe le parole, e quindi controllare che tutte abbiano lo stesso numero di occorrenze;
- Serve per prima cosa memorizzare i dati: due variabili String per le stringhe. Per gli altri dati, bisogna associare ad una lettera (Char) un numero (Int32), quindi una chiave ad un valore: il tipo di dato ideale è un Dictionary(Of Char, Int32). Si possono usare due dizionari o uno solo a seconda di cosa vi viene meglio (per semplicità ne userò due);
- Ovviamente, a priori, se le stringhe hanno lunghezza diversa, sicuramente non sono anagrammi;
- Ora è necessario analizzare le stringhe. Per scorrere una stringa, basta servirsi di un ciclo For e per ottenere un dato carattere a una data posizione, la proprietà (di default) Chars;
- In questo ciclo, se il dizionario contiene il carattere, allora questo è già stato trovato almeno una volta, quindi se ne prende il valore e lo si incrementa di uno; in caso contrario, si aggiunge una nuova chiave con il valore 1;
- Una volta ottenuti i due dizionari, per prima cosa, devono avere lo stesso numero di chiavi, altrimenti una stringa avrebbe dei caratteri che non compaiono nell'altra; poi bisogna vedere se ogni chiave del primo dizionario esiste anche nel secondo, ed infine se il valore ad essa associato è lo stesso. Se una sola di queste condizioni è falsa, allora le stringhe NON sono anagrammi.
Module Module1 Sub Main() Dim S1, S2 As String Dim C1, C2 As Dictionary(Of Char, Int32) Dim Result As Boolean = True Console.Write("Stringa 1: ") S1 = Console.ReadLine Console.Write("Stringa 2: ") S2 = Console.ReadLine If S1.Length = S2.Length Then C1 = New Dictionary(Of Char, Int32) For I As Int16 = 0 To S1.Length - 1 If C1.ContainsKey(S1.Chars(I)) Then C1(S1.Chars(I)) += 1 Else C1.Add(S1.Chars(I), 1) End If Next C2 = New Dictionary(Of Char, Int32) For I As Int16 = 0 To S2.Length - 1 If C2.ContainsKey(S2.Chars(I)) Then C2(S2.Chars(I)) += 1 Else C2.Add(S2.Chars(I), 1) End If Next If C1.Keys.Count = C2.Keys.Count Then For Each C As Char In C1.Keys If Not C2.ContainsKey(C) Then Result = False ElseIf C1(C) <> C2(C) Then Result = False End If If Not Result Then Exit For End If Next Else Result = False End If Else Result = False End If If Result Then Console.WriteLine("Sono anagrammi!") Else Console.WriteLine("Non sono anagrammi!") End If Console.ReadKey() End Sub End Module
A cura di: Il Totem