FreeBASIC – select case

Torniamo a parlare di strutture di controllo.

In particolare ci focalizzeremo su quelle strutture che ci danno l’opportunità di operare delle scelte.

Abbiamo già conosciuto una struttura di questo tipo: la struttura if-then-else.

Oggi conosceremo la struttura select case.

Sintassi

La sintassi di questa struttura di controllo è molto semplice. Vediamola:

select case espressione
  case valore1
    [istruzioni]
  case valore2
    [istruzioni]
  ...
    ...
  [case else]
    [istruzioni]
end select

In pratica il blocco di istruzioni che viene eseguito dipende unicamente dal valore dell’espressione.

Esempio

Facciamo un esempio molto semplice:

dim colore as string

input "Di che colore è il semaforo in questo momento"; colore

select case colore
  case "rosso"
    print "Fermati!"
  case "giallo"
    print "Attenzione!"
  case "verde"
    print "Ok, puoi andare."
  case else
    print"Mh... sei sicuro di vederci bene?"
end select

Al prossimo articolo. 😉

Il problema delle tre monete

Oggi entreremo nel mondo degli algoritmi con questo problema che potete trovare a pagina 13 del testo Il pensiero computazionale – Dagli algoritmi al coding di Paolo Ferragina e Fabrizio Luccio.

Tra tre monete di identico aspetto una potrebbe essere falsa e di peso differente. Disponendo di una bilancia a due piatti per confrontare le monete tra loro si vuole individuare la moneta falsa se c’è, e in questo caso stabilire se pesa più o meno delle altre, mediante non più di due pesate.

Prima di partire a testa bassa immaginando un ipotetico algoritmo o, peggio ancora, cominciando a scrivere direttamente del codice, fermiamoci e leggiamo lentamente, più volte, e con molta attenzione l’enunciato del problema.

Cerchiamo quindi di individuare e ordinare le informazioni essenziali che rappresenteranno i nostri dati.

Poi sforziamoci di capire cosa ci viene chiesto, in modo tale da essere in grado di formulare con precisione le giuste domande a cui dovremo cercare di dare risposta con il nostro algoritmo tradotto in codice FreeBASIC.

Le giuste domande ci guidano nella definizione del nostro obiettivo.

Raccolta delle informazioni essenziali (dati)

Suddividiamo e ordiniamo le informazioni in punti:

  • «tre monete di identico aspetto»;
  • «una potrebbe essere falsa e di peso differente»;
  • «bilancia a due piatti per confrontare le monete»;
  • «non più di due pesate».

Una volta raccolte le informazioni essenziali che, come già detto, non sono altro che i dati su cui potremo operare, analizziamo i nostri punti.

Il primo, il terzo e il quarto non sembrano presentare ambiguità. Concentriamoci quindi sul secondo punto. Questo punto, se letto in maniera poco attenta, potrebbe crearci dei problemi nel momento in cui inizieremo a progettare l’algoritmo risolutivo: il testo, infatti, riferendosi alle monete ci dice che «una potrebbe essere falsa».

Stiamo bene attenti…

Cosa ci dice questa frase?

Che ci sono delle monete false?

No!

La frase semplicemente asserisce due cose:

  1. la prima è che non ci possono essere più monete false;
  2. la seconda è che potrebbero anche non essercene.

Quindi i casi sono soltanto due:

  1. o c’è una moneta falsa;
  2. oppure non ci sono monete false.

Il secondo punto rappresenta quindi una informazione chiave per la definizione del nostro obiettivo.

Dobbiamo quindi stare sempre attenti non soltanto a ciò che di esplicito ci dicono le informazioni in nostro possesso, ma anche a ciò che ci viene suggerito in modo implicito. Molto spesso sono proprio le informazioni implicite a sfuggirci con più facilità.

Ecco allora che è bene seguire questa regola:

È bene trasformare sempre le informazioni implicite in esplicite.

Proviamo quindi a farlo con il nostro secondo punto:

  • frase implicita: «una potrebbe essere falsa e di peso differente»;
  • frase esplicita: se esiste una moneta tra le tre date che ha un peso differente dalle altre due che per definizione devono avere in questo caso pari peso, allora questa è falsa.

Definizione precisa delle domande (obiettivo)

Torniamo all’enunciato del problema per cercare di scoprire qual è il nostro obiettivo: ovvero a quali domande dobbiamo rispondere.

Ecco le frasi che ci interessano:

  • «individuare la moneta falsa se c’è»;
  • «in questo caso stabilire se pesa più o meno delle altre».

Che possiamo tradurre in queste due domande:

  1. Esiste una moneta falsa?
  2. Se esiste, pesa di più o di meno delle altre due?

Progettazione di un algoritmo risolutivo (pensiero computazionale)

Bene, ora che abbiamo raccolto i dati e definito le domande a cui dobbiamo rispondere, proviamo ad immaginare il cuore del nostro algoritmo procedendo per passi.

  • la prima cosa da fare che dovrebbe venirci in mente è quella di identificare in qualche modo le nostre tre monete per poterle riconoscere senza ambiguità: diamo quindi i nomi di prima, seconda e terza moneta;
  • a questo punto possiamo procedere con la prima pesata (#1) tra la prima e la seconda moneta;
  • da questa prima operazione potremo ricavare esclusivamente due possibili soluzioni:
    1. le due monete hanno lo stesso peso;
    2. le due monete hanno peso differente.
  • a questo punto procediamo con la seconda pesata (#2) tra la prima moneta e la terza moneta;
  • ovviamente anche da questa operazione non potremo che ottenere le due possibili soluzioni che abbiamo visto in precedenza, ovvero:
    1. le due monete hanno lo stesso peso;
    2. le due monete hanno peso differente.

E adesso viene il bello: che ce ne facciamo con questi risultati?

Proviamo a spremere le meningi…

Tabella associativa

La cosa migliore da fare sembra essere quella di costruire una Tabella associativa che metta in relazione tutti i possibili risultati delle due pesate ottenendo quindi le diverse soluzioni possibili:

'Tabella associativa risultati pesate/soluzioni possibili
'             pesata #2
'  pesata #1 |  1  |  2
'      1     |  A  |  B
'      2     |  C  |  D

Analizziamo ora il significato delle soluzioni:

  • A: pesata #1 risultato 1, pesata #2 risultato 1 → la moneta falsa non esiste
  • B: pesata #1 risultato 1, pesata #2 risultato 2 → la terza moneta è falsa
  • C: pesata #1 risultato 2, pesata #2 risultato 1 → la seconda moneta è falsa
  • D: pesata #1 risultato 2, pesata #2 risultato 2 → la prima moneta è falsa

Se siamo abbastanza confidenti – ancora non possiamo infatti dirci sicuri al 100% – che il nostro algoritmo funzioni, allora possiamo a ragion veduta cominciare a scrivere il nostro codice sorgente. Farlo prima ci avrebbe fatto perdere soltanto del tempo. Provare per credere. 😉

Scrittura del codice (coding)

Bene. Riprendiamo in mano il nostro algoritmo e cominciamo a scrivere:

'identificazione delle monete attraverso la loro dichiarazione
dim as integer m1, m2, m3

'definisci il caso di test assegnando
'i pesi delle monete
input "quanto pesa la prima moneta"; m1
input "quanto pesa la seconda moneta"; m2
input "quanto pesa la terza moneta"; m3

'verifica correttezza specifica
if m1 <> m2 and m1 <> m3 and m2 <> m3 then
  print "Attenzione! Non stai rispettando la"
  print "specifica che prevede che al massimo"
  print "ci possa essere una sola moneta falsa"
  print "con peso differente."
  end 'esci dal programma
end if

'pesata #1 tra la prima e la seconda moneta
if m1 = m2 then
  'procediamo con la seconda pesata
  if m1 = m3 then
    'Soluzione A
    print "la moneta falsa non esiste"
  elseif m1 < m3 then
    'Soluzione B
    print "la terza moneta è falsa e pesa di più"
  else 'm1 > m3
    'Soluzione B
    print "la terza moneta è falsa e pesa di meno"
  end if
elseif m1 < m2 then
  'procediamo con la seconda pesata
  if m1 = m3 then
    'Soluzione C
    print "la seconda moneta è falsa e pesa di più"
  else 'm1 < m3
    'Soluzione D
    print "la prima moneta è falsa e pesa di meno"
  end if
else 'm1 > m2
  'procediamo con la seconda pesata
  if m1 = m3 then
    'Soluzione C
    print "la seconda moneta è falsa e pesa di meno"
  else 'm1 > m3
    'Soluzione D
    print "la prima moneta è falsa e pesa di più"
  end if
end if

Rivediamo più volte il codice prima di compilarlo. Se la compilazione dovesse fallire leggiamo bene il tipo di errore che il compilatore FreeBASIC ci segnala e operiamo le opportune correzioni al codice. Una volta che la compilazione ha successo possiamo finalmente eseguire il nostro codice e passare alla fase di collaudo del software.

Test

Per collaudare il nostro software dobbiamo testare tutte le diverse combinazioni dei risultati delle due pesate (caso di test) e confrontare quindi i risultati (i nostri print a video) con le possibili soluzioni (oracolo).

Per fare questo non dobbiamo inventarci nulla, ma semplicemente riprendere la nostra Tabella associativa e giocare con i pesi rispettando le specifiche date: ovvero stando attenti a non inserire pesi diversi per tutte e tre le monete.

Potete testare il codice anche su JDoodle cliccando qui.

Buon divertimento e a presto. 😉

FreeBASIC -passaggio di argomenti

Nel precedente articolo abbiamo introdotto il concetto di procedura, sia nella forma di subroutine che di funzione.

In questo nuovo articolo vedremo un aspetto fondamentale che riguarda le procedure: ovvero il passaggio di argomenti:

Quando si chiama una procedura è possibile passarle alcune informazioni attraverso degli argomenti (ovvero variabili od oggetti). All’interno della procedura ci si riferirà a queste informazioni chiamandole parametri.

Per fare in modo che una procedura possa gestire degli argomenti, è necessario che nella sua dichiarazione venga specificata la lista dei parametri. Ciascun elemento di questa lista viene descritto attraverso un nome e la relativa tipologia di dato:

'dichiarazione di una subroutine con lista dei parametri
declare sub scheda (n as string, c as string, e as integer)

'chiamata della subroutine con argomenti racchiusi
'tra parentesi
scheda ("Mario", "Rossi", 182)

'definizione della subroutine
sub scheda (n as string, c as string, e as integer)
  print "nome: "; n
  print "cognome: "; c
  print "età: "; e
end sub

Sebbene in FreeBASIC, quando si chiama una procedura, non è obbligatorio racchiudere gli argomenti tra parentesi, per una migliore leggibilità del codice, soprattutto in presenza di più argomenti, è preferibile utilizzarle.

Sino a questo momento abbiamo parlato genericamente di passaggio di informazioni. Occorre però sapere che in FreeBASIC è possibile passare queste informazioni in due diverse modalità:

  1. passaggio per valore (che in assenza di specifica differente dichiarazione è la modalità di default)
  2. passaggio per riferimento

Vediamo ora di capire bene la differenza tra questi due diversi modi di passare le informazioni.

Passaggio di argomenti per valore (byval)

Abbiamo detto che questa è la modalità di default utilizzata in FreeBASIC.

In pratica quando si passa un argomento per valore, il relativo parametro all’interno della procedura riceve in realtà una copia del valore dell’argomento, e quindi qualsiasi variazione del valore del parametro che potrà avvenire all’interno della procedura non si ripercuoterà sul valore originale dell’argomento.

Ciò significa, per esempio, che se l’argomento è una variabile, il valore di questa variabile rimane inalterato.

Passaggio di argomenti per riferimento (byref)

Se si ha la necessità di fare in modo che le operazioni all’interno di una procedura su un determinato parametro abbiano effetto direttamente sulla variabile, o sull’oggetto passato come argomento in fase di chiamata della procedura, allora è necessario passare questo argomento anteponendo la parola-chiave byref:

'dichiarazione di una variabile
dim a as integer

'dichiarazione della subroutine
declare sub raddoppia (byref n as integer)

'valorizzazione iniziale della variabile
a = 5

'stampa
print "il valore di a prima della chiamata è: "; a

'chiamata della subroutine senza utilizzare le parentesi
raddoppia a

'stampa
print "il valore di a dopo la chiamata è: "; a

'definizione della subroutine
sub raddoppia (byref n as integer)
  n *= 2
end sub

Bene. Per oggi ci fermiamo qui. 🙂

Nel prossimo articolo approfondiremo l’argomento relativo al ritorno dei valori dopo la chiamata di una funzione.

FreeBASIC – procedure

Dopo aver introdotto nel precedente articolo il tipo di dato definito da noi, in questo nuovo articolo faremo la conoscenza delle procedure.

Iniziamo dandone una sintetica definizione:

Una procedura è un blocco di codice che può essere eseguito tramite una chiamata da qualsiasi punto del nostro programma per un numero indefinito di volte.

La stessa definizione ci aiuta a capire che ha senso scrivere una procedura ogni qual volta ci accorgiamo che una specifica sequenza di istruzioni deve essere utilizzata più volte durante la scrittura del nostro programma.

Scrivere infatti una sola volta un blocco di codice, oltre a farci risparmiare tempo, ci evita di dover andare a ripetere lo stesso tipo di correzione, su diversi punti del programma, ogni qual volta ci dovessimo accorgere che una modifica si rende necessaria. Questo aumenta la cosiddetta manutenibilità del codice.

Tipi di procedure

In FreeBASIC ci sono due diversi tipi di procedure:

  1. procedure che non restituiscono alcun valore. In questo caso le si definiscono con il termine inglese subroutines;
  2. procedure che restituiscono un valore. In questo caso le si definiscono più propriamente funzioni.

Subroutine

Per lavorare con una subroutine occorre prima dichiararla con la parola-chiave declare e poi definirla con con la parola-chiave sub.

La dichiarazione assegna un nome (o etichetta) alla procedura, mentre la definizione contiene le vere e proprie istruzioni che dovranno essere eseguite ogni volta che verrà fatta una chiamata con il nome della procedura:

'dichiarazione della procedura
declare sub mia_procedura

'chiamata della procedura
mia_procedura

'definizione della procedura
sub mia_procedura
  print "ciao"
end sub

Come è possibile notare, per poter chiamare una procedura occorre che la chiamata sia successiva alla sua dichiarazione, mentre il corpo della procedura vera e propria può essere scritto sotto alla chiamata o, come più spesso accade, in un file separato.

Funzione

Come abbiamo già detto, una funzione differisce da una semplice procedura per il fatto che la funzione restituisce sempre un valore nel punto esatto dove questa viene chiamata.

Anche una funzione, come avviene per una subroutine, va prima dichiarata assegnandole un nome. Soltanto successivamente va definita attraverso la parola-chiave function. Per la funzione è anche necessario dichiarare il tipo di dato restituito e questo lo si fa all’atto della dichiarazione:

'dichiarazione della funzione
declare function mia_funzione as integer

'dichiarazione di una variabile
dim i as integer

'chiamata della funzione
i = mia_funzione
print i

'definizione della funzione
function mia_funzione as integer
  return 10
end function

Bene. Ora che sappiamo usare anche le procedure possiamo cominciare a scrivere i nostri primi veri programmi procedurali. 😉

A rileggerci al prossimo articolo dove approfondiremo la conoscenza delle procedure affrontando il tema del passaggio di argomenti.

FreeBASIC – puntatore

Nel precedente articolo abbiamo parlato del contenitore array. Oggi faremo la conoscenza del puntatore.

Cominciamo con il darne una sintetica definizione:

Un puntatore è una variabile il cui valore rimanda ad uno specifico indirizzo di memoria.

Dichiarare un puntatore

È necessario che vi sia una diretta corrispondenza tra il tipo di dato del puntatore e il tipo di dato dell’elemento puntato.

La dichiarazione di un puntatore è simile a quella per una variabile con l’unica differenza che occorre inserire il suffisso pointer (o ptr).

Assegnare un indirizzo di memoria ad un puntatore

Per assegnare ad un puntatore l’indirizzo di memoria di un elemento si utilizza l’operatore @ (chiocciola) che sta per indirizzo di.

Accedere al dato dell’elemento puntato

Per accedere al dato dell’elemento puntato si utilizza l’operatore * (asterisco) che sta per valore di.

Esempio

dim altezza as integer = 180
dim puntatore_altezza as integer pointer = @altezza
*puntatore_altezza = 20
print "Il valore dell'altezza e': "; altezza

Questo esempio mostra chiaramente che:

Far riferimento direttamente alla variabile o al puntatore della variabile non fa alcuna differenza.

Manipolare gli indirizzi di memoria

Arrivati a questo punto è lecito porsi due domande:

  1. Ma che ce ne dobbiamo fare dei puntatori?
  2. Non sono sufficienti le variabili?

Il fatto è che in alcune circostanze può essere utile lavorare direttamente con gli indirizzi di memoria. Questo però è un argomento che rientra tra quelli avanzati e quindi ne parleremo più avanti. Un po’ di pazienza 😉

Sino a questo momento abbiamo sempre parlato dei tipi di dato standard, nel prossimo articolo inizieremo a lavorare con i tipi di dato definiti da noi.

FreeBASIC – array

Dopo aver imparato il concetto di variabile e il suo utilizzo, vediamo ora un nuovo costrutto tipico dei linguaggi di programmazione.

Immaginiamo di dover lavorare con decine o centinaia di valori tutti dello stesso tipo di dato standard o di un tipo di dato deninito da noi. Farlo dichiarando per ogni valore una variabile sarebbe un compito alquanto stancante. In questi casi si ricorre all’uso degli array.

Definizione

Possiamo quindi definire un array come un:

contenitore indicizzato – mono o multidimendionale – di elementi tutti dello stesso tipo.

Indice di un elemento

Per accedere in lettura o in scrittura ad uno specifico elemento di questo insieme, si ricorre al suo relativo indice che non è altro che un valore di tipo integer che identifica la posizione dell’elemento all’interno dell’array.

L’indice va racchiuso all’interno di due parentesi tonde:

'dimensiona un array di 3 valori stringa
dim array_semaforo(1 to 3) as string

'assegna i valori
array_semaforo(1) = "rosso"
array_semaforo(2) = "giallo"
array_semaforo(3) = "verde"

'stampa i valori dell'array
print "Il semaforo ha i seguenti colori:"
for i as integer = 1 to 3
  print array_semaforo(i)
next

Nota sul dimensionamento di un array

Se nella fase di dimensionamento di un array non viene specificato il limite inferiore, questo assumerà il valore di default 0 (zero).

Dimensione di un array

Un array ha quindi una dimensione rappresentata dal numero degli elementi che contiene. Questa dimensione può essere fissa o variabile. Questa particolarità discrimina quindi due diversi tipi di array:

  1. fixed-length arrays
  2. variable-length arrays

La dimensione di un array è data dalla differenza tra il valore massimo dell’indice, ovvero il limite superiore (upper bound), e il suo valore minimo, ovvero il limite inferiore (lower bound) + 1.

Per ottenere il limite superiore e quello inferiore possiamo utilizzare le seguenti due istruzioni:

limite_superiore = ubound(mio_array)
limite_inferiore = lbound(mio_array)

La dimensione del nostro array sarà quindi:

dimensione = ubound(mio_array) - lbound(mio_array) + 1

Indice valido

Un valore valido per l’indice di un array deve soddisfare questa caratteristica:

lower bound ≤ indice valido ≤ upper bound

Differenze tra gli array fissi e variabili

Gli array con dimensione fissa hanno una maggiore efficienza rispetto a quelli con dimensione variabile in quanto il compilatore assegna loro una porzione di memoria statica che è più velocemente accessibile della memoria dinamica.

È quindi proprio l’allocazione statica della memoria che rende la dimensione di un array immutabile.

Agli array con dimensione variabile, invece, il compilatore assegna dinamicamente la memoria necessaria durante la stessa esecuzione del programma (ovvero a run-time). È proprio l’allocazione dinamica della memoria che permette di ampliare o ridurre le dimensioni di un array a scapito però della efficienza di lavoro.

Quindi se dobbiamo creare un insieme di valori omogenei che sappiamo a priori non varieranno mai come numero di elementi, allora ci converrà scegliere un array di tipo fisso, altrimenti ne creeremo uno di tipo variabile.

Come creare un array di tipo fisso o di tipo variabile

Per creare un array di tipo fisso è necessario che i suoi limiti siano dichiarati attraverso delle costanti o dei letterali:

'creo un array di tipo fisso con 10 elementi
const limite_superiore = 10
dim array_fisso(1 to limite_superiore) as string

Per creare invece un array di tipo variabile possiamo in un caso dichiarare i suoi limiti attraverso delle variabili, oppure crearne uno vuoto e poi utilizzare l’istruzione redim per ridimensionarlo:

'I caso: creo un array di tipo variabile con 10 elementi
dim as integer lim_inf, lim_sup
lim_inf = 1
lim_sup = 10
dim array1(lim_inf to lim_sup) as string

'II caso: creo un array vuoto e poi lo ridimensiono
dim array2() as string
redim array2(1 to 10) 'non è necessario specificare il tipo

Nota: quando utilizziamo l’istruzione redim per ridimensionare gli array di tipo variabile non è necessario specificare di nuovo il tipo di dato degli elementi.

Il ridimensionamento di un array di tipo variabile causa la perdita dei dati contenuti precedentemente in esso. Per ovviare a questo occorre utilizzare l’istruzione preserve:

redim preserve array2(1 to 10)

Creare un array multidimensionale

Se abbiamo necessità di creare un array a più dimensioni non faremo altro che aggiungere nuovi limiti nella fase di creazione o di ridimensionamento.

Per esempio potremmo avere la necessità di rappresentare una tabella con 10 righe e 5 colonne:

dim mia_tabella(1 to 10, 1 to 5) as [type]

Bene. Ora che abbiamo imparato a lavorare con gli array, siamo pronti per fare la conoscenza nel prossimo articolo di un nuovo amico: il puntatore.

FreeBASIC – variabile

Nel precedente articolo abbiamo discusso del concetto informatico di costante e di come utilizzarla nel nostro codice.

È arrivato quindi il momento di parlare della variabile cominciando a darne una sintetica definizione:

Una variabile è una etichetta che rimanda a dei dati registrati in una qualsiasi porzione di memoria che possono essere modificati in qualsiasi momento.

Dichiarazione

La prima cosa da fare quando vogliamo utilizzare una variabile nel nostro codice è dichiararla attraverso l’istruzione dim:

dim mia_variabile as string

Occorre però fare attenzione che i nomi delle nostre variabili rispettino due semplici regole sintattiche:

  1. i nomi possono contenere soltanto lettere maiuscole o minuscole, numeri e il carattere _ (trattino basso, underscore);
  2. i nomi non devono contenere degli spazi.

Come abbiamo già avuto modo di vedere nell’articolo dedicato specificatamente alla dichiarazione, questa assegna uno specifico tipo alla nostra variabile; ciò comporta che dovremo utilizzarla in maniera coerente nel nostro codice: ovvero se abbiamo dichiarato che la variabile è di tipo testo (string), non potremo utilizzarla direttamente in una espressione aritmetica, a meno che non operiamo intenzionalmente una sua conversione.

Ambito di utilizzo

A seconda del punto dove dichiariamo una variabile nel codice che stiamo scrivendo, questa avrà un suo specifico ambito di utilizzo (in inglese scope) che potrà essere:

  • limitato ad una procedura (subroutine o funzione);
  • limitato ad un modulo;
  • oppure utilizzabile ovunque all’interno dei moduli che compongono il nostro programma utilizzando la parola-chiave shared.

Bene, arrivati a questo punto siamo pronti per fare la conoscenza nel prossimo articolo di un tipo di variabile molto speciale: il vettore/matrice (più comunemente noto con il termine inglese array).

FreeBASIC – costante

In questo articolo faremo la conoscenza delle costanti.

In determinate circostanze è molto utile nella programmazione ricorrere all’uso di una o più costanti.

Per esempio se in più punti del nostro codice dobbiamo utilizzare il valore del Pi greco, è bene definire una costante, non avrebbe senso infatti definire una variabile in quanto il Pi greco è una nota costante matematica:

const pi_greco = 3.14159 ''la dichiarazione del tipo è opzionale

Una volta definita una costante non sarà più possibile cambiarne il valore nel codice, pena l’incorrere in un errore in fase di compilazione.

La cosa bella delle costanti è che hanno un ambito globale (global scope): ovvero possiamo utilizzare il loro nome, e quindi il loro valore, anche tra moduli diversi dello stesso programma.

Ovviamente siamo liberi di definire costanti numeriche, booleane o stringa:

const sconto as integer = 20
const stato as boolean = true
const etichetta as string = "SCONTO"

Se non viene dichiarato esplicitamente il tipo di costante, questo viene comunque inferito dal compilatore.

Nel prossimo articolo parleremo delle variabili.

FreeBASIC – conversione del tipo di variabile

Occhio agli errori

Quando lavoriamo su un programma può presentarsi la necessità di fare delle operazioni tra tipi di variabili differenti.

Questo tipo di operazioni possono presentare dei rischi: per esempio assegnando ad una variabile numerica di tipo short (i cui limiti di memorizzazione vanno da -32768 a +32767) il risultato della somma di due variabili di tipo integer che hanno una capacità di memorizzazione molto più ampia, si può ottenere un risultato sbagliato:

dim a as integer
dim b as integer
dim c as short 'limite: da -32768 a +32767

a = 32766
b= 1
c = a + b
print c 'viene stampato a video il valore corretto 32767

a = 32767
b= 1
c = a + b
print c 'viene stampato a video il valore errato -32768

In questo caso il compilatore FreeBASIC non ci avverte neanche del problema. Quindi occorre assolutamente stare attenti.

Modalità di conversione

Esistono due modalità di conversione di tipo:

  • conversione implicita (coercion)
  • conversione esplicita (conversion)

Modalità implicita

In una espressione con più argomenti, o nelle procedure (nel passaggio degli argomenti o nel ritorno dei risultati), il compilatore FreeBASIC provvede ad operare una serie di conversioni automatiche di tipo di dato secondo i seguenti criteri:

  1. tutte le variabili numeriche con capacità più piccola di quella del tipo integer vengono convertite nel tipo integer;
  2. più in generale, le variabili numeriche vengono convertite nello stesso tipo di dato della variabile con più capacità (vedi Tabella dei limiti dei tipi di dato standard);
  3. se in una espressione compaiono variabili di tipo single, queste vengono convertite in double.

Modalità esplicita

Come programmatori possiamo anche forzare la conversione di tipo chiedendo esplicitamente al compilatore FreeBASIC – per esempio con l’istruzione cast – di operare una determinata conversione.

Oltre all’istruzione cast, esistono diversi operatori e procedure di conversione di tipi di dato che sono predefinite in FreeBASIC. Per esempio:

'funzione str
'converte una espressione numerica in una stringa
dim a as integer
dim b as integer
dim tot as string
a = 3
b = 2
tot = str(a + b)
print tot

oppure:

'funzione val
'converte una stringa in un valore numerico a virgola mobile (double)
dim a as string
dim b as integer
a = "1"
b = 4 + val(a)
print b 'stampa a video 5

A rileggerci nel prossimo articolo dove parleremo delle costanti.

FreeBASIC – letterali

Innanzitutto, cosa intendiamo con il termine letterale?

Un letterale è un valore di tipo numerico (0-9) o booleano (true/false), oppure una stringa di caratteri che il programmatore scrive direttamente nel codice e su cui il compilatore FreeBASIC non fa alcun tipo di elaborazione/calcolo.

Utilizzo dei letterali

Un valore letterale può essere assegnato ad una qualsiasi variabile (o costante) coerentemente al suo tipo (vedi tabella dei tipi standard):

dim as integer mio_numero = 7
dim as boolean mio_valore = true
dim as string mio_testo = "ciao"

Oppure può essere utilizzato direttamente in una espressione:

dim as integer somma
somma = 4 + 3

Un utilizzo più avanzato è come parametro in una procedura. Questo argomento lo tratteremo in uno dei prossimi articoli.

Letterali numerici interi

In FreeBASIC possiamo scrivire un letterale numerico intero (integer, byte, ecc.) in quattro differenti forme:

Forma decimale

Si utilizzano le cifre decimali (0-9) e per i numeri negativi si antepone il simbolo (-):

dim as integer a = 8200364
dim as byte b = -121

Forma esadecimale

Si antepone “&h” alle cifre esadecimali (0-F):

dim as integer a = &h2F
dim as byte b = &h3

Forma ottale

Si antepone “&o” alle cifre ottali (0-7):

dim as integer a = &o34
dim as byte b = &o4

Forma binaria

Si antepone “&b” alle cifre binarie (0-1):

dim as integer a = &b01110
dim as byte b = &b101

Letterali numerici in virgola mobile

Un letterale numerico in virgola mobile è specificato tramite le cifre (0-9) e il simbolo del punto (.) come separatore dei decimali. Per i numeri negativi si antepone il simbolo (). Opzionalmente può avere un esponente:

dim as single a = -3.54
dim as double b = 4576.83457287
dim as double c = 43.42e-4 'elevamento con esponente negativo

Letterali stringa

I letterali stringa sono sequenze di caratteri racchiusi tra doppie virgolette:

dim as string s = "pippo"

I letterali stringa possono contenere le cosiddette sequenze di escape anteponendo alle prime doppie virgolette il simbolo (!):

dim as string s = !"prima riga\nseconda riga"

Anteponento invece il simbolo ($) si forza il compilatore a considerare la sequenza come priva di caratteri di escape:

dim as string d = $"c:\temp"

Letterali booleani

Questi letterali rappresentano i due valori di verità vero/falso:

dim as boolean a = true
dim as boolean b = false

Bene. Per adesso ci fermiamo qui.

Nel prossimo articolo parleremo di conversione del tipo di variabile.