Italian Array con indice letterale

MaAncheNo

Member
Licensed User
Longtime User
Non mi è chiaro.
Quello che scrivevo era solo per dire che non avevi bisogno di una mappa o un array per cercare il riferimento numerico di una particolare lettera visto che potevi ricavare l'indice nell'array dei valori direttamente dalla lettera.

Se esiste un problema dove un codice tipo AKKT12345 deve essere letto come AKT123, ovvero sostituendo la doppia K con una sola, allora bisognerebbe lavorare sul codice in ingresso e fornirlo "ripulito" all'alagoritmo del checkdigit che hai già preparato.
Ti serve aiuto su questo punto? Eliminare da una stringa dei doppioni (o multipli) ?

No, è solo che a lettera X corrisponde numero N ma i numeri non sono consecutivi:
B4X:
    Dim Indice() As String= Array As String("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z")
    Dim Value() As Int = Array As Int (10,12,13,14,15,16,17,18,19,20,21,23,24,25,26,27,28,29,30,31,32,34,35,36,37,38)
"Saltano" 11, 22, 33 ..... anche se non mi è ancora chiaro il motivo (ISO6346).
 

Star-Dust

Expert
Licensed User
Longtime User
Su wiki ho trovato un esempio in VB:
B4X:
Function ISO6346Check(k As String) ' Calculates the ISO Shipping Container Check Digit
Dim i%, s&
Application.Volatile
For i = 1 To 10
s = s + IIf(i < 5, Fix(11 * (Asc(Mid(k, i)) - 56) / 10) + 1, Asc(Mid(k, i)) - 48) * 2 ^ (i - 1)
Next i
ISO6346Check = (s - Fix(s / 11) * 11) Mod 10
End Function
Sarebbe ancora più compatta ma necessita di traduzione VB->B4A :D
Stanotte se ho tempo ci riprovo...
Funziona come il calcolo del codice fiscale, di trasformare le prime tre lettere corrispondenti numeri. Solo che non è come il codice ASCII, che i numeri sono sequenziali ma sono a saltare quindi non c'è una progressività. Il saltonon è uniforme.

Bisogna aprire semplicemente una lista in cui associare le lettere i numeri ma basterebbe solo una lista
 

MaAncheNo

Member
Licensed User
Longtime User
Stamattina presto ho provato l'algoritmo su Wikipedia e visto che aveva ancora la mente lucida, l'ho studiato. E non mi sembra di aver visto la necessità di dichiarare numero reale visto che sono tutte le pozioni a Potenza di numeri interi
C'è una divisione per 11....

Interessante anche questa indicizzazione: "0123456789A?BCDEFGHIJK?LMNOPQRSTU?VWXYZ"
 

Star-Dust

Expert
Licensed User
Longtime User
C'è una divisione per 11....
Non è una divisione ,è il calcolo del resto, che dai un numero intero, quindi non serve assolutamente che sia un numero reale

Nella formula originale è così
Rst=Somma - Floor(somma/11) * 11
Quindi
Art= Somma mod 11
 

udg

Expert
Licensed User
Longtime User
Bisogna aprire semplicemente una lista in cui associare le lettere i numeri ma basterebbe solo una lista
Era esattamente ciò che proponevo io. Fare a meno del primo array e della mappa e pescare il valore numerico direttamente dall'array Value.
Poi, visto che sappiamo che i numeri "saltati" sono 11,22,33 e che si trovano in una ben definita posizione non è improponibile pensare ad una formula che ricavi anche Value tenendo conto di queste 3 "eccezioni" (se ne vale la pena).
 

Star-Dust

Expert
Licensed User
Longtime User
Era esattamente ciò che proponevo io. Fare a meno del primo array e della mappa e pescare il valore numerico direttamente dall'array Value.
Poi, visto che sappiamo che i numeri "saltati" sono 11,22,33 e che si trovano in una ben definita posizione non è improponibile pensare ad una formula che ricavi anche Value tenendo conto di queste 3 "eccezioni" (se ne vale la pena).
non sono quelli i numeri.
Sono: 10.11,13,14,16...

https://en.m.wikipedia.org/wiki/ISO_6346
 

udg

Expert
Licensed User
Longtime User

Star-Dust

Expert
Licensed User
Longtime User
ABCDEFGHIJKLM
10,12,13,14,15,16,17181920212324
 

udg

Expert
Licensed User
Longtime User
E non sono gli stessi caricati in Value() ?
Altra domanda: non ho letto da nessuna parte che bisogna evitare i doppioni.
E' vero che non ho ancora preso il mio primo caffè del giorno..ma mi viene il dubbio che per doppioni intendesse semplicemente 11, 22 e 33..sigh!

Ad ogni modo su Wikipedia c'è la formula da seguire per crearsi l'algoritmo senza dover "tradurre" quello di altri linguaggi. Se po' fa..

ps: buona anche l'idea dell'esempio in Java (e JS) dove si ricava il valore numerico direttamente dalla posizione del simbolo in una stringa che contiene numeri, lettere e caratteri "placeholder" non validi per "saltare" i soliti 11,22, e 33.

pps: e dopo 29 messaggi, dovremmo ricevere un container a testa; magari per Natale così lo mettiamo sotto l'albero ed abbiamo anche il gusto della sorpresa
 
Last edited:

udg

Expert
Licensed User
Longtime User
Volevi codice compatto? Guarda se può piacerti questa versione:
B4X:
'Compute the checkdigit as per ISO 6346 on eqn (equipment number)
'No check if eqn is a valid equipment number
Sub Compute6346CD(eqn As String) As Char
   Dim const s As String = "0123456789A?BCDEFGHIJK?LMNOPQRSTU?VWXYZ"
   Dim i, total As Int = 0
   For j = 0 To eqn.Length-1
     total = total + (s.IndexOf(eqn.CharAt(j)) * Power(2, j))
   Next
   i = (total Mod 11)
   If i = 10 Then Return 0 Else Return i
End Sub

Totalmente ispirato da codice in versione C# sulla pagina di Wikipedia e da quanto già rilevabile nei post precedenti.
Di mio aggiungerei un paio di interessanti funzioni:
B4X:
'Check whether code is a valid ISO 6346 code complete of its checkdigit
Sub IsValid6346Code(code As String) As Boolean
   Return Regex.IsMatch("\w{3}[UJZ]\d{7}",code)
End Sub

'Check whether eqn is a valid equipment number based on ISO 6346 rules
Sub IsValid6346EN(eqn As String) As Boolean
   Return Regex.IsMatch("\w{3}[UJZ]\d{6}",eqn)
End Sub

La seconda la utilizzere prima di cercare di calcolare un checkdigit per quello che potrebbe essere un valore iniziale errato o non conforme.
La prima potrebbe essere utile per capire se un codice completo di checkdigit è formalmente corretto
Lascio al tuo divertimento la terza ( o la modifica della prima): preso un codice completo, verifica che sia corretto non solo formalmente ma anche sostanzialmente, controllando che il checkdigit incluso nel codice sia efefttivamente quello ricavabile dal calcolo.

bye
 

Star-Dust

Expert
Licensed User
Longtime User
Poveti fare di meglio :)

B4X:
i = (total Mod 11)
If i = 10 Then Return 0 Else Return i
Sostituisci con
B4X:
' I risultato con mod 11 saranno un numero da 0..10 
'... se a sua volta fai un altro mod il numero 10 diventa 0... come vuole la regola
return ((total Mod 11) mod 10)
 
Last edited:

udg

Expert
Licensed User
Longtime User
ehehe vero, ma prima avrei dovuto bere il terzo caffé..eheh
 

udg

Expert
Licensed User
Longtime User
Un'ottimizzazione che potrebbe aver senso (in funzione della richiesta iniziale) potrebbe anche essere quella di evitare l'elevamento a potenza, che, almeno un tempo, veniva considerata una "compute intensive operation"; in fondo sappiamo a priori sia la base che quali esponenti utilizzeremo, quindi basterebbe un array con i valori pre-calcolati.
Nel mio codice userei la stessa j del ciclo For per prelevare il valore corrispondente in un nuovo array Potenze(1,2,4..).
 

MaAncheNo

Member
Licensed User
Longtime User
Un'ottimizzazione che potrebbe aver senso (in funzione della richiesta iniziale) potrebbe anche essere quella di evitare l'elevamento a potenza, che, almeno un tempo, veniva considerata una "compute intensive operation"; in fondo sappiamo a priori sia la base che quali esponenti utilizzeremo, quindi basterebbe un array con i valori pre-calcolati.
Nel mio codice userei la stessa j del ciclo For per prelevare il valore corrispondente in un nuovo array Potenze(1,2,4..).


Esattamente quello a cui ho accennato, si possono usare diversi metodi per ottenere lo stesso risultato ma si deve anche tenere conto di ciò che si vuole ottimizzare:

B4X:
    Dim n, total As Int
    total = 53741
  
    ' 1
    n = (total Mod 11)
    If n = 10 Then n = 0

    ' 2
    n = ((total Mod 11) Mod 10)

    ' 3
    n = total - Floor(total / 11) * 11

Ho fatto dei test in termini di tempo di elaborazione, le prime due sono molto simili, un piccolo vantaggio per la 2, forse a causa delle istruzione supplementari della 1, ma la 3......... è proprio una ciofeca:mad:.
Quindi sostituire Power e Floor se si cerca la velocità è obbligatorio!
 

MaAncheNo

Member
Licensed User
Longtime User
Raccogliendo tutti i suggerimenti è venuta fuori questa che quasi certamente è la più veloce in termini di tempo di esecuzione e nello stesso tempo abbastanza chiara:

B4X:
public Sub ISO_6346_Ck(CtrNum As String) As Boolean
    Dim acc, ax As Int
    Dim s As String
    Dim lastnum, cnt, checkdigit As Int
    Dim Indice() As String= Array As String("0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z")
    Dim Value() As Int = Array As Int (0,1,2,3,4,5,6,7,8,9,10,12,13,14,15,16,17,18,19,20,21,23,24,25,26,27,28,29,30,31,32,34,35,36,37,38)
    Dim Pow() As Int = Array As Int (1,2,4,8,16,32,64,128,256,512)
    Dim M As Map
    M.Initialize
    acc = 0
    For cnt= 0 To Indice.Length - 1
        M.Put(Indice(cnt),Value(cnt))
    Next
    For cnt = 0  To 9
        s = CtrNum.CharAt(cnt)
        ax = M.Get(s) * Pow(cnt)
        acc = acc + ax
    Next
    lastnum = ((acc Mod 11) Mod 10)
    checkdigit = Asc(CtrNum.CharAt(10))-48
    If (CtrNum.Length = 11) And (lastnum = checkdigit) Then
        Return(True)
    End If
    Return (False)
End Sub

LA cosa ancora per me oscura è perché non possa chiamare direttamente ax = M.Get(CtrNum.CharAt(cnt)) * Pow(cnt) ma devo fare un passaggio intermedio, forso dovrò rileggermi le Map :(
 

Star-Dust

Expert
Licensed User
Longtime User
Mi dispiace contraddirti. Ma non é il miglior codice, Questo é meglio: :D:D

B4X:
public Sub ISO_6346_Ck(CtrNum As String) As Boolean
    Dim acc, ax As Int
    Dim lastnum, cnt, checkdigit As Int
    Dim Indice As List= Array As String("0","1","2","3","4","5","6","7","8","9","A","?","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","?","V","W","X","Y","Z")
    Dim Pow() As Int = Array As Int (1,2,4,8,16,32,64,128,256,512)

    For cnt = 0  To 9
        ax = Indice.IndexOf(CtrNum.CharAt(cnt)) * Pow(cnt)
        acc = acc + ax
    Next
    lastnum = ((acc Mod 11) Mod 10)
    checkdigit = Asc(CtrNum.CharAt(10))-48
    If (CtrNum.Length = 11) And (lastnum = checkdigit) Then    Return(True) Else Return (False)
End Sub
 

MaAncheNo

Member
Licensed User
Longtime User
Ops!!! Huston, abbiamo un problema!
O meglio 2, anche se il primo è una banalità, manca il salto tra "K","?","L"
L'altro è dovuto a lo strano comportamento di B4A come ho accennato da qualche parte sopra... ovvero funziona solo così:
B4X:
        s = CtrNum.CharAt(cnt)
ax = Indice.IndexOf(s) * Pow(cnt)

e non così:

B4X:
ax = Indice.IndexOf(CtrNum.CharAt(cnt)) * Pow(cnt)

Con s = stringa

P.S. E comunque la tua versione è più veloce.... :p:p:p
 

Star-Dust

Expert
Licensed User
Longtime User
Prova così:

Primo problema
B4X:
Dim Indice AsList= ArrayAsString("0","1","2","3","4","5","6","7","8","9","A","?","B","C","D","E","F","G","H","I","J","K","?","L","M","N","O","P","Q","R","S","T","U","?","V","W","X","Y","Z")

Secondo
B4X:
 ax = Indice.IndexOf(CtrNum.Substring2(cnt,cnt+1)) * Pow(cnt)
 

MaAncheNo

Member
Licensed User
Longtime User
Si, il primo punto lo avevo già visto.
Il secondo funziona ma meriterebbe però un'indagine approfondita, è come se il parser del compilatore non riuscisse a capire che CtrNum.CharAt(cnt) restituisce una stringa.
Era la stessa cosa che affermavo quando dicevo che ax = M.Get(CtrNum.CharAt(cnt)) * Pow(cnt) non funzionava.
 
Top