I bachi inattesi? Colpa della Semantica, signori miei...
Questo post, frivolo nell’esemplificazione che darò, pretende in modo alquanto serioso di discutere su un problema che affligge l’Informatica e non solo. Parlo della Semantica, disciplina linguistica che si occupa del significato di un testo, meaning in inglese. Di sfuggita cito solo un interrogativo provocatorio (che riprendo – se non ricordo male - dal ponderoso trattato di Semiotica generale di Umberto Eco): The meaning of the meaning che diavolo è?
In due parole, alla buona, la faccenda ha a che fare - nella vita o peggio ancora nella giurisprudenza - coi molti fraintendimenti derivanti dalla più o meno evitabile divaricazione fra i significati letterali e quelli intenzionali (cosiddetti). In gergo semiotico si tenta una distinzione fra denotazione e connotazione di un messaggio.
Venendo allo specifico dell’Informatica sono noti i fallimenti di certi settori avanzati. Tipicamente l’Intelligenza Artificiale (IA) o un suo figliolo, la traduzione automatica. L’IA ogni tanto viene ri-chiamata in ballo ma oggi dei tanto celebrati Sistemi Esperti quasi nessuno parla più. La seconda da qualche tempo partorisce qualche topolino non del tutto privo di utilità, però ognuno di noi ha quotidianamente modo di ridacchiare di fronte a certi risultati del pur potente traduttore di Google...
Un test al volo, mentre scrivo queste noterelle. “Non tutto il male viene per nuocere” mi viene reso con “Every cloud has a silver lining”!!! Il buffo è che la traduzione inversa restituisce “Ogni nuvola ha un rivestimento d'argento”. Formalmente corretto, a parte la stramberia semantica e l’ancor più imbarazzante fatto che non ha nulla a che fare con l’originale.
Il frivolo esempio: ruotine eliminare cifre da una stringa
Appartiene all’autobiografia recente. Volevo una routinetta volta a eliminare i caratteri numerici da una stringa. Ed ecco una serie di tentativi infruttuosi di specifiche Sub, prima in VBA poi in Visual Basic 2008, che ho corredato con istruzioni messaggistiche opportune per facilitarne l’esecuzione passo dopo passo a chi legge.
Prima versione
Sub EliminaCifre()
Dim MiaStr As String, Car As String, i As Integer
MiaStr = "Amba12raba2345cicci178"
For i = 1 To Len(MiaStr)
Car = Mid(MiaStr, i, 1)
If IsNumeric(Car) Then
MiaStr = Replace(MiaStr, Car, "")
End If
MsgBox("i = " & i & vbLf & "Car = " _
& Car & vbLf & "MiaStr = " & MiaStr)
Next
MsgBox(("Stringa finale: " & MiaStr)
End Sub
Lanciando la procedurina si notano due stranezze inattese (per gente ingenua come molti di noi siamo):
- non tutte le cifre vengono eliminate e l’MsgBox conclusivo proclama “Stringa finale: ambaraba34cicci8”;
- il ciclo va avanti fino a i = 22 (la Len iniziale della stringa), indicando caratteri vuoti (blank) negli ultimi passaggi.
Dopo riflessione mi sono reso conto che il guaio deriva dal fatto che lunghezza della MiaStr diminuisce di un’unità ad ogni rimpiazzo di una cifra con “” (Replace(MiaStr, Car, “”). La pezza (patch) è presto trovata.
Seconda versione
Sub EliminaCifreBIS()
Dim MiaStr As String, Car As String, i As Integer
MiaStr = "Amba12raba2345cicci178"
For i = 1 To Len(MiaStr)
Car = Mid(MiaStr, i, 1)
If IsNumeric(Car) Then
MiaStr = Replace(MiaStr, Car, "")
i = i - 1
End If
MsgBox ("i = " & i & vbLf & "Car = " _
& Car & vbLf & "MiaStr = " & MiaStr)
Next
MsgBox ("Stringa finale: " & MiaStr)
End Sub
L’aggiunta, evidenziata in giallo, è semplicemente la correzione i = i - 1 ogni volta che una cifra è rimpiazzata. Ci potremmo accontentare, visto che il risultato ora è corretto, anche se resta la seconda anomalia.
Da queste come da altre esperienza si evince facilmente il principio enunciato in apertura, che nel caso del software, tradurrei nella legge seguente:
In Informatica è la Semantica lo scoglio in cui s’infrangono molti prodotti, a causa della discrasia non sempre evitabile fra l’intenzionalità dell’uomo e la fredda, inesorabile logica letterale della macchina.
Visual Studio 2008: mondo nuovo, altra sorpresa
In tale ambiente ho poi testato il precedente programmetto. Funzionava, ma poi pur non essendo un benpensante ho... ben pensato di sostituire Car As String con Car As Char (non supportato in VBA o VB6) più moderno e logico, visto che si tratta di un singolo carattere. Già che c’ero, ho approfittato della possibilità di accedere con un indice a ciascun carattere di una stringa, con Car = MiaStr(i).
Terza versione (VB 2008)
Sub EliminaCifre()
Dim MiaStr As String, Car As Char, i As Integer
MiaStr = "Amba12raba2345cicci178"
For i = 0 To Len(MiaStr) - 1
Car = MiaStr(i)
If IsNumeric(Car) Then
Mid(MiaStr, i, 1) = Replace(MiaStr, Car, "")
i -= 1
End If
MessageBox.Show("i = " & i & vbLf & "Car = " _
& Car & vbLf & "MiaStr = " & MiaStr)
Next
MessageBox.Show("Stringa finale: " & MiaStr)
End Sub
Tutto bene? Macché. L’illusione semantica stavolta naufraga in modo, come sempre, inatteso. Infatti non si procede con tutti i 22 giri del loop For i = 0 To Len(MiaStr) ... Next in quanto ahimè il programma si arresta per errore a run-time sulla tanto ammirata istruzione moderna Car = MiaStr(i), con indicazione di indice fuori dai limiti.
Riflettendo adeguatamente comprendiamo che:
- il ciclo For... To Next del Visual Basic (a differenza di altri linguaggi, credo) valuta una tantum l’espressione all’inizio e ne mantiene il valore in tutto il suo corso (in altri casi questa è una virtù, ma qui ci crea un guaio);
- Mid(MiaStr, i, 1) è permissivo al punto di non dare errore se l’indice va fuori della stringa (semplicemente restituisce un blank); al contrario di Car(i), che invece considera la stringa come un vettore di caratteri.
Nota. Stavolta potremmo anche prendersela con l’architetto del VB 2008 di Microsoft, ma dobbiamo rassegnarci e prendere atto della realtà.
Soluzione finale
Ne riporto il nocciolo, astenendomi dal commentare altre varianti “moderne”, dicendo solo che alla fine ho adottato un ciclo While ... End While (While... Wend in VBA/VB6) che è immune dalla persistenza della lunghezza imputabile a For... Next e, pertanto, fa sì che MiaStr.Length muti dinamicamente ad ogni giro dell’anello.
Dim i = 0
While i < MiaStr.Length
Car = MiaStr(i)
CodCar = Char.ConvertToUtf32(Car, 0)
If Not (CodCar < 48 Or CodCar > 57) Then
MiaStr = MiaStr.Replace(Car, "")
i -= 1
End If
i += 1
End While
?>
?>