FreeBASIC – Grafica [1]

E così siamo finalmente arrivati a parlare di grafica.

Disegnare delle semplici figure 2D sullo schermo e interagire con queste sarà l’obiettivo principale di questa nuova serie di articoli.

Come è ormai nostra abitudine, partiamo subito con qualcosa di concreto.

Come creare una finestra grafica

La prima cosa che ci serve è creare una finestra grafica su cui poi andremo a disegnare.

Per farlo si utilizza l’istruzione screen.

screen 16
print "Ecco la mia prima finestra grafica in"
print "modalita' 16: ovvero con risoluzione 512x384 pixels."
sleep 'istruzione per sospendere l'esecuzione del programma

Dare un titolo alla finestra grafica

Per abbellire la nostra finestra grafica diamole un titolo sfruttando la subroutine windowtitle:

windowtitle("La mia prima finestra")
screen 19
sleep

Modalità grafica

Il numero subito dopo l’istruzione screen definisce la modalità grafica (mode) sulla base delle seguenti due tabelle:

Le modalità dalla 1 alla 13 sono compatibili anche con i vecchi programmi scritti con il glorioso QuickBASIC, mentre le nuove modalità dalla 14 alla 21 sono specifiche del FreeBASIC. Sarà proprio su queste ultime che ci concentreremo.

Le due tabelle ci mostrano con chiarezza che più la modalità scelta sarà alta, più grandi saranno le dimensioni digitali della nostra finestra: ovvero la risoluzione dello schermo (resolution).

La dimensione digitale di uno schermo (nel nostro caso della finestra) è espressa come numero di pixels orizzontali e verticali (p.e. 512×384).

Scriviamo ora un semplice programma per visualizzare una finestra in tutte e sedici le diverse modalità. Per farlo sfrutteremo un array e un ciclo for.

'dimensiona un array con tutte le modalità grafiche
 dim mode_array (1 to 16) as integer

 'assegna i valori
 mode_array(1) = 1
 mode_array(2) = 2
 mode_array(3) = 7
 mode_array(4) = 8
 mode_array(5) = 9
 mode_array(6) = 11
 mode_array(7) = 12
 mode_array(8) = 13
 mode_array(9) = 14
 mode_array(10) = 15
 mode_array(11) = 16
 mode_array(12) = 17
 mode_array(13) = 18
 mode_array(14) = 19
 mode_array(15) = 20
 mode_array(16) = 21

 'visualizza con un ciclo le varie finestre
 for i as integer = lbound(mode_array) to ubound(mode_array)
   screen mode_array(i)
   print "Finestra grafica in modalita': "; mode_array(i)
   print "---"
   print "premi un taso per continuare…"
   sleep 'sospende l'esecuzione del programma
 next i

Funzione screenres

Le modalità grafiche che abbiamo visto hanno delle dimensioni digitali preimpostate, ma se volessimo creare una finestra grafica di dimensioni diverse?

Niente panico! FreeBASIC viene in nostro aiuto con la funzione screenres che ci permette proprio di definire le dimensioni digitali che vogliamo.

Questa funzione restituisce il valore 0 (zero) se le dimensioni digitali specificate sono realizzabili dal nostro computer, altrimenti viene restituito un numero che specifica il tipo di errore.

Per approfondire l’argomento vai all’articolo dedicato alla gestione degli errori.

Dopo aver provato il codice più sotto, provate a specificare per esempio un numero negativo come larghezza o come altezza:

''crea una finestra grafica larga 800px e alta 50px 
select case screenres(800,50)
  case 0 ''tutto OK
    print "Ecco una strana finestra grafica!"
  case else ''errore
    beep
    print "Non posso creare la finestra grafica che desideri."
end select
sleep ''rimani in pausa

Bene. Per oggi ci fermiamo qui.

Nel prossimo articolo parleremo della risoluzione grafica: ovvero della qualità delle immagini. 🙂

FreeBASIC – lavorare con i files [4]

Nel precedente articolo abbiamo visto la modalità di accesso binary per lavorare con i files binari.

Oggi vedremo come creare un database: ovvero un file binario contenente dati organizzati in records.

Possiamo immaginare un record come una singola riga di una tabella contenente una o più di queste righe. Un record è composto a sua volta da uno o più campi che contengono ciascuno uno specifico tipo di dato (stringa, intero, ecc.). I records devono avere una lunghezza in bytes predefinita.

Per accedere ai campi dei singoli records si utilizza la modalità di accesso random.

Prima però di aprire un file binario in modalità random, è necessario definire il tipo di dato che rappresenterà il nostro record . Abbiamo già visto come farlo nell’articolo che trattava appunto il tipo di dato definito dall’utente.

Vediamo un esempio:

'definizione del tipo di dato (record)
type persona
nome as string * 10
cognome as string * 20
anno_di_nascita as integer
patente as boolean
end type

'dimensionamento e creazione della variabile associata al record
dim p1 as persona
dim p2 as persona
dim px as persona

'valorizzazione dei dati
p1.nome = "Mario"
p1.cognome = "Rossi"
p1.anno_di_nascita = 1977
p1.patente = false
p2.nome = "Maria"
p2.cognome = "Bianchi"
p2.anno_di_nascita = 1985
p2.patente = true

'crea il file
open "mio_db.dat" for random as #1 len=sizeof(persona)
put #1, 1, p1
put #1, 2, p2
close #1

'apri il file e leggi un dato
open "mio_db.dat" for random as #1 len=sizeof(persona)
get #1, 2, px
close #1

'stampa i dati del record 2
print "Nome: "; px.nome
print "Cognome: "; px.cognome
print "Anno di nascita: "; px.anno_di_nascita
print "Patente: "; px.patente

Come avrete notato è stato necessario, sia in fase di creazione che di lettura del file binario, specificare la lunghezza in byte del record. Per farlo si è utilizzata la funzione sizeof().

Bene. Con i files per adesso possiamo fermarci qui.

Nei prossimi articoli inizieremo a creare le nostre prime finestre grafiche. 🙂

FreeBASIC – lavorare con i files [3]

Dopo aver trattato nei precedenti due articoli i files di testo vedendo sia la modalità di scrittura/lettura che la modalità append, in questo nuovo articolo discuteremo di un tipo di file molto particolare: il file binario.

Un file binario è un file che contiene esclusivamente codice binario che può rappresentare qualsiasi tipo di dato.

In FreeBASIC per lavorare con i files binari si utilizza la modalità di apertura binary.

Con questa modalità di apertura si informa il compilatore FreeBASIC che si intende scrivere o leggere dei dati binari.

Puntatore di file

Nel momento in cui si crea un nuovo file binario e si scrive il primo dato, il puntatore del file viene valorizzato a 1.

Scrivere un dato binario

Per scrivere su un file un dato binario, proveniente da un buffer di dati, si utilizza l’istruzione put #.

Leggere un dato binario

Per leggere da un file un dato binario si utilizza l’istruzione get # che riversa i dati su un buffer di dati.

Con un po’ di fantasia, possiamo assimilare il buffer di un computer alla memoria a breve termine di noi umani. 😉

Esempio

dim pi_greco as single
pi_greco = 3.1415

print "La variabile pi_greco e' stata inizializzata al valore: "; pi_greco
 
'Crea il file dati e salva in binario il valore della variabile.
open "prova.dat" for binary as #1
  put #1, , pi_greco
close #1

pi_greco = 0
print "La variabile pi_greco dopo essere stata azzerata vale: "; pi_greco

open "prova.dat" for binary as #1
  get #1, , pi_greco
close #1

print "La variabile pi_greco letta dal file ora vale: "; pi_greco

Abbastanza semplice, no? 🙂

Nel prossimo articolo vedremo invece la modalità di apertura random.

FreeBASIC – lavorare con i files [2]

Nel precedente articolo abbiamo visto come creare un file di testo e come leggerlo riga per riga.

Oggi scopriremo come aggiungere del contenuto testuale.

Per farlo dobbiamo aprire un file di testo esistente in modalità append. Se il file non dovesse esistere ne verrebbe comunque creato uno nuovo:

'Crea un file di testo e scrive la prima riga.
open "prova.txt" for output as #1
 print #1, "riga 1"
close #1

'Apre il file di testo esistente e aggiunge una nuova riga di testo.
open "prova.txt" for append as #1
  print #1, "riga 2"
close #1

Scrivere e leggere valori in formato tabellare

Oltre a poter leggere una riga di testo per volta con l’istruzione line input # e a scriverne una per volta con l’istruzione print #, è possibile memorizzare in un file di testo delle informazioni in formato tabellare (dati separati da virgole) attraverso l’istruzione write # e quindi leggerli con l’istruzione input #:

dim as string a, c
dim as integer b

'Crea un file di testo con tre informazioni separate da virgola.
open "prova.txt" for output as #1
  write #1, "la gatta ha partorito", 4, "gattini"
close #1

'Apre in lettura il file di testo ed assegna a tre variabili le rispettive informazioni.
open "prova.txt" for input as #1
  input #1, a, b, c
close #1

'Stampa il valore delle variabili.
print "la prima informazione e': "; a
print "la seconda informazione e': "; b
print "la terza informazione e': "; c

Bene. Per oggi ci fermiamo qui.

Nel prossimo articolo affronteremo la modalità di apertura binary.

FreeBASIC – lavorare con i files [1]

Dopo un po’ di tempo che si è iniziato a programmare, è inevitabile che sorga l’esigenza di lavorare con delle informazioni salvate su files.

È bene quindi sapere come farlo anche con FreeBASIC.

Istruzione open

L’istruzione che si utilizza in FreeBASIC per lavorare con i files è l’istruzione open.

Di per sé l’istruzione open non specifica cosa in realtà vogliamo fare: se, per esempio, leggere un file esistente, oppure scriverci sopra, o magari crearne uno nuovo.

Per dire al nostro computer cosa vogliamo fare occorre specificare una delle seguenti cinque modalità di apertura:

  1. Per scrivere del testo a partire da un file vuoto (parola-chiave output)
  2. Per leggere il contenuto testuale (parola-chiave input)
  3. Per aggiungere del testo ad un file esistente (parola-chiave append)
  4. Modalità binaria (parola-chiave binary)
  5. Modalità casuale (parola-chiave random)

In questo articolo vedremo le prime due.

Creare un file di testo (output)

Proviamo a creare il nostro primo file di testo:

dim nome_file as string
dim numero_file as long

nome_file = "prova.txt"
numero_file = freefile 'restituisce un numero di file libero

open nome_file for output encoding "utf8" lock write as #numero_file
print #numero_file, "FreeBASIC è figo!"
close #numero_file

Vediamo di spiegare nel dettaglio cosa abbiamo fatto.

Numero del file

In FreeBASIC è necessario identificare il file su cui stiamo lavorando con un numero.

Nonostante siamo liberi di specificare direttamente noi un valore che sia compreso tra 1 e 255, è preferibile utilizzare la funzione freefile che evita il rischio di utilizzare un valore già in uso.

Nome del file

Se nella stringa del nome del file non viene specificato alcun percorso, il file verrà aperto nella cartella corrente (vedi anche la funzione curdir).

Codifica dei caratteri

Per quanto riguarda la scelta della codifica dei caratteri, in FreeBASIC possiamo sceglierne una tra le seguenti quattro attraverso l’istruzione encoding:

  1. ASCII: digitando “ascii” (codifica di default se non specificata)
  2. UTF-8: digitando “utf8” (codifica consigliata dal W3C)
  3. UTF-16: digitando “utf16”
  4. UTF-32: digitando “utf32”

Accessibilità al file

Con l’istruzione lock si può specificare se l’accesso al file è esclusivo o se ne è permesso l’accesso in lettura o scrittura anche ad altri programmi presenti sul nostro computer:

  • read: impedita la lettura da parte di altri programmi
  • write: impedita la sola scrittura
  • read write: accesso totalmente negato ad altri programmi (valore di default)

Come leggere un file di testo

Proviamo ora a leggere il contenuto del file appena creato utilizzando la modalità input e aggiungendo al nostro programma le seguenti linee di codice:

dim riga_di_testo as string

numero_file = freefile

open nome_file for input as #numero_file
line input #numero_file, riga_di_testo
close #numero_file

print riga_di_testo

Compilando e mandando in esecuzione il programma ci accorgiamo subito che la lettura del carattere accentato “è” non è andata a buon fine. Questo perché non avendo specificato la codifica dei caratteri, di default FreeBASIC ha interpretato il testo come codificato in ASCII. Basterà quindi modificare il codice aggiungendo la codifica in UTF-8 e la lettura del testo sarà corretta.

Modalità di lettura

Per leggere il contenuto di un file di testo abbiamo due diverse istruzioni:

  • line input #: che permette la lettura di una intera riga di testo sino al carattere di fine riga che viene scartato;
  • input #: che permette la lettura di valori separati da virgola (ne parleremo nel prossimo articolo).

Intercettare la fine di un file

Una volta scelta la modalità di lettura, dovremo inserirla in un ciclo in modo tale da leggere tutto il contenuto del file.

Qui si pone un problema: quando uscire dal ciclo di lettura?

Niente panico! FreeBASIC ci mette a disposizione la funzione eof che restituisce il valore -1 (true) se si è raggiunta la fine del file, altrimenti viene restituito il valore 0 (false).

Bene! Per oggi ci fermiamo qui.

Nel prossimo articolo continueremo comunque ad approfondire l’argomento e vedremo come lavora la modalità di apertura append. 🙂

FreeBASIC – ritorno dei valori

Come abbiamo visto nel primo articolo dedicato alle procedure, a differenza delle subroutines le funzioni hanno la particolarità di ritornare uno o più valori una volta che sono state chiamate.

Ciò significa, per esempio, che se scrivo una ipotetica funzione somma in grado di ricevere due argomenti, una volta eseguito il codice all’interno della funzione, questa restituirà al codice chiamante un valore pari alla somma dei due parametri (che possiamo quindi interpretare come addendi).

Ritorno dei valori per valore

Diciamo subito che in FreeBASIC la modalità del ritorno per valore è quella di default.

Non vi è quindi la necessità di specificarla attraverso la parola-chiave byval nella dichiarazione e nella definizione della funzione.

Tipologie di restituzione

Esistono tre differenti tipologie di restituzione dei valori:

'1. usando lo stesso nome della funzione
function semaforo() as string
  [...]
  semaforo = "rosso"
  [...]
end function

'2. usando la parola-chiave function
function semaforo() as string
  [...]
  function = "rosso"
  [...]
end function

'3. usando la parola-chiave return
function semaforo() as string
  [...]
  return "rosso"
end function

La differenza tra queste tre modalità sta nel fatto che nelle prime due, una volta restituito il valore, viene comunque eseguito il resto del codice presente nella funzione, mentre, nel terzo caso, l’esecuzione della funzione termina con la restituzione del valore.

Ritorno dei valori per riferimento

Alle volte potrebbe essere utile avere il riferimento al risultato di una funzione piuttosto che il suo valore.

Avere il riferimento al risultato permette infatti al codice chiamante di modificare successivamente il valore della stessa variabile utilizzata dalla funzione per fornire il risultato conoscendone appunto il suo indirizzo di memoria.

Per scegliere questa modalità di restituzione dei valori è necessario specificarla, sia in fase di dichiarazione che in quella di definizione della funzione, attraverso la parola-chiave byref.

Vediamo un esempio direttamente tratto dalla Guida del programmatore disponibile sul sito ufficiale di FreeBASIC:

'dichiarazione della funzione
declare function minimo(byref i as integer, byref j as integer) byref as integer

'dimensionamento e valorizzazione delle variabili
dim as integer a = 13, b = 7 

'stampa del valore originale delle variabili
print a, b

'stampa del valore minimo
print minimo(a, b)

'modifica del valore della variabile con valore minimo
minimo(a, b) = 0

'stampa del valore delle variabili aggiornato
print a, b

'definizione della funzione
function minimo(byref i as integer, byref j as integer) byref as integer
  if i < j then
    return i
  else
    return j
  end if
end function

Bene. Anche oggi abbiamo fatto un piccolo passo in avanti nella conoscenza del FreeBASIC.

A rileggerci al prossimo articolo. 🙂

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.