Linguaggio generale e di
simulazione
SIMULA (SIMUlation LAnguage) ha origine in Norvegia nel 1962 al
Norwegian Computing Centre in Oslo, ad opera di Kristen Nygaard, esperto di
Ricerca Operativa e di Ole-John Dahal un programmatore con esperienza nella costruzione di compilatori.
Nel 1957 il Norwegian Computing Centre acquisì un calcolatrore Ferranti MERCURY, sul quale Kristen Nygaard iniziò l’implementazione di un programma di simulazione (Monte Carlo compiler); questa attività indirizzò la sua ricerca verso la formalizzazione della descrizione di sistemi complessi con l’obiettivo di poterli simulare con un computer.
SIMULA I doveva essere un linguaggio per descrivere dei sistemi (servizi, aeroporti, impianti industriali, …) e per effettuare simulazione, ma durante l’implementazione risultò evidente che esso doveva avere anche le capacità di un linguaggio, ciò portò al coinvolgimento nell’attività di sviluppo di Ole-John Dahal. Nell’agosto del 1962, in occasione della seconda conferenza internazionale sull’Information Processing in Germania a Monaco, SIMULA fu presentato come “un’estensione di ALGOL per la descrizione di reti di eventi discreti”. Il progetto di SIMULA poté svilupparsi grazie ad un accordo con la UNIVAC, interessata a rafforzare l’offerta di ALGOL in alternativa al FORTRAN dell’IBM.
L’idea iniziale era di creare un pecompilatore per ALGOL (1963) per descrivere una rete di servizi attivi (station) attraverso la quale fluiscono dei clienti (customer); ma gli sviluppatori si resero conto che la struttura di ALGOL, in particolare lo stack, non era sufficiente per gli aspetti di simulazione, per cui decisero di modificare un compilatore ALGOL (1964) per adattarlo ai loro scopi.
Il linguaggio, sviluppato su UNIVAC 1107, incontrò un certo successo e fu portato su Borroughs B5500 (1968) e sul sovietico URAL 16 (1967).
SIMULA I ha l’aspetto formale di un programma specializzato, con tipi di
dato ed istruzioni ad hoc:
· customer una lista di clienti con i loro attributi,
I blocchi di programma di SIMULA I, mutuati da ALGOL, sono visti
esternamente come operazioni e, se dichiarati come procedure con dati locali
accessibili dall’esterno, hanno di fatto alcune caratteristiche degli oggetti.
SIMULA I fu utilizzato anche al di fuori dei progetti di simulazione, ciò rese ancor più evidente che la sua evoluzione doveva renderlo un linguaggio di programmazione completo ed in particolare acquisire la capacità di trattare stringhe di caratteri e poter accedere ai file.
Fu deciso di mantenere un’alta compatibilità con
AlGOL 60 e nel 1967 iniziò la fase di progettazione e di implementazione con la
disponibilità del linguaggio fra 1969 ed il 1972 su CONTROL DATA 3000, UNIVAC
100 e IBM 370.
A differenza di altri linguaggi che hanno avuto una
diffusione analoga, SIMULA 67 è rimasto sostanzialmente invariato nel tempo.
|
numeri e caratteri |
booleani |
testi |
REF |
assegnazione |
:= |
:= |
:= |
:- |
uguale |
= |
EQV |
= |
== |
diverso |
<> |
NOT variab
EQV |
<> |
=/= |
I tipi di dato sono numeri interi e reali, boolean, caratteri e referenze. Quest’ultime sono di fatto l’indirizzo di un oggetto. SIMULA è bizzarramente semiortogonale per quanto riguarda alcuni operatori sui diversi tipi di dato, come si può notare nella figura a lato.
La struttura dei
programmi è a blocchi begin ... end con
i dati dichiarati all’interno del blocco
non visibili all’esterno del
blocco:
begin comment
starting declarations; text
array T(1:100);
! array declarations; Integer
I;
Integer
LL = 100;
comment constant declarations; ref (InFile)
Inf;
! declaration of file reference; !
starting instructions; Inf :-
new InFile("IN.TXT");
inspect
inf do
! per evitare inf.open, ...; begin
Open(Blanks(LL));
! open file;
while not endfile do
begin
I := I + 1;
InImage;
! read line in buffer;
T(I) :- Inf.Image; ! put
line in array;
OutText(T(I));
end;
Close; end
of inspect; end of program |
Si notino le differenti sintassi dei commenti, !
... ; o comment
...;,
obbligatoriamente terminati da ;, ma anche la possibilità di inserire commenti dopo la
parola riservata end.
Si possono utilizzare matrici di tutti i tipi di dato, in esse le dimensioni sono implicitamente date indicando il primo e l’ultimo elemento (in realtà sono liste i cui elementi sono accessibili con chiave numerica), e poiché lo spazio per le variabili è allocato ogni volta che il blocco è attivato, si possono indicare matrici i cui limiti dipendono da variabili esterne al blocco e dunque ottenere una certa dinamicità.
L’istruzione condizionale è l’usuale if espressione then ... else, in cui espressione può contenere gli operatori logici not, and, or, impl e eqv, e anche and then e or else il cui effetto è di evitare la valutazione di entrambe le espressioni in and o or, quando non è necessario (short circuit).
Interessante è l’utilizzo delle espressioni condizionali nell’assegnazione:
i:=
inint; ! chiede un numero;
outtext(if mod(i, 2) = 0 then "pari" else
"dispari");
Esiste il while condizione do ... ed il for variabile := espressione step ... until ... do ..., questo ha un’utile variante per eseguire le iterazioni per insieme di valori, ad esempio:
for
pcard := "nord","sud","est","ovest" do
outtext(pcard);
E’ presente anche una sorta di struttura case, il comando inspect oggetto do ... limitato agli oggetti con scopo principale di omettere il qualificatore oggetto all’interno del blocco (vedi esempio in Figura 2-24 ) e in cui opzionalmente sono accettate clausole when e otherwise.
Funzioni, procedure e class, gli oggetti nella terminologia di SIMULA, sono blocchi con un nome e con l’indicazione dei parametri, le funzioni si differenziano dalle procedure, in quanto sono prefissate col tipo di dati che restituiscono ed il nome della funzione è il nome della variabile contenente il valore calcolato. Le variabili sono passate per valore (VALUE) riferimento (REF) o nome (NAME), quest’ultima forma permette alle variabili di essere modificate dalla procedura.
Le procedure di ALGOL furono lo strumento per trasformare le activity di SIMULA I in classi di oggetti, questo salto concettuale fu favorito dall’osservazione che esistono processi che hanno delle proprietà comuni e, se realizzati come oggetti, possono essere usati per costruire degli oggetti derivati (ereditarietà), l’ereditarieà si ottiene prefissando il nome della classe con la classe da cui si vuole ereditare.
Una delle conseguenze formali dell’introduzione degli oggetti è che stringhe, input/output e liste sono implementati come oggetti. La sintassi per creare un’istanza di un oggetto, class è ridondante, si veda l’esempio di Figura 2-24 applicata ad un file in lettura in cui è necessario:
· dichiarare una variabile per il riferimento ad un oggetto della classe InFile,
· creare un oggetto InFile,
· utilizzare del file.
La gestione dei file è comunque macchinosa: in input occorre alimentare il buffer tramite InImage, prima di poter assegnarne il contenuto ad una variabile testo; in output si hanno procedure per scrivere testi (OutText), numeri (OutInt e OutFix), caratteri (OutChar)e ritorno a capo (OutImage).
Le stringhe, devono essere allocate tramite le procedure copy(testo) o blanks(quantità), ed hanno proprietà quali variab.length, variab.pos (pointer per accedere all’interno della stringa) e metodi o funzioni fra cui variab.setpos, variab.getchar, variab.putchar(char) e variab.sub(da, lungo).
Una innovazione di SIMULA fu il parallelismo di esecuzione delle procedure, in realtà uno pseudo parallelismo controllato dalle istruzioni detach e resume che trasformano una procedura in una coroutine, i cui punti di uscita (e il successivo rientro) sono detach e resume. Il programma sottostante è un gioco in cui i due giocatori cercano di occupare porzioni di un’area o di liberarle dall’avversario, in particolare le istruzioni players(n) :- NEW play(n); creano un’istanza della coroutine che viene eseguita fino all’istruzione detach permettendo l’inizializzazione della situazione del giocatore n-esimo, RESUME(players(1)); fa inizia il il gioco). .
!
cim-3.33-i586-pc-cygwin ; !
gioco occupa e distruggi ; begin text
digit,Area;
! Area
da occupare;
integer seed;
! per numeri random;
REF(play) ARRAY players(1:2);
CLASS
play(who); Integer who; begin character
Procedure convert(n); integer n;
begin
digit.SetPos(n+1);
convert := digit.GetChar;
! converte digit in carattere; end;
Procedure
HoldDestroy (n,flag,occupa);
integer
n,flag; boolean occupa;
comment
Occupa o distrugge postazione;
begin
Area.SetPos(n); ! posiziono sul byte n ;
if occupa and Area.Sub(n,1) = " " then
Area.PutChar(convert(flag))
else
begin
if not occupa then Area.PutChar(' ') end; end++of++HoldDestroy; begin
integer I,J;
Integer ARRAY player(1:100);
boolean
occupa;
! variabile occupare/distruggere;
! riempe Matrice con gli n numeri da 1 ad n in ordine casuale;
For I := 1 step 1 until 100 do
begin
J := RandINT(1,100,seed);
while player(J) <> 0 do J:= RandINt(1,100,seed);
player(J) := I;
end;
DETACH;
! serve ad inizializzare;
FOR I := 1 step 1 until 100 do
begin
occupa := Draw(1-I/100,seed);
HoldDestroy(player(I),who,occupa);
RESUME(players(MOD(who,2)+1)); ! tocca a te;
end; end;
end++of++class; begin
Integer I,J,N;
digit :- COPY("0123456789");
seed := clocktime;
Area
:- Blanks(100); !
Campo di battaglia ;
players(1) :- NEW play(1);
players(2) :- NEW play(2);
RESUME(players(1));
! fa ripartire il gioco;
outtext(Area); Outimage;
For I := 1 step 1 until Area.length do
begin
if Area.Sub(I,1) = "1" then J := J + 1;
if Area.Sub(I,1) = "2" then N := N + 1;
end; outtext("Giocatore
1: "); Outint(J,2); Outimage; outtext("Giocatore
2: "); Outint(N,2); Outimage; end;
end |
Figura
2-25
L’esecuzione del gioco è riportata in Figura 2-26 .
211 12
1 2
1 2
1 1
1 21 22 21
111 2 21 2 2
2 112 1 1
2 2 Giocatore 1: 18 Giocatore 2: 16 |
SIMULA 67 è ritenuto, riduttivamente, un linguaggio di simulazione; in realtà è un linguaggio generale che tramite delle funzioni native per generare numeri a caso, con diverse distribuzioni di probabilità, e le istruzioni detach e resume per gestire coroutines, permette di fare della simulazione; tuttavia la creazione di simulazioni diventa un’attività molto semplice ed elegante se si utilizzano le due classi predefinite SIMSET e SIMULATION. La prima fornisce proprietà e metodi per gestire delle liste, la seconda è una sottoclasse di SIMSET, che ha proprietà, metodi e sottoclassi che, senza svincolare del tutto la realizzazione di una simulazione dal come implementarla, permetteno di concentrarsi su cosa si vuol ottenere. SIMULATION gestisce la coda degli eventi ordinata su base temporale tramite l’utilizzo di detach e resume, la funzione time fornisce l’ora dell’evento.
Gli elementi che entrano in causa in una simulazione sono realizzati come classi che ereditano dalla classe process di SIMULATION una serie di attributi e metodi, in particolare i metodi activate per attivare un processo, hold(durata), per simulare una durata e rischedulare il processo al tempo opportuno.
Nel listato sottostante è riportata la simulazione
di uno sportello che eroga servizi, con attivazione e disattivazione di un
secondo sportello per gestire le punte di servizio).
.
!
cim-3.33-i586-pc-cygwin ; ! queue simulation; Simulation begin Integer
U;
! seed for random generators; REF(head)
Q;
! queue; Integer
LevelHigh = 20,LevelLow = 10; !
for second server; procedure
signal(What); NAME What; boolean What; begin
OutText(if What then "deactivate" else "activate");
OutText(" second server at "); outint(time, -6);
OutText("whit ");OutInt(Q.cardinal,3);OutText("
clients");
outimage;
What := not What;
end; process
class client; begin
REF(client) customer;
while true do begin
customer :- new client;
customer.into(Q);
HOLD(negexp(1/5, U)) ! negative exponential distribution;
end;
end;
process
class server; begin
while true do begin
if not Q.empty then begin
Q.first.out;
hold(uniform(1,10, U)); !
uniform distribution;
end;
hold(1);
end; end;
process
class wizard(); begin
boolean secondserver; !
boolean are initialized at false;
REF(server) secondsrv;
while TRUE do begin
if Q.cardinal > LevelHigh and then not secondserver then begin
secondsrv :- new server;
activate secondsrv;
signal(secondserver);
end;
if Q.cardinal < LevelLow and then secondserver then begin
cancel(secondsrv);
signal(secondserver);
end;
hold(1);
end; end;
Q
:- new head; outtext("Simulation
start..."); outimage; U
:= clocktime;
! for random seed; activate
new client; activate
new wizard; activate
new server; hold(2000);
! simulation running time; outtext("Simulation
end"); end |
Segue il risultato della simulazione.
C:\Simula\bin>coda Simulation start... activate second server at 225
whit 21 clients deactivate second server at 320
whit 9 clients activate second server at 448
whit 20 clients deactivate second server at 506
whit 9 clients activate second server at 1130
whit 20 clients deactivate second server at 1211
whit 9 clients activate second server at 1511
whit 20 clients deactivate second server at 1674
whit 9 clients activate second server at 1881
whit 20 clients deactivate second server at 1971
whit 9 clients Simulation end |
Figura 2 -27