Caricare documenti e articoli online 
INFtub.com è un sito progettato per cercare i documenti in vari tipi di file e il caricamento di articoli online.


 
Non ricordi la password?  ››  Iscriviti gratis
 

TAMC a.a. 1999-00 - Dispense di PASCAL

informatica



TAMC a.a. 1999-00

Dispense di PASCAL

dott. Antonella Carbonaro

0. Introduzione

Il Pascal è un linguaggio algoritmico per la programmazione sequenziale progettato nel 1968 da N. Wirth.

Carartteristiche:

· 939g62j   compatto con formato vincolato

· 939g62j   semantica degli statements ben specificata

· 939g62j   numero di statements limitato ma completo



· 939g62j   utile collezione di predefined data types

· 939g62j   meccanismo di subroutine generale


Il Pascal è un linguaggio compilato. Il processo di compilazione è composto da due fasi:

· 939g62j   l'analisi (lessicale, sintattica, semantica)

· 939g62j   la sintesi (linguaggio intermedio, ottimizzazione, linguaggio target).


La versione standard del Pascal: ISO, non è legata ad alcun tipo specifico di compilatore.


Il linguaggio è definito tramite il Vocabolario e la Grammatica.

La Grammatica è un insieme composto da:

· 939g62j   simboli terminali (parole che appartengono al vocabolario),

· 939g62j   simboli non terminali (metasimboli per giungere allo scopo),

· 939g62j   produzioni (elenco delle sostituzioni ammesse),

· 939g62j   scopo

1. Vocabolario

Vocabolario

def. L'INSIEME DEI CARATTERI DI BASE utilizzabili nel descrivere un programma Pascal.

Essi sono:

· 939g62j   Le 26 lettere dell'alfabeto inglese (maiuscole A..Z e minuscole a..z)

· 939g62j   le 10 cifre arabe (da 0 a 9)

· 939g62j   i caratteri speciali: + - * / = < > . , : ; '

· 939g62j   i caratteri blank, tab e quelli di fine linea

2. Lessico

Unità lessicale

def una sequenza finita dei caratteri dell'alfabeto


La Sintassi del Pascal ci dice quali SEQUENZE di unità lessicali sono riconosciute come frasi corrette.


Il linguaggio Pascal riconosce 4 tipi di unità lessicali:

identificatori

simboli speciali

numeri

stringhe

Identificatori

Sono nomi utilizzati per denotare diverse entità del linguaggio.

Sono identificatori corretti:

ciao CIAO ciAO4 c4iAo

mentre non lo sono

13maggio 66 alfa-beta c&ao $ciao £$


Nota Un identificatore può essere di una lunghezza qualsiasi di caratteri (comunque della lunghezza di una linea) TUTTAVIA solo i primi N sono considerati significativi.

Es. Se N = 8

variabile1 e variabile2 saranno considerate come lo stesso identificatore


Alcuni identificatori hanno in Pascal un significato preciso e congelato a prescindere dal quale non sono utilizzabili correttamente.

Essi sono le PAROLE CHIAVE o RISERVATE

and array begin case const div do downto

else end file for function goto if in

label mod nil not of or packed procedure

program record repeat set then to type until

var while with


Oss. Nelle carte sintattiche appaiono come simboli terminali

Sono soggette a regole sintattiche particolari (es. then deve seguire if..)


Inoltre vi sono anche gli IDENTIFICATORI STANDARD

Essi hanno un significato preciso ma non congelato (cioè possono essere ridefiniti)

e rappresentrano funzioni matematiche, valori logici, procedure standard, costanti, strutture di dati...

abs arctan boolean char chr cos dispose eof

eoln exp false get input integer ln maxint

new odd ord output pack page pred put

read readln real reset rewrite round sin sqr

sqrt succ text true trunc unpack write writeln


Simboli speciali

Sono caratteri speciali o coppie degli stessi interpretatie come singole unità.

Essi denotano operazioni o punteggiatura.


< > <= >= <> ^ :=

, ; : ' . ..

( ) (. .) (* *)


Nelle carte sintattiche sono ovviamente rappresentati come simboli terminali.

Numeri

Sono numeri corretti:

1 +1 -1 1.3 +1.3 -1.3 1.3E4 +1E-4 1.3E-4


Stringhe di caratteri

Una stringa è una qualsiasi sequenza di caratteri delimitata da '

Per scrivere il carattere ' basterà raddoppiarlo ''

sono stringhe corrette:

'12345' 'a' '£$%ç§' 'l''ape' '''' l''l


Nota:

· 939g62j   Il "blank" è ammesso all'interno di una stringa

· 939g62j   I caratteri di "fine linea" non sono permessi all'interno della stringa.

Segue che la lunghezza di una stringa non può superare la dimensione fisica di una linea.


3 Struttura di un programma Pascal

Un programma Pascal non è altro che la successione di identificatori, numeri, stringhe e caratteri speciali giustapposti secondo le regole che verranno via via specificate.


Supponiamo di voler descrivere un programma che trasferisce un testo riga per riga da un dispositivo di input (terminale) ad uno di output (stampante).

Seguendo l'approccio top-down

· 939g62j   ad un primo livello specifichiamo:

tre risorse su cui basare l'algoritmo risolutivo

descrizione della riga (dato su cui si opera)

descrizione di leggi

descrizione di scrivi

l'algoritmo risolutivo

ripeti fino alla fine del testo

leggi una riga (moduli

scrivi una riga

· 939g62j   ad un secondo livello siamo interessati a risolvere i sottoproblemi di lettura e scrittura e individuiamo:

le risorse

descrizione del carattere (dato su cui si opera)

descrizione di leggicarattere

l'algoritmo risolutivo

ripeti fino al carattere di fine-linea

leggi un carattere


Come si può notare la descrizione del modulo è identica ad ogni livello di dettaglio

e si compone di due parti

la specifica delle risorse utilizzate

l'elaborazione delle stesse.


Un programma Pascal avrà dunque la seguente sintassi:

programma=program header+blocco .


Dove:

· 939g62j   il program header specifica il nome del programma e i suoi rapporti con l'esterno tramite degli identificatori.

· 939g62j   il blocco invece include le specifiche delle risorse e dell'elaborazione delle stesse.



4 Descrizione delle risorse

Tutte le risorse utilizzate nella parte algoritmica di ogni modulo devono essere precedentemente specificate, ovvero dichiarate.


Ad ogni risorsa verrà associato un identificatore che la denoterà univocamente all'interno del programma e che sarà appunto utilizzato per riferirla nella parte algoritmica.


Le risorse possono essere di diversi tipi:

· 939g62j   etichette

· 939g62j   costanti

· 939g62j   tipi

· 939g62j   variabili

· 939g62j   procedure o funzioni

4.1 Dichiarazione delle etichette

Le etichette o label servono per individuare punti particolari del programma a cui ci si potrà riferire per fare un salto all'interno dello stesso.

4.2 Dichiarazione delle costanti

All'interno di un programma Pascal si possono identificatori come sinonimi di dati che non si modificano mai durante l'esecuzione del programma.

Due i vantaggi principali:

Migliore leggibilità del programma

Migliore manutenibilità: per modificare il valore, basterà modificare la sola dichiarazione anziché ogni sua occorrenza.


Il tipo della costante è determinato dal suo contenuto.


Sono dunque costanti corrette

const

max = +527; (integer)

pigreco =3.14; (real)

nome = 'elena'; (array of char)

min = - max; (integer)

alwaystrue = true; (boolean)

a = 'z'; (char)


4.3 Dichiarazione dei tipi

Il tipo dei dati definisce l'insieme dei valori e l'insieme delle operazioni che si possono effettuare su di essi.

Una variabile definita nel programma deve appartenere obbligatoriamente ad un solo tipo di dato.

I vantaggi sono:

· 939g62j   posso esprimere i dati ad un certo livello di ASTRAZIONE senza entrare nei dettagli della rappresentazione in memoria,

· 939g62j   la manipolazione è riferita alla STRUTTURA ASTRATTA e non alla ORGANIZZAZIONE FISICA,

· 939g62j   esiste protezione automatica da combinazioni errate di dati.


Definisco un TIPO DI DATO ASTRATTO attraverso :

· 939g62j   la struttura fisica cioè i VALORI PERMESSI (correlati con lo spazio fisico di memoria),

· 939g62j   gli accessi, cioè gli OPERATORI (procedure e funzioni) definite sulla struttura.


Il Pascal definisce diversi tipi di dati predefiniti e i costrutti che permettono al programmatore di definire i propri a seconda delle esigenze dettate dalla natura del problema.















BOOLEAN    ENUMERATIVI ARRAY

INTEGER   SUBRANGE RECORD

REAL SET

CHAR    FILE

POINTER


Sui tipi torneremo più avanti.


4.4 Dichiarazione delle variabili

Se la dichiarazione del tipo introduce un sinonimo per un modello di dato, la dichiarazione della variabile esplicita un istanziamento di tale modello.


La dichiarazione associa un nome simbolico ad una variabile di un certo tipo. Pascal è fortemente tipato: le variabili devono essere dichiarate prima di essere utilizzate.


Scopo di una dichiarazione:

· 939g62j   definire ed allocare lo spazio di memoria necessario,

· 939g62j   definire i valori ammessi,

· 939g62j   definire gli operatori.


var

max : integer;

flag : boolean;

radice1, radice2 : real;


4.5 Dichiarazione di procedure e funzioni

Procedure e funzioni rappresentano in pascal dei veri e propri sottoprogrammi.

Nel pascal standard esse devono fare parte integrante del programma globale e costituire quindi una parte di un'unica unità di compilazione. Esistono tuttavia altri linguaggi che accettano una compilazione separata con la possibilità quindi di costruire librerie di sotto-programmi.


Distinguiamo tra procedure e funzioni perché le seconde restituiscono esse stesse un valore assegnabile ad esempio ad una variabile.


5 I tipi

Tipo di dato

def. classe dei valori e insieme delle operazioni permesse su tali elementi.


In Pascal ad ogni variabile e costante, nonche funzione, deve essere associato un tipo di dato.

Tale associazione è STATICA ed è effettuata al momento della dichiarazione o definizione dell'oggetto.

Ad esempio:

const

max=120;

var sono oggetti di tipo integer

i : integer;


5.1 Tipi non strutturati

Tipo non strutturato

def. E' un tipo caratterizzato da un insieme di valori elementari che rappresentano atomi che non possono essere ulteriormente scomponibili.


tipo semplice: tipo enumerativo o tipo subrange o tipo predefinito


Tutto il calcolo esprimibile in Pascal si basa su questi valori atomici.



5.1.1 Tipo enumerativo

Per definire un tipo enumerativo basta elencarne (enumerarne) tutti i possibili valori, ovvero basta elencare tutti gli identificatori con cui sono denotati i valori del tipo, che, in pratica, rappresentano le costanti letterali che caratterizzano il tipo stesso.

Una variabile dichiarata di quel tipo potrà assumere solo tali valori.


Seguono alcuni esempi di tipi enumerativi:


type

colore = (rosso,arancio,giallo,verde,azzurro,indaco,violetto);

frutta = (mela,pesca,arancio,limone);

var

moneta: (dollaro,marco,sterlina,franco,yen,lira);

giorno: (lun,mar,mer,gio,ven,sab,dom);

portata:(antipasto,primo, secondo, contorno, dolce, frutta);


Nota: 

Un identificatore di un valore scalare definito dall'utente può comparire nella definizione di un solo type.

L'ordine con cui compaiono nella enumerazione gli identificatori definisce l'ordinamento dei valori denotati da quei nomi.

Ciò permette di

· 939g62j   confrontare oggetti dei tipi enumerativi con operatori relazionali

lun < mer

rosso < violetto

· 939g62j   applicare le funzioni pred e succ che individuano il predecessore e il successore di un valore nella sequenza data nella definizione del tipo enumerativo

pred(mer) = mar

succ(gio)= ven

pred(lun) = undefined !

· 939g62j   ottenere la posizione del valore considerando numerato con zero il primo posto tramite la funzione ord

ord(lun)=0

ord(gio)=3



5.1.2 Tipo subrange

Valori: sottoinsieme dei valori di un ORDINAL TYPE (integer, boolean, char, enumerativi).

E' definito indicando limiti inferiore e superiore.


Deve valere la seguente relazione

limite inferiore <= limite superiore

e le costanti devono essere dello stesso tipo.


Sono pertanto corrette definizioni del tipo

type

feriale = lun..ven;

festivo = sab..dom;

indice = 0..63;

lettera = 'A'..'Z';

mentre non lo è

type

my_week= dom..mer;

perchè dom > mer.


C'è validazione automatica nelle istruzioni, cioè i risultati devono appartenere al tipo scalare associato.


5.1.3 Tipi predefiniti

il Pascal supporta 4 tipi predefiniti denotati da altrettanti identificatori standard i cui valori sono insiti nel linguaggio stesso e non possono essere ridefiniti dall'utente.


a)    tipo integer

Rappresenta l'insieme dei numeri interi rappresentabili nella word di memoria della macchina.

Quindi possiamo definire questo tipo standard come:

type

integer = -maxint-1..maxint

dove maxint

è un identificatore standard che denota il più grande numero intero rappresentabile sulla

macchina che dipende dalla lunghezza (numero di bit) della word e dalla notazione adottata.


Ogni operazione che porta fuori dal range -maxint-1..maxint produce un OVERFLOW error.

Tutti i tipi di dati che hanno una natura enumerabile possono essere rappresentati come integer.

const

dozzina = 12;

type

lunghezza = integer;

ora = 0..24;

var

perimetro : integer;

contatore: integer;


Questo tipo è caratterizzato da operazioni specifiche:

somma (operatore diadico) e segno (operatore monadico)

- sottrazione(oper. diadico) e segno(operatore monadico)

moltiplicazione

esponenziazione (non ISO)

div divisione intera

mod modulo (resto della divisione intera)

abs valore assoluto

pred predecessore

succ successore

con risultato integer;

divisione

arctan arcotangente

cos coseno

exp esponenziale

ln logaritmo naturale

sin seno

sqr quadrato

sqrt radice quadrata

con risultato reale;

odd dispari

= <> < > <= >=

con risultato booleano.


b)   tipo real

Rappresenta l'insieme dei numeri reali rappresentabili nella word di memoria della macchina.

In questo caso la limitazione fisica della lunghezza della parola di memoria della macchina introduce limiti

- sul range dei valori permessi

- sulla precisione degli stessi, con la conseguenza di rendere discreta la natura continua dei numeri reali


Rappresentazione Floating Point normalizzata, PRECISIONE MACCHINA e NUMERI MACCHINA.

Errori di OVERFLOW ed errori di UNDERFLOW.


Alcuni esempi di definizione con il tipo real sono.

const

g = 9.8;

type

pressione = real;

var

ascissa,ordinata : real;


Non essendo di utilità pratica che il tipo real sia riconosciuto come enumerativo, non si possono definire subrange reali.


Questo tipo è caratterizzato dalle seguenti operazioni specifiche:

trunc troncamento

round arrotondamento

con risultato integer;

somma (operatore diadico) e segno (operatore monadico)

- sottrazione(oper. diadico) e segno(operatore monadico)

moltiplicazione

esponenziazione (non ISO)

abs valore assoluto

divisione

arctan arcotangente

cos coseno

exp esponenziale

ln logaritmo naturale

sin seno

sqr quadrato

sqrt radice quadrata

con risultato reale;

< > = <> <= >=

con risultato booleano.


c)    Tipo boolean

La definizione del tipo boolean è la seguente:

type

boolean = (false,true);

I due valori di verità false e true sono identificatori standard.

Per essi vale la convenzione di ordinamento per cui

false<true


Anche per i tipi boolean sono definiti in Pascal operatori logici derivanti dall'algebra booleana; essi sono:

and congiunzione

or disgiunzione

not negazione

= <> < > >= <=


Alcuni esempi di definizione con il tipo real sono:

const

vero = true;

falso = false;

type

bit = boolean;

var

flag,errore : boolean;


d)   Tipo char

Il tipo predefinito char è dato dall'insieme finito ed ordinato dei caratteri utilizzabili dalla macchina. L'ordine è dipendente dalla codifica (ASCII, EBCDIC, ...)

Tutti i caratteri grafici sono presenti in questo insieme.


Alcuni esempi di definizioni di tipi char sono:

const

blank = ' ';

quote = '''';

type

lettera = char;

var

simbolo:char;

dove si è utilizzata la carta sintattica delle stringhe di caratteri.


Normalmente in tutte le macchine è vero che:

'a' < 'b' < .... < 'z'

'A' < 'B' < .... < 'Z'

'0'< '1' < .... < '9'

in tal caso la natura enumerativa del tipo permette di definire tipi subrange come:

type

maiuscole = 'A' .. 'Z';

var

cifra: '0'..'9';


Note

· 939g62j   Non è ovviamente consentito definire una nuova enumerazione costituita da caratteri come la seguente:

type

vocali ('a','e','i','o','u');


· 939g62j   Ricordando che la funzione ord fornisce in genere la codifica interna del simolo che i compilatori pascal fanno corrispondere alla posizione nella definizione, segue che ord('a') ci fornisce la codifica interna del carattere 'a'.


· 939g62j   La funzione chr applicata ad un intero da 0 a 255 ritorna il carattere con la rappresentazione uguale all'argomento.


5.2 I tipi strutturati

Il Pascal mette a disposizione i seguenti costruttori di tipo:

a)    array

b)   record

c)    set

d)   file


a)   Il tipo array

L'array

def. è una struttura dati omogenea costituita da un numero predefinito di componenti dello stesso tipo detto tipo base ai quali si accede casualmente mediante uno o più indici. Possono essere a 1, 2, o più dimensioni.


Tale struttura rappresenta bene vettori, matrici, tabelle.


Per gli indici si può adottare un qualsiasi ordinal type (dunque non real).

Ad esempio:

type

chefai = array of (mangi,dormi,lavori,tidiverti)

è un vettore di lunghezza 24

type

giorno = (LUN,MAR,MER,GIO,VEN,SAB,DOM)

è un vettore di lunghezza 7

type

chefaiinsettimana = array giorno of chefai

è una matrice 7 * 24


Dato un array, un suo elemento è individuato dal nome dell'array seguito dal valore dell'indice tra parentesi quadre.

La sintassi specifica anche i riferimenti per record e puntatori, che vedremo in seguito.


Ora, supponendo di avere le variabili:

var

x: chefai;

y: chefainsettimana;

i: 0..23;

j: giorno;


potremo scrivere:

x per accedere all'elemento corrispondente all'ora tra le 15 e le 16

x i+5 per accedere all'elemento corrispondente all'ora individuata da i+5

y mer per accedere all'array di tipo "che fai" corrispondente alla giornata di mercoledì

y succ(j) per accedere all'array di tipo "che fai" corrispondente alla giornata successiva

a quella contenuta nella variabile j

y lun per accedere all'elemento corrispondente all'ora tra le 9 e le 10 della giornata

di lunedì della settimana identificata dalla variabile y


Esempi:

· 939g62j   prodotto scalare fra due vettori

· 939g62j   somma di due matrici


b)   il tipo record

def. struttura dati che permette di aggregare sotto lo stesso nome una sequenza di componenti di tipi arbitrari (che potrebbero a loro volta essere strutturati).


Il concetto è simile a quello di N_PLA, cioè un elemento del prodotto cartesiano dei tipi costituenti.


La sintassi della definizione di un record è la seguente, ove, per ora non esplicitiamo la parte variante.


Così ad esempio si può definire:

type

data = record

giorno: 1..31;

mese: 1..12;

anno: 0..2000

end;

var

scadenza : data;




L'accesso ad una componente del record avviene giustapponendo al nome della variabile il nome del campo preceduto da un '.'

scadenza.mese identifica la componente mese della variabile scadenza

Il record ci permette di definire strutture estremamente articolate, ad esempio:

type

nome = array of char;

anno = 1500..2000;

libro = record

autori: array of nome;

titolo: array of char;

editore: nome;

dtastampa,dataacquisto:anno;

collocazione:record

tipo:(romanzo,saggio,manuale);

scaffale:'A'..'Z';

posizione:1..100

end

end;

biblioteca= array of libro;


Da cui avendo a disposizione una variabile

var miabibl:biblioteca;

si può accedere ad una qualsiasi singola informazione,


ad esempio con miabibl .autori

si accede all'iniziale del primo autore del quarto libro della biblioteca.


I record possono avere anche una parte variante di tipo non completamente noto a priori e più precisamente che può essere scelto in un insieme definito di tipi.


Questo ci permette di definire record che a seconda della costante assunta dall'identificatore che segue il case presenta dei campi diversi.


Se ad esempio dobbiamo definire un tipo fattura che deve presentare campi diversi a seconda che il cliente sia normale, esente da iva, o estero possiamo utilizzare la seguente struttura record:

type

fattura = record

data:record

gg:1..31;

mm:1..12;

aa:00..99

end;

numerofattura:integer;

cliente:record

nome,città:array of char;

indirizzo:array of char;

cap:integer;

partitaiva:integer

end;

case tipocliente:(normale,esente,estero)of

normale:(imponibile:integer;

iva:integer);

esente: (causale: array of char);

estero: (tipovaluta:(lit,usd,dm,lst,ff,fs);

valore unitario:integer;

imponibileinvaluta:integer;

imponibileinlire:integer;

iva:integer);

totale:integer

end;


Si noti che defininendo una variabile di tipo record con varianti viene allocata IN MANIERA STATICA tanta memoria quanta ne servirebbe per la variante più grande tra quelle possibili, le altre utilizzano infatti comunque una sottoparte della stessa. Questo può comportare dei problemi.

Il record può essere utilizzato per costruire strutture più complesse, come ad esempio array di record oppure record di array.


c)    il tipo set

def. definisce un insieme di elementi di tipo scalare (real escluso).


Gli operatori disponibili sul tipo set sono, dati a e b insiemi ed x elemento di a:

a + b unione di insiemi

a * b intersezione di insiemi

a >= b espr. booleana "a contiene b"

a <= b espr. booleana "a sottoinsieme di b"

x in a espr. booleana "x appartiene ad a"

Si può considerare anche l'insieme vuoto '


Esempio:

var

minuscole,maiuscole,cifre:set of char;



begin


maiuscole:= 'A'..'Z'

minuscole:= 'a'..'z'

cifre:=

vuoto:=

read(ch);

if ch in maiuscole then


end


d)   il tipo file

def. sequenza di valori dello stesso tipo, accesssibili in ordine rigorosamente sequenziale.

in genere è correlata alla presenza di memorie di massa per registrare grandi q.tà di dati.


I file verranno apprpfonditi più avanti (paragrafo 12).



6 La specifica dell'algoritmo

La specifica dell'algoritmo rappresenta la parte algoritmica di un blocco ed è descritta da uno statement composto = sequenza di statement racchiusa da due parole riservate di inizio e di fine.


Ogni statement separato dal successivo da un separatore: il simbolo speciale ; (non occorre prima di un end), può essere: una qualsiasi delle frasi eseguibili del linguaggio ovvero

semplici statement operativi : assegnamento,richiamo a procedura

strutture di controllo: if, case, while, repeat, for with goto.

una sequenza di statement: statement composto

uno statement vuoto.


7 Espressioni e assegnamenti

Le istruzioni di assegnamento permettono di:

· 939g62j   calcolare il valore di un'espressione

· 939g62j   assegnare tale valore ad una variabile.


Nota

Si è usato il termine generale di variabile e non di identificatore per non considerare le sole variabili di tipo semplice, ma anche variabili di tipi strutturati che vedremo in seguito.


La sintassi delle espressioni è la seguente:


Nota I richiami di funzione non sono altro che espressioni del tipo:

sqrt(delta)

che, accettando uno o più argomenti (nel nostro esempio il delta) restituisce un risultato.

Tali funzioni possono essere pre-definite e disponibili con il pascal standard

o definite dall'utente all'interno del programma che lo intende utilizzare.


Segue una tabella riassunto di tutti gli operatori descritti e delle funzioni standard

OPERATORE

SIGNIFICATO

TIPO OPERANDI

TIPO RISULTATO


somma o segno

unione

integer o real

set

integer o real

set


sottrazione o segno

integer o real

integer o real


moltiplicazione

intersezione

integer o real

set

integer o real

set


divisione

integer o real

real

div

divisione intera

integer

integer

mod

modulo

integer

integer

not

negazione

boolean

boolean

and

congiunzione

boolean

boolean

or

disgiunzione

boolean

boolean


uguale

tipi semplici uguali

pointers, insiemi

boolean

<>

diverso

tipi semplici uguali

pointers, insiemi

boolean

<

minore

tipi semplici uguali

boolean

>

maggiore

tipi semplici uguali

boolean

<=

minore uguale

sottoinsieme

tipi semplici uguali

tipi set

boolean

boolean

>=

maggiore uguale

contiene

tipi semplici uguali

tipi set

boolean

boolean

in

appartiene

elemento e set

boolean

abs

valore assoluto

integer o real

stesso tipo argomento

arctan

arcotangente

integer o real

stesso tipo argomento

chr

carattere


char

cos

coseno

integer o real

real

eoln

fine della linea

file

boolean

eof

fine del file

file

boolean

exp

esponenziale

integer o real

real

ln

log naturale

integer o real

real

odd

dispari

integer

boolean

ord

ordinale

scalare

integer

pred

predecessore

scalare

stesso tipo argomento

round

arrotondamento

real

integer

sin

seno

integer o real

real

sqr

quadrato

integer o real

stesso tipo argomento

sqrt

radice quadrata

integer o real

real

succ

successore

scalare

stesso tipo argomento

trunc

troncamento

real

integer


Facciamo un esempio per mostrare alcune diverse assegnazioni per inizializzare delle variabili:

type

sesso = (maschio, femmina);

index = 1..5;

data = record

gg: 1..31;

mm:1..12;

aa: 0..2000

end;

var

s: sesso;

i: index;

a,b: array index of integer;

c,d:data;

r,s: set of char;

begin


s:= maschio;

i:=1;

a i :=6; a:=b;

d.gg := 9; c:=d,

s:= 'a','e','i'


end


8 Strutture di controllo

def. Strutture di controllo

I modelli di composizione per la descrizione di algoritmi


Esse sono di tre tipi:

sequenza

selezione

iterazione


8.1 sequenza

In Pascal la sequenza è espressa come statement composto, che abbiamo già descritto precedentemente, in cui gli statement sono eseguiti sequenzialmente nell'ordine testuale.

Ogni statement è concatenato al successivo dal simbolo terminale ";".


8.2 Selezione

Il Pascal mette a disposizione dell'utente selezioni singola e multipla.


8.2.3 Selezione singola: statement if

Lo statement if permette:

· 939g62j   l'esecuzione condizionale di uno statement

· 939g62j   l'esecuzione selettiva tra due statement in dipendenza della valutazione di una condizione.


La sintassi prevede dunque due diverse "forme":

if <espressione booleana> then

<statement>


if <espressione booleana> then

<statement>

else <statement>

Dovendo calcolare un'equazione di secondo grado avremo qualcosa del tipo

program equaz (input,output);

var

a,b,c,delta,x1,x2:real;

begin

.... (* lettura variabili a,b,c *)

if a = 0 then

x1 := - c/b;

else

begin

delta := (b*b - 4*a*c);

if delta <0 then

... (*messaggio di errore*)

else

if delta = 0 then

x1, x2 := - b/2*a;

else

begin

x1:= (- b - sqrt(delta)/2*a);

x2:= (- b + sqrt(delta)/2*a)

end

end

end


Gli statement nei rami dell'if possono essere:

· 939g62j   statement composti

· 939g62j   o altri if

· 939g62j   o altri generici statement.


Focalizzando l'attenzione sugli if nidificati

if cond1 then

statement 1

else if cond2 then

statement2

else if cond3 then

statement3


else

statementn

può non essere chiaro a quale if si riferisce l'else.


Il Pascal adotta la convenzione secondo cui

l'else si riferisce sempre all'if più vicino (più interno nella nidificazione)


Se dunque ad esempio abbiamo lo statement

if a>0 then if a < 50 then a:= 0 else a:=100;

esso è equivalente a

if a>0 then

begin

if a < 50 then

a:= 0

else a:=100

end;

se vogliamo che l'else sia riferito al primo if dobbiamo scrivere:

if a>0 then

begin

if a < 50 then

a:= 0

end

else a:=100;

8.2.2 Selezione multipla: statement case

Lo statement case specifica la selezione di un ramo tra n possibili in base al valore di una determinata espressione.


L'espressione che segue la parola chiave case è detta "selettore" e può essere di un qualsiasi tipo scalare.

Si ha:

· 939g62j   Ogni ramo risulta etichettato da uno o più valori costanti dello stesso tipo el selettore.

· 939g62j   Ovviamente ogni etichetta non può apparire in più di un ramo.

· 939g62j   L'ordine di scrittura dei rami nel testo è irrilevante.


Esempio di utilizzo del case:

program mese (input,output);

var numMese:1..12;

anno:0..2000;

numGiorni:28..31;

begin

.... (* lettura variabili numMese e anno*)



case numMese of

1,3,5,7,8,10,12:

numGiorni:=31;

2:

if (anno mod 4 = 0) then

numGiorni:=29

else

numGiorni:=28;

4,6,9,11:

numGiorni:=30

end;

....

end.


Nota:

E' banalmente possibile rendere lo statement case con degli statement if.

Allo stesso modo lo statement if si può rendere con il case:

if <condizione> then

<statement1>

else

<statement2>

si può scrivere:

case <condizione> of

true: <statement1>

false: <statement2>

end

8.3 Iterazione

Spesso può essere necessario avere la possibilità di poter ripetere le azioni specificate in un determinato algoritmo.

Il Pascal fornisce tre strutture di controllo iterative:

· 939g62j   due orientate all'iterazione guidata da una condizione, (statement while e repeat)

· 939g62j   una all'iterazione guidata da un contatore. (statement for)

8.3.1 statement while

Lo statement while consente di iterare delle operazioni FINTANTOCHE' una condizione risulta verificata.


L'espressione booleana è valutata prima di ogni iterazione.

Se il valore è TRUE , lo statement viene eseguito, mentre se è FALSE lo statement non è eseguito e si esce dall'iterazione.


Supponiamo di voler scrivere un programma per calcolare n!

program nfatt(input,output);

type naturale = 1..maxint;

var contatore,n,risultato: naturale;

begin

.... (* lettura variabili n *)

contatore,risultato:=1;

while contatore <=n do

begin

risultato:=risultato*contatore;

contatore:=contatore+1

end;

.....

end.


8.3.1 statement repeat

Lo statement repeat consente di iterare delle operazioni FINO A CHE una condizionenon diventa vera.


L'espressione booleana è valutata dopo ogni iterazione.

Se il valore è FALSE , la sequenza di statement viene nuovamente eseguita, mentre se è TRUE si esce dall'iterazione.


Supponiamo di voler di nuovo scrivere un programma per calcolare n!, utilizzando però lo statement repeat:


program nfatt(input,output);

type naturale = 1..maxint;

var contatore,n,risultato: naturale;

begin

.... (* lettura variabili n *)

contatore,risultato:=1;

if contatore <= n then (*superfluo perché n è naturale*)

repeat

risultato:=risultato*contatore;

contatore:=contatore+1

until contatore >n;

.....

end.


Nota

E' ovviamente possibile rendere lo statement repeat con lo statement while:

repeat <statement>

<statement> = = while not <condizione> do

until<condizione> <statement>

e analogamente si può rendere il while con il repeat e l'if:

while <condizione> do if <condizione> then

<statement> = = repeat <statement>

until not <condizione>


8.3.1 statement for

Lo statement for specifica che uno statement deve essere ripetuto per valori consecutivi di una certa variabile.

Questa struttura di controllo può essere utilizzata per modellare i casi frequenti in cui il numero delle ripetizioni è conosciuto in anticipo.


Si ha:

· 939g62j   L'identificatore denota la variabile di controllo, che deve essere stata dichiarata di un qualsiaisi tipo scalare.

· 939g62j   Entrambe le espressioni devono essere dello stesso tipo della variabile: esse sono valutate una sola volta e i loro valori costituiscono gli estremi di una successione di valori assunti dalla variabile di controllo.

· 939g62j   Lo statement è eseguito una volta per ognuno dei valori di tale successione crescente o decrescente a seeconda se è stata specificata la forma con la parola chiave to o downto.

Le fasi di inizializzazione, controllo ed incremento della variabile di controllo sono eseguite automaticamente dalla struttura for.


Supponiamo di voler riscrivere il programma per calcolare n! utilizzando lo statement for

program nfatt(input,output);

type

naturale = 1..maxint;

var contatore: integer;

n,risultato: naturale;

begin

.... (* lettura della variabile n *)

risultato := 1;

for contatore := 1 to n do

risultato:=risultato*contatore;

.....

end.


Nota: Per il corretto funzionamento della struttura for è necessario che:

· 939g62j   gli estremi non vengano alterati

· 939g62j   il valore della variabile di controllo al termine dell'iterazione venga considerato


Altri esempi di uso del for: 


var g: (lun,mar,mer,gio,ven,sab,dom);

c:char;


for g:= lun to ven do....

for g:= dom downto lun do....

for g:= 'a' to 'z' do....

for g:= '9' downto '0' do....



Questi esempi mostrano come

i valori della successione siano quelli calcolati dalle funzioni pred e succ


Si ha che

se, nel caso di successione crescente, l'espressione iniziale è > dell'espressione finale

for g:= dom yo lun do...

o se, nel caso di successione decrescente, l'espressione iniziale è < dell'espressione finale,

for c:= 'g' downto 'w' do...

non viene attivata alcuna iterazione!!!


8.4 Altre strutture di controllo

8.4.1 lo statement with

La ricca strutturazione dei dati Pascal impone l'uso di identificatori strutturati molto lunghi per l'accesso alle singole componenti.

Lo statement with permette di utilizzare nomi abbreviati per i record.


In tutta la porzione di codice controllata dalla struttura, i records acceduti possono essere referenziati con la sola parte non specificata nella struttura with stessa.

Supponiamo di utilizzare i record della struttura libro già specificata in precedenza:

type

persona = record

nome: array of char;

cognome: array of char;

luogonascita: array of char;

datanascita:record

gg:1..31;

mm:1..12;

aa:0..2000

end

end;

var cliente: array of persona;

begin


with cliente .datanascita do

begin

gg := 13;

mm := 7;

aa := 1970

end;


end


8.4.1 lo statement goto

L'istruzione goto permette di effettuare un salto da una parte di programma a un'altra, identificata con un'etichetta adeguata.


L'uso di tale istruzione in Pascal è sconsigliato perchè ad esempio comporta un certo insieme di istruzioni di deallocazione di memoria, per liberare lo stack delle variabili locali della procedura o delle procedure abbandonate, con conseguente consumo del tempo di esecuzione, con conseguenze di inefficienza.


In generale, poi istruzioni di salto in avanti possono essere rese con delle strutture di selezione e istruzioni di salto all'indietro possono essere rese con delle strutture di iterazione, senza così uscire dai principi della programmazione strutturata.


Tuttavia, a fronte di situazioni anomale può rivelarsi utile saltare porzioni significative di programma per effettuare operazioni di recupero, per segnalare errori catastrofici intrerrompendo la normale esecuzione del programma per evitare l'inserimento di condizioni indesiderate nel modello dell'algoritmo.


La sintassi è la seguente:


L'istruzione goto ha l'effetto di trasferire il controllo all'istruzione denotata dall'etichetta che segue la parola goto, la quale deve essere stata precedentemente dichiarata all'interno del modulo in cui compare.


Volendo fare un esempio:


program esempio

label 1;

...

begin

....

if tragicoerrore then

begin

<invia msg. errore>;

goto 1

end;

....

1: <invia msg. esecuzione terminata>

end



9 Input ed output standard

Una delle caratteristiche dei calcolatori è quella di poter "comunicare" con il mondo esterno:

· 939g62j   per ricevere dati da elaborare,

· 939g62j   trasmettere i risultati dell'elaborazione.


In Pascal, nel program header vengono specificati due identificatori standard: INPUT ed OUTPUT, che denotano i dispositivi di ingresso e di uscita standard: la tastiera ed il video.


Un generico programma Pascal che include comunicazione con il mondo esterno può così essere descritto:

program parla (input, output);

<blocco>


9.1 Input standard

Il Pascal mette a disposizione dell'utente le procedure predefinite

· 939g62j   read (par)che permette di assegnare alla variabile specificata come parametro il valore letto

dall'input standard

· 939g62j   readln(par) che permette di assegnare alla variabile specificata come parametro il valore letto dall'input standard e si posiziona su una nuova linea, ignorando i caratteri che

seguono quello letto e precedono il carattere di fine linea .


I valori dei dati in ingresso sono semplici sequenze di caratteri grafici.

Il linguaggio esegue in lettura una CONVERSIONE AUTOMATICA di tali valori nella corrispondente rappresentazione interna del programma (vedi una determinata codifica binaria) in base al tipo della variabile da leggere. Tale variabile può essere di tipo: char, integer, real o subrange dei primi due.


Una forma notazionale abbreviata consente di esplicitare un numero qualsiasi di parametri nella procedure read o readln:

read (a,b,c);

readln (a,b,c);

dove lo spazio tra un dato in ingresso e l'altro è riconosciuto come separatore .


Anche il carattere di fine linea viene considerato come un separatore indipendentemente dalla sua rappresentazione adottata dal sistema.

Il Pascal fornisce una funzione predefinita booleana eoln che ritorna:

· 939g62j   true quando si incontra il carattere di fine linea

· 939g62j   false altrimenti.

La chiamata a procedura read(a) dopo avere incontrato il carattere di fine linea causa l'avanzamento alla prossima riga.

Già di per sé invece readln(a) epermette di saltare alla riga successiva senza considerare tutti i caratteri rimanenti della riga attuale.


Supponendo di avere queste tre linee successive di dati:

125 250 500

10 100 1000

13.57 23.2 3.14

esse sono correttamente lette dal seguente programma:

program leggi (input,output);

var i,j,k,l:integer;

r:real;

begin

read(i) (* i=125 *)

readln(j,k,l); (* j=250 k=500 l=10 *)

readln(r) (* r=13.57 *)

end.


Potrebbe essere utile leggere dati finchè ve ne sono di disponibili.

In questo caso il Pascal mette a disposizione la funzione booleana predefinita eof che ritorna:

· 939g62j   true se la fine dei caratteri in ingresso è incontrata,

· 939g62j   false altrimenti.

Se si proverà a leggere un dato una volta incontrata la fine dei dati si incorrerà in un errore.

9.2 Output standard

Il problema è in questo caso inverso rispetto al precedente: i valori dati nella rappresentazione interna del programma (binaria) devono essere convertiti nelle equivalenti forme simboliche riconoscibili dall'utente.


Il Pascal mette a disposizione dell'utente le procedure predefinite

· 939g62j   write (par)che permette di scrivere nel canale di output standard una sequenza di caratteri

corrispondente alla forma simbolica del dell'espressione specificata come

parametro

· 939g62j   writeln(par)che permette di scrivere nel canale di output standard una sequenza di caratteri

corrispondente alla forma simbolica del valore dell'espressione specificata come parametro aggiungendo il carattere di fine linea (si posiziona nella linea seguente).


Il tipo dell'espressione può essere uno qualsiasi dei tipi predefiniti, compresi subrange di char, integer, costanti stringhe.

La forma simbolica in uscita è la stessa delle costanti riconosciute dal linguaggio per quei tipi, eccetto per le stringhe di caratteri dove i terminatori non sono riportati in scrittura.


Il linguaggio assume delle lunghezze standard dei campi in cui scrivere i valori, allineati alla destra. Per avere un miglior controllo sulla sequenza in uscita, l'utente può usare delle specifiche di formato. In particolare è possibile specificare il numero complessivo di caratteri in cui scrivere il dato. La forma sintattica:

write(e:n);

include una specifica di formato indicante la scrittura di n caratteri contenenti la rappresentazione simbolica dell'espressione e, allineato a destra.


N.B. Se n non è sufficiente l'indicazione di formato è tralasciata e i caratteri necessari sono

comunque scritti.


Per il caso reale è prevista una specifica di formato aggiuntiva:

write(e:n:m);

specifica la scrittura di n caratteri contenenti la rappresentazione simbolica della notazione senza esponente del valore dell'espressione reale e, allineato a destra, di cui m caratteri sono usati per la parte decimale

Anche in questo caso se m non è sufficiente l'indicazione di formato è tralasciata.

Esempio:

write(215.87:8:4) produce la sequenza in uscita 2.1587



Analogamente alla lettura una forma notazionale abbreviata consente di scrivere un numero qualsiasi di parametri nelle procedure write o writeln.

Esempio:

write (a,b,c); scrive i valori delle variabili a, b e c

writeln ('a','b','c'); scrive i caratteri a, b e c e va a capo


Un'ulteriore sovrastruttura presente di solito nel testo in uscita prodotto da un programma è data dalla procedura predefinita page che termina l'attuale pagina del testo in uscita e ne inizia una nuova.

La sequenza

write(i); scrive il valore di i

page; cambia pagina

writeln; salta una riga

write(j); scrive il valore di j


Esempio di programma per indovinare un numero da 0 a 999 generato pseudocasualmente in base ad un numero generatore:

program gioco (input,output);

var

numero,tentativo:0..999;

numTentativi,generatore:integer;

indovinato: boolean;

begin

write ('Insere il numero di generatore da 0 a 999: ');

readln(generatore),

numero := trunc(1000*random(generatore))

numTentativi:=0;

indovinato:= false;

repeat

write ('Insere un numero da da 0 a 999: ');

readln(tentativo),

numTentativi:=numTentativi + 1;

if tentativo > numero then

writeln('Numero troppo alto')

else

if tentativo < numero then

writeln('Numero troppo basso')

else

indovinato:= true;

until indovinato

writeln ('Hai indovinato in ',numTentativi:1, 'mosse');

end. ** ** *FINO QUI ** ** ********


10 Procedure e funzioni

La costruzione di programmi complessi si attua attraverso la tecnica dei raffinamenti successivi (o sviluppo top-down) che consiste nel

decomporre il problema da risolvere in sotto problemi più semplici

costruire un programma che risolva il problema generale supponendo che i singoli problemi siano già risolti

affrontare con la stessa tecnica i sottoproblemi sino ad arrivare a problemi direttamente risolvibili.


In Pascal questo procedimento si realizza grazie all'uso di risorse quali le procedure e le funzioni.

Esse devono, almeno in Pascal standard, fare parte integrante del programma principale (essere dichiarate e sviluppate all'interno di esso).

Esistono tuttavia molti compilatori che permettono di fare riferimento a librerie di sottoprogrammi esterne al programma contenente il suddetto riferimento.


Ogni sottoprogramma ha la stessa struttura modulare del programma principale: descrizione delle risorse e specifica dell'algoritmo.

Inoltre comunica con l'esterno attraverso:

· 939g62j   normali operazioni di I/O,

· 939g62j   accesso a variabili del programma "chiamante",

· 939g62j   parametri.


In particolare per ogni chiamata a sottoprogramma viene effettuato un cambio di contesto, secondo il seguente modello.

Al programma principale viene associata una precisa area di memoria organizzata a stack (ovvero può essere visitata una variabile alla volta partendo dall'ultima inserita.

In fase di esecuzione come prima cosa vengono allocate in tale area tutte le variabili richieste dal programma principale.

Ogni volta che viene chiamato un sottoprogramma si ha l'allocazione nello stack delle sue variabili "locali" (ovvero che hanno senso e sono definite sono all'interno dello stesso) via via sopra quelle del modulo chiamante.

Una volta terminata l'esecuzione del sottoprogramma le sue variabili locali vengono deallocate, e così via da modulo chiamato a modulo chiamante sino a quando una volta terminato anche il programma principale la memoria ad esso associata viene definitivamente liberata.



Vediamo di specificare questo concetto con un breve esempio considerando un programma che calcola la media tra 100 numeri disposti su un array utilizzando apposite procedure di inserimento dei dati e di somma.


program media (input, output);

type

a=array of real;

var

tab: a;

n : 1..100;

m,s: real;

procedure leggitab;

var

x:real;

i:1.100;

begin

writeln('Inserisci 100 numeri reali');

for i := 1 to 100 do

begin

readln(x);

tab i := x

end

end;

procedure sommatab;

var

i:1..100;

begin

for i := 1 to 100 do

s:=s+tab i

end;

begin

write('Media in n valori di 100 numeri');

write('Inserisci n: ');

readln(n);

leggitab;

sommatab;

m:= s/n;

write('La media in n valori dei numeri

da te inseriti è: ',m)

end.




Nota

Se c'è un caso di 'omonimia' (due variabili con lo stesso nome),

si fa riferimento all'ultima variabile con quel nome inserita.

Situazione dello stack all'esecuzione

delle istruzioni:


begin

tab


n

m

s




leggitab;

tab


n

m

s

x

i




sommatab;

tab


n

m

s

i





10.1 Le procedure

Una procedura è un sottoprogramma caratterizzato dal fatto che:

· 939g62j   è dotata di nome

· 939g62j   accetta informazioni in input (come parametri e non) nel senso che può anche modificare

· 939g62j   fornisce risultati in output (come parametri e non)   variabili globali senza specificarle come parametri

· 939g62j   il richiamo di una procedura avviene con l'inserimento del suo nome come istruzione nel corpo del modulo chiamante

I dati in ingresso e i risultati in uscita non sono sempre necessari: si pensi ad esempio ad una procedura che stampa un messaggio fisso.


Facciamo un esempio con un programma per sommare i primi n naturali.

program somma(input;output);

(* somma i primi n numeri naturali*)

var n,s:integer;

procedure sum;

var i:integer;

begin

for i:=1 to n do

s:=s+i

end;

begin

readln(n);

s:=0;

sum;

writeln('somma = ',s)

end.


La procedura sum eredita dal programma principale la visibilità delle variabili n ed i, che considera rispettivamente come variabili di ingresso e di uscita.


L'invocazione della procedura è fatta esclusivamente tramite il suo nome, proprio come se questo rappresentasse per il programma di cui rappresenta una risorsa una nuova istruzione elementare, esattamente come le altre "procedure predefinite" (read, write, ecc...) che mantenendo la visione gerarchica, possono essere pensate come risorse definite ad un livello superiore a quello del programma principale.


Mantenendo gli stessi principi di visibilità delle variabili secondo una struttura gerarchica è possibile annidare le procedure una dentro l'altra.


10.2 I parametri

Un sottoprogramma (procedura o funzione) ha la possibilità di definire parametricamente le variabili su cui operare utilizzando dei parametri.


Nella dichiarazione della procedura inseriremo una dichiarazione di tali variabili come LISTA di parametri formali, cioè di un insieme di variabili che verranno usate nel corpo della procedura stessa, ma che, durante l'esecuzione, dovranno essere riferite alle informazioni corrispondenti fornite all'atto della chiamata, cioè ai parametri attuali.


Nelle chiamate delle procedure read e write i valori passati come argomento sono proprio parametri attuali, mentre in altri casi è possibile definire parametri formali in una dichiarazione di procedura.



Sono possibili diverse modalità di passaggio di parametri.

Il Pascal ne mette a disposizione due:


passaggio per valore

Il passaggio di parametri per valore prevede che alla procedura venga passato il valore di un'espressione(eventualmente costituita da una sola costante o variabile), la quale viene ricopiata nell'area dello stack locale alla procedura, appositamente riservata mediante la dichiarazione del parametro formale corrispondente.


Esempio:

program cerca(input,output);

(* cerca un valore in una di due tabelle*)

const

n=30;

n1=31;

type

table:array 1..n of integer;

range:1..n;

var

k:range;

i:0..1;

tab1:table; (* tabella *)

key:integer; (* valore da cercare nella tabella *)

found:boolean; (* controllo *)


procedure look (x:table; v:integer; m:range);

begin

repeat

i:=i+1

until (i>m) or (x i =v); (* è finita la tabella *)

if x i =v then (* o si è trovato il valore*)

found:=true

end;


begin

readln(k); (*lettura range tabella*)

for i:=1 to k do (*lettura tabella*)

readln(tab1 i

readln(key); (*lettura valore da cercare nella tabella*)

found:=false;

i:=0;

look(tab1,key,k);

if found then

write('trovato')

else

write('non trovato)

end.


I parametri formali x,v ed m della procedura look sono sostituiti nella chiamata dagli attuali tab1,key e k.Il meccanismo di passaggio di parametro non è altro che una ricopiatura del valore del parametro attuale nella zona dello stack della procedura riservata al parametro formale corrispondente.

Ciò comporta

· 939g62j   uno spreco di memoria, e di tempo necessario per il trasferimento,

· 939g62j   l'impossibilità che qualsiasi cambiamento effettuato sul parametro passato per valore venga registrato all'esterno della procedura stessa essendo effettuato sulla copia locale della variabile.


Il passaggio dei parametri per valore consente anche parametri attuali definiti tramite espressioni aritmetiche, es. look(tab1,17,k-5). In questo caso il richiamo comporterà il calcolo del valore dell'espressione prima di effettuare il trasferimento del valore.


Nota I parametri attuali devono essere forniti con rispetto di:

· 939g62j   numero,

· 939g62j   ordine

· 939g62j   tipo



rispetto ai parametri formali.

Dunque i parametri sono posizionali, ovvero al primo parametro formale viene fatto corrispondere il primo parametro attuale, e così via per i successivi.


Passaggio per indirizzo

Il meccanismo di passaggio di parametro per indirizzo fornisce al sottoprogramma chiamato esclusivamente l'indirizzo del parametro attuale, cosicchè questo possa essere direttamente raggiunto e operato in lettura e scrittura dalla procedura chiamata.


Considerando il programma precedente possiamo riscrivere la procedura look con una nuova intestazione, specificando un nuovo parametro passato per indirizzo (trovato):

procedure look(x:table;v:integer;m:range; var trovato:boolean);

begin

repeat

i:=i+1

until (i>m) or (x i =v);

if x i =v then

trovato:=true

end;

dove il parametro formale trovato è per indirizzo.

Nel programma principale nel richiamo della procedura verrà passata la variabile found come parametro attuale per trovato.

look(tab1,key,k,found);


Ciò comporta che

· 939g62j   L'occupazione della memoria legata al parametro formale che viene allocata nello stack locale alla procedura è esclusivamente quella necessaria per contenere l'indirizzo del parametro attuale.

· 939g62j   Cambiando il valore di trovato nella procedura automaticamente viene alterato il valore di found nello stack del programma principale.


parametri procedure/funzioni

In Pascal è possibile passare procedure e funzioni come parametri.

In questo caso il parametro formale corrisponde al modello di una procedura o funzione il cui nome è fittizio, ma di cui vengono specificati i comportamenti (parametri...) e il parametro attuale sarà il nome questa volta reale della procedura/funzione passata.



10.3 Le funzioni

Una funzione è un sottoprogramma caratterizzato dal fatto che:

· 939g62j   è dotata di nome a cui viene associato un valore di ritorno

· 939g62j   è dotata di tipo, che viene dichiarato nella testata della funzione

· 939g62j   accetta informazioni in input (come parametri e non)

· 939g62j   fornisce risultati in output (come parametri e non, oltre al risultato relativo alla funzione stessa).

· 939g62j   nel corpo della funzione il suo nome deve apparire come parte sinistra di un assegnamento, che permette di attribuire ad esso il valore che verrà poi trasmesso al programma chiamante.

· 939g62j   il richiamo di una funzione avviene con l'inserimento del suo nome in un espressione nel corpo del modulo chiamante

Nota L'uso dei parametri nelle funzioni è assolutamente il medesimo che nelle procedure.


Per esempio consideriamo un programma che genera una successione di n numeri che sembrano distribuiti casualmente in modo uniforme nell'intervallo a,b


program Casuale(input,output);

var

r:real; (* variabile per i numeri da generare*)

i:integer; (* contatore*)

n:integer;

a,b:integer; (* estremi dell'intervallo *)

function rndm(x:intero):real;

(* dato un intero genera un valore reale *)

(* pseudocasuale compreso tra 0 e 1 escluso *)

begin

x:=(25173*(x-1)+13849)mod65536;

rndm:=x/65536

end;

begin

readln(n);

readln(a);

readln(b);

for i:=1 to n do

begin

r:=rndm(i)*(b-a)+a;

writeln(r)

end

end.


Dalle osservazioni fatte emerge che il nome di una funzione:

- a destra di un assegnamento ne rappresenta il valore,

- a sinistra invece ha il significato di locazione di memoria

del tutto compatibilmente con il concetto di variabile.


10.3 La ricorsione

Tra le risorse che una procedura o funzione può vedere c'è anche sé stessa.

Ciò permette ad una procedura o ad una funzione di richiamarsi, meccanismo che si definisce


Esiste una ricca classe di problemi che si presta ad una formulazione ricorsiva.

Esempio la funzione fattoriale definita come


n!=1*2*...*(n-1) *n

oppure ricorsivamente come


n!=(n-1)!*n

Utilizzando quindi la forma ricorsiva possiamo scrivere

program fattoriale(input,output);

var n:integer;

function fact(x:integer):integer;

(* calcola il fattoriale di x *)

begin

if x=0 then

fact:=1

else

fact:=x*fact(x-1)

end;

begin

readln(n);

writeln(fact(n))

end.


Si noti che ora il nome della funzione compare a destra di un'assegnament ed è ugualmente corretto.


Osserviamo cosa accade con la ricorsione visualizzando graficamente cosa accade nello stack delle variabili.


Supponiamo che in fase di esecuzione venga letto il valore n=2.

n = 2



Alla prima chiamata sarà allocata una variabile locale, corrispondente al nome fact e verrà copiato il parametro in x.

n = 2

fact

x = 2




siccome x è > 0 viene eseguita la clausola else della struttura if e quindi fact chiama ricorsivamente sé stessa, alla seconda chiamata lo stack sarà divenuto:

n = 2

fact

x = 2

fact

x = 1


siccome x èancora > 0 viene eseguita la clausola else della struttura if e quindi fact chiama di nuovo ricorsivamente sé stessa, alla terza chiamata lo stack sarà divenuto:

n = 2

fact

x = 2

fact

x = 1

fact 1

x = 0



Poichè ora x vale 0 l'attivazione attuale di fact, viene eseguita la clausola else della struttura if e quindi fact ritornerà con valore 1, da cui, eliminando lo spazio di memoria creato dall'ultima attivazione lo stack è:

n = 2

fact

x = 2

fact 1*1

x = 1



Proseguendo, all'uscita della seconda chiamata ricorsiva avremo

n = 2

fact 2*1

x = 2



Infine, all'uscita della prima attivazione della funzione avremo di nuovo:

n = 2



La ricorsione presentata in quest'esempio è detta ricorsione diretta, in quanto il richiamo ricorsivo avviene della funzione (o della procedura) avviene "direttamente" nel corpo della stessa.

Esistono altre situazioni in cui si parla di ricorsione mutua, in cui la procedura A chiama la procedura B che a sua volta richiama la procedura A.


11 Puntatori

Come abbiamo visto il Pascal alloca durante l'esecuzione le proprie variabili in modo dinamico, avvalendosi di un'area di memoria opportunamente dedicata, detta stack.

Tali variabili, create e distrutte man mano che si eseguono procedure e funzioni, vengono però dichiarate in modo statico dal programmatore che infatti non può determinarne istanze al di fuori "dell'ambiente dichiarativo".


Tuttavia esistono le cosiddette variabili dinamiche che sono create e distrutte dinamicamnete durante l'esecuzione del programma completamente sotto il controllo del programmatore.

Per gestire variabili così strutturate uil Pascal mette a disposizione:

· 939g62j   il tipo pointer

· 939g62j   le procedure predefinite new e dispose.


Una variabile di tipo "puntatore a tipo" significa che tale variabile contiene l'indirizzo di una seconda variabile del tipo specificato.

Dunque la dichiarazione

type

ptr = ^integer;

var

x:ptr;

y, z:integer;

permette di attribuire:

· 939g62j   ad x il valore del puntatore ossia dell'indirizzo della variabile puntata

· 939g62j   a x^ il valore della variabile puntata.


Perciò dopo aver effettuato i seguenti assegnamenti

y:=3;

x:=^y;

z:=x^-4;

la situazione dello stack sarà:

x

y 7

z 3


In realtà la memoria non è proprio distribuita così.

In particolare memoria gestita dinamicamente dal programmatore detta heap risulta staccata dallo stack che contiene solo le variabili allocate durante la "lettura della fase dichiarativa".


Onde evitare la presenza di troppe aree di memoria differenti per la gestione delle variabili, STACK e HEAP, che potrebbe portare a situazioni spiacevoli quali:

· 939g62j   problemi di sovrapposizione,

· 939g62j   problemi di dimensionamento (vedi casi in cui lo heap è quasi vuoto e lo stack è pieno oviceversa).

il Pascal usa una stessa area di memoria per ambedue, facendo crescere le aree in maniera contrapposta:


STACK




HEAP


In questo modo lo stack cresce verso il basso e lo heap verso l'alto e chi necessita maggiormente di memoria occupa quella disponibile.

La memoria si esaurisce quando le due aree collidono, e ciò solitamente comporta un messaggio di errore durante l'esecuzione di tipo "stack overflow".


Il Pascal mette a disposizione una procedura predefinita:

new(x)

che applicata ad una variabile x di tipo pointer ha i seguenti effetti:

alloca nell' apposita zona di memoria heap, un'area adeguata a contenere una variabile del tipo puntato dall'argomento;

inserisce nella variabile puntatore l'indirizzo di tale area di memoria.


Simmetricamente alla procedura new il pascal mette a disposizione la procedura

dispose(x)

che applicata ad una variabile x di tipo pointer:

restituisce lo spazio allocato dall'argomentorendendolo nuovamente disponibile per altre allocazioni.

Nota In realtà molti compilatori considerano in ogni caso tale spazio inusabile, altri ricompattano periodicamente,... il tutto a causa del fatto che non sempre si riesce a riutilizzare correttamente la memoria lascata disponibile.

A livello intuitivo possiamo pensare che un'intero occupi meno spazio di un array di 10 elementi, il chè porta ad avere un resto che può a sua volta essere riutilizzato, ma quasi mai del tutto, con conseguenti "buchi inutrilizzati".


Infine il Pascal mette a disposizione la costante

nil (parola chiave)

Tale costante viene utilizzata per attribuire ad una variabile puntatore ads un qualsiasi tipo

un valore che indica che non sta puntando a nulla.


11.1 L'uso dei puntatori, un esempio: le liste

Def. LISTA

Sequenza di elementi di tipo definito in cui è possibile aggiungere e togliere elementi

struttura dati a dimensione variabile

in cui si accede al primo elemento della lista o a un sottinsieme ristretto di elementi


Un elemento può essere aggiunto o tolto specificandone la "posizione" all'interno della lista e raggiungendolo scandendo sequenzialmente la sequenza degli elementi.


Def. LUNGHEZZA DI UNA LISTA

il numero n degli elementi di L


La lista vuota si indica con L


REALIZZAZIONE DI UNA LISTA CON PUNTATORI

Si utilizzano delle celle t.c. l'i-esima cella contiene: l'i-esimo elemento della lista

e il puntatore (indirizzo) all'elemento successivo


nil

 
Lista vuota (lista di lunghezza 0)

L



a1

 

nil

 

 
Lista con un elemento (lista di lunghezza 1)

L



a1

 

 

 

 

a2

 

 

ai+1

 

 

ai

 

nil

 

an

 
Lista con n elementi (lista di lunghezza n)

L ..... .....



N.B. L è il puntatore alla testa della lista


Utilizzando i puntatori del Pascal otteniamo la seguente dichiarazione di tipo:

type

cella = record

elem: tipoelem;

next: ^cella;

end

lista = ^cella;

posizione = ^cella;


 



 

 

 

 
.....


p p^.next p^.next^.next


p^.elem p^.next^.elem


12 File

Il concetto di file è legato all'idea di poter avere informazioni in grandi quantità su supporti esterni.

Non si può pertanto pensare di allocare una variabile di tale tipo sulla memoria centrale (nello stack).

In particolare la dichiarazione di una variabile di tipo file corrisponde all'allocazione in memoria di:

· 939g62j   un'area, detta buffer, organizzzata come uno degli elementi del file,

· 939g62j   una variabile puntatore, che punta a tale area, corrispondente al nome del file.


Quindi volendo definire:

type

libro = record

autore,titolo,editore:array[1..30]of char;

prezzo:integer

end;

biblioteca: file of libro;

var

x:biblioteca;

il modo per accedere ad un elemento della variabile x di tipo biblioteca è

x^

che mette a disposizione il valore corrente del buffer.


Vediamo ora come mettere in relazione il buffer con il file vero e proprio.

12.1 Input files

Il Pascal mette a disposizione le seguenti procedure:

· 939g62j   reset(filename) che predispone il file alla lettura partendo dal primo elemento e

se il file è vuoto attribuisce il valore true alla funz. eof e x^ risulta indefinito

altrimenti inserisce nel buffer(x^) il primo elemento del file.

· 939g62j   get(filename) che sposta il buffer sul prossimo elemento disponibile sul file;

se il file è terminato attribuisce il valore true alla funzione eof.

e la seguente funzione:

· 939g62j   eof(filename) che ritorna true se durante le operazioni sul file è stata raggiunta la sua fine,

false altrimenti.


Essendo il file una variabile esterna al programma, deve essere ad esso passata tramite specificazione nella lista che segue il nome del programma stesso.

Consideriamo ad esempio un programma che suppone di avere a disposizione un file x di interi da leggere e totalizzare:

program somma (input,output,x);

var x:file of integer;

s: integer;

begin

reset(x);

s:=0;

while not eof(x) do

begin

s:=s+x^;

get(x)(*nuovo elemento inserito nel buffer

oppure eof*)

end;

writeln('somma:',s)

end


Altre procedure più sofisticate del get per operare su files in input, è la procedura read con i seguenti funzionamenti:

· 939g62j   read(filename,a) che equivale a a:=filename^;

get(filename);

· 939g62j   read(a) che equivale a read(input,a);

· 939g62j   read(filename,a,b,c,..)che equivale a read(filename,a);

read(filename,b);

read(filename,c);


L'esempio precedente potrebbe quindi essere riscritto:

program somma (input,output,x);

var x:file of integer;

s,i: integer;

begin

reset(x);

s:=0;

while not eof(x) do

begin

read(x,i);

s:=s+i

end;

writeln('somma:',s)

end


12.2 Gli output files

Il Pascal mette a disposizione in modo simmetrico rispetto all'input, le procedure:

· 939g62j   rewrite(filename) che predispone il file in scrittura,

distrugge eventuali dati presenti sul file,

e setta eof a true.

· 939g62j   put(filename) che scrive fisicamente sul file il contenuto del buffer nella prima posizione

disponibile.


Consideriamo come esempio un programma per scrivere un certo insieme di interi su un file.

program writeint(input,output,x);

var x:file of integer;

i:integer;

begin

rewrite(x);

for I:=1 to 100 do

begin

x^:=i;

put(x)

end

end


Sempre simmetricamente a quanto avviene per il caso di file in input, le operazioni di output possono essere facilitate dalla procedura write con i seguenti funzionamenti:

· 939g62j   write(filename,a) che equivale a filename^:=a;

put(filename)

· 939g62j   write(a) che equivale a write(output,a)

· 939g62j   write(filename,a,b,c,...) che equivale a  write(filename,a);

write(filename,b);

write(filename,c);

12.3 I file di testo o textfiles

I textfiles sono dei file di caratteri, dotati perciò di tipo predefinito

Perciò sono equivalenti le scritture:

var

x:text;

var

x:file of char;


Su tali tipi di file si possono utilizzare procedure o funzioni più ampio dei normali files e con un significato leggermente diverso.

Supponiamo di voler leggere un intero da tastiera:

la digitazione dei tasti comporta l'invio al calcolatore della corrispondentestringa di caratteri

tale stringa deve essere tradotta nel corrispondente valore intero in rappresentazione binaria.

Analogamente la stampa di un intero su video presuppone

la conversione del valore intero dalla rappresentazione binaria alla stringa di caratteri

quindi l'operazione di trasposizione su video vera e propria.

Tali operazioni vengono fatte automaticamentedalle procedure read e write nell'ambito dei textfiles.


Un'altra caratteristica interessante dei textfiles è la possibilità di considerare il carattere di fine-linea, che significa che i textfiles riconoscono la struttura a linee.


Nota:

Si può osservare che lo standard input e lo standard output sono a tutti gli effetti dei textfiles.

Vediamo meglio quali sono le primitive con cui è possibile operare sui textfiles.

· 939g62j   read(filename,a,b,c,...) il comportamento è lo stesso che nei file normali,

a parte il fatto che qualora a, b, c siano variabili integer o real

le stringhe di caratteri in ingresso vengono automaticamente

convertite nella rappresentazione interna dei valori relativi.

· 939g62j   readln(fillename,a,b,...) funziona come la precedente, a parte il fatto che alla fine dell'operazione di lettura si posiziona sulla riga successiva

ignorando eventuali altri caratteri sulla riga precedente.

· 939g62j   write(filename,a,b,c,...) il comportamento è lo stesso che nei file normali, a parte il

fatto che qualora a, b, c siano variabili integer o real essi

vengono automaticamente convertiti nella forma di caratteri.

· 939g62j   writeln(filename,a,b,...) funziona come la precedente, a parte il fatto che alla fine

dell'operazione di visualizzazione si posiziona sulla riga successiva.

· 939g62j   eof(filename)funziona come per i file normali.

· 939g62j   eoln(filename)è una funzione booleanache assume il valore true

se tutti i caratteri di una linea sono stati letti (nel caso di input)

o se tutti gli spazi di una linea sono stati scritti (nel caso di output)






Privacy




});

Articolo informazione


Hits: 1746
Apprezzato: scheda appunto

Commentare questo articolo:

Non sei registrato
Devi essere registrato per commentare

ISCRIVITI



Copiare il codice

nella pagina web del tuo sito.


Copyright InfTub.com 2024