Modulo 5 – La programmazione
Unità 9 – Le matrici e strutture
Prof. Antonio Scanu
1 Le Matrici
Supponiamo di voler memorizzare le votazioni riportate dai ginnasti in un'intera gara (agli
anelli, al corpo libero, alla sbarra, alle parallele, al volteggio e al cavallo con maniglie).
Avremo bisogno di sei vettori da gestire in modo parallelo (sette, considerando quello dei
nominativi).
In queste circostanze è utile inserire i punteggi in una struttura di dati a doppia entrata.
Tale struttura è caratterizzata da tante righe quanti sono i ginnasti e da tante colonne
quante sono le prove da sostenere. L'incrocio tra una riga e una colonna conterrà la votazione di un ginnasta in una prova.
Questa struttura prende il nome di matrice o array bidimensionale.
Si definisce matrice a N righe e a M colonne, o più brevemente matrice di tipo NxM, una
tabella formata con NxM elementi omogenei, disposti su N linee orizzontali (righe) ed M li nee verticali (colonne) . Gli elementi si indicano di solito con una stessa lettera munita di
due indici: il primo per indicare la riga, il secondo per indicare la colonna a cui apparten gono.
Gli array finora esaminati erano monodimensionali e richiedevano l'uso di un solo indice.
È facile determinare il numero di dimensioni di un array osservando la sua dichiarazione:
se è presente una sola coppia di parentesi quadre [], l' array è monodimensionale; due
coppie di parentesi quadre [][] indicano invece un array bidimensionale e così via. Il numero massimo di dimensioni solitamente non supera 3 (array multidimensionali).
Per dichiarare una matrice utilizziamo la seguente sintassi, che evidenzia come sia necessario far seguire al nome della variabile l'operatore di dichiarazione [] ripetuto due volte:
< TipoElementi > < NomeArray > [ <Righe >][ < Colonne > ];
dove <Righe> e <Colonne> indicano rispettivamente il numero di righe e il numero di colonne della matrice.
Per dichiarare e creare una matrice 7x4 di numeri reali e di nome Temperature scriveremo:
float Temperature[7][ 4];
Per assegnare a una variabile X il valore dell'elemento di posto riga 5 e colonna 1 scriveremo:
X = Temperature [5] [ 1];
1.1 Esempio
Dimensionare una matrice, caricarla, visualizzarla e creare un vettore parallelo contenente le somme delle righe della matrice.
#include<iostream>
#define dimcolonne 100
#define dimrighe 100
using namespace std;
void dimensiona(int &n, int &m);
void carica(int n, int m, int matt[dimrighe][dimcolonne]);
void visualizza(int n, int m, int const matt[dimrighe][dimcolonne]);
void creavettore(int n, int m, int matt[dimrighe][dimcolonne],int vett[dimrighe]);
void visualizzavettore (int n,int const v[dimrighe]);
int main() {
int nrighe,mcolonne,matrice[dimrighe][dimcolonne],vettore[dimrighe];
dimensiona(nrighe,mcolonne);
carica(nrighe,mcolonne,matrice);
visualizza(nrighe,mcolonne,matrice);
creavettore(nrighe,mcolonne,matrice,vettore);
visualizzavettore(nrighe,vettore);
return 0;
}
void dimensiona(int &n, int &m){
do{cout<<"inserisci il numero delle righe";
cin>>n;
}while(n<=0 || n>=dimrighe);
do{cout<<"inserisci il numero delle colonne";
cin>>m;
}while(m<=0 || m>=dimcolonne);
}
void carica(int n, int m, int matt[dimrighe][dimcolonne]){
int i,j;
for(i=0;i<n;i++)
{for(j=0;j<m;j++)
{cout<<"inserisci il valore di posizione"<<i<<j;
cin>>matt[i][j];
}
}
}
void visualizza(int n, int m, int const matt[dimrighe][dimcolonne]){
int i,j;
for(i=0;i<n;i++)
{for(j=0;j<m;j++)
{cout<<matt[i][j]<<" ";
}
cout<<endl;
}
}
void creavettore(int n, int m, int matt[dimrighe][dimcolonne],int vett[dimrighe]){
int i,j;
for(i=0;i<n;i++)
{for(j=0;j<m;j++)
{vett[i]=vett[i]+matt[i][j];
}
}
}
void visualizzavettore (int n,int const vett[dimrighe]){
int j;
for(j=0;j<n;j++)
{cout<<vett[j]<<" ";
}
}
2 I Record
Nella realtà nasce spesso l'esigenza di dover trattare in formazioni di tipo diverso relative
a un oggetto preso in esame. Ad esempio, per avere delle informazioni su una fattura è
necessario ricordare il numero della fattura in questione, la data di emissione, la denominazione dell'impresa emittente e l' importo. Utilizzando le seguenti variabili semplici:
è possibile risolvere il problema, ma l'utilizzo di tali variabili non indica in alcun modo che i
dati in esse contenuti si riferiscono allo stesso oggetto (nel nostro caso, alla stessa fattu ra) .
Un altro esempio: le variabili Titolo, Autore, CasaEditrice e DataPubb contengono informazioni relative a un medesimo libro, ma non esiste un modo che ci consenta di assicurarci
di ciò.
Dagli esempi esposti si evince che il nostro intento non è quello di descrivere un oggetto,
bensì di trovare un metodo per descrivere una classe di oggetti aventi caratteristiche comuni, che prendono il nome di attributi.
Riferendoci all'ultimo esempio, possiamo supporre che la classe di oggetti Libri sia composta da tutti i libri presenti nella biblioteca della nostra scuola e che ogni libro sia caratte rizzato dai seguenti attributi: Titolo, Autore, CasaEditrice e DataPubb. Tali valori rappresentano, perciò, gli attributi comuni a tutti i libri presenti nella biblioteca, quindi tutti gli oggetti appartenenti alla classe.
Utilizzando gli array, il solo modo di rappresentare la classe di oggetti Libri è quello di servirsi di quattro vettori paralleli, perché gli attributi non sono omogenei: uno per la memorizzazione dei titoli, uno per i nominativi degli autori, uno per il nome della casa editrice e
uno per la memorizzazione della data di pubblicazione.
L'idea di utilizzare quattro vettori paralleli sembrerebbe valida, ma ci accorgiamo che, pur
risolvendo il problema, non ci consente di avere una visione globale dei dati relativi a ciascun oggetto. Non possiamo infatti fare riferimento ai singoli attributi come insieme logico
di attributi di un libro. In conclusione, quando dobbiamo trattare più informazioni non necessariamente omogenee relative a uno stesso oggetto, dobbiamo ricorrere a un altro tipo
di dato strutturato: il record.
Un record, o registrazione, è una struttura di dati a carattere statico composta da un insieme finito di elementi omogenei o eterogenei detti campi. I campi sono tra loro logicamen te connessi e corrispondono agli attributi. Ogni campo accoglie un valore per un attributo.
Il record è un costruttore di tipi di dato predefinito. Viene messo a disposizione da molti
linguaggi di programmazione non basati sul paradigma a oggetti. Un record è caratterizza to da un nome, che lo identifica e si riferisce a esso nella sua globalità. I campi che lo
compongono sono caratterizzati da un nome e dal tipo di dato che possono contenere.
Possono essere, indifferentemente, di tipo semplice o di tipo strutturato: un singolo campo di un record, infatti, può essere a sua volta un record oppure un array. Chiamiamo
struttura di un record la definizione dei campi che compongono il record stesso.
Le strutture del linguaggio C coincidono con quelli che in informatica sono comunemente
definiti record. Nel seguito, dunque, utilizzeremo indifferentemente i termini "struttura" e
"record". Il raggruppamento delle suddette informazioni sotto un nome comune permette
di rappresentare tramite le strutture entità logiche i cui attributi sono rappresentati dalle
variabili comprese nella struttura.
Un esempio può essere l'aggregazione di informazioni quali Marca, Modello, Targa, Cilindrata, Potenza, che costituiscono un insieme di tipo eterogeneo ma che sono accomunate
dall'essere le caratteristiche peculiari di una struttura che possiamo chiamare Automobi le.
Per dichiarare una struttura si utilizza la seguente sintassi:
struct < NomeStruttura >
{
< Ti p_oCamQ.Qj > < NomeCampo 1 >;
<TipoCampoN > < NomeCampoN >;
};
dove < TipoCampo > e < NomeCampo > indicano rispettivamente il tipo e il nome di ogni
elemento che concorre a caratterizzare la struttura che stiamo definendo.
Tali elementi si chiamano campi. Possiamo allora dire che una struttura, o meglio un particolare tipo di struttura, è dichiarata definendone il nome ed elencando i nomi e i tipi dei
suoi campi. La precedente dichiarazione è considerata una dichiarazione di tipo. Per
esempio, per dichiarare la struttura automobile o equivalentemente "una struttura di tipo
automobile" o ancora "un tipo di struttura automobile" scriveremo:
struct automobile
{
char marca;
char modello;
char targa;
int cilindrata;
float potenza;
};
All'interno della dichiarazione:
non è consentita alcuna inizializzazione dei campi;
non è consentito utilizzare un nome per un campo uguale al nome della struttura;
è consentito usare lo stesso nome per campi appartenenti a strutture diverse;
si possono utilizzare tipi semplici, tipi aggregati o tipi definiti dall'utente.
Esiste l'istruzione typedef che permette di rinominare la struttura dati per semplificare la
notazione. Ad esempio se vogliamo chiamare la struttura automobile auto scriveremo alla
fine della dichiarazione della struttura:
typedef struct automobile auto;
Per dichiarare tre variabili di tipo automobile e di nome a1,a2 e a3 scriveremo:
struct automobile a1 , a2, a3;
auto a1,a2,a3 /*nel caso della typedef; */
Per fare riferimento a un particolare campo di una struttura, ricorriamo alla dot notation
secondo la seguente sintassi:
< VariabileDiTipoStruct > . < NomeCampo >
Per esempio, per assegnare a una variabile x di tipo intero il valore della cilindrata di una
variabile al di tipo automobile, scriveremo:
x = a1 .cilindrata;
In modo analogo, per assegnare al campo cilindrata della variabile al di tipo automobile
un nuovo valore, scriveremo:
a1 .cilindrata = 1600;
2.1 Esempio
Dopo aver definito una struttura dati che descrive le seguenti caratteristiche di un uomo:
nome,altezza, peso ed età. Scrivere un programma in cui si inseriscono i dati di due uomini e si visualizzano dopo averli scambiati.
#include <iostream>
using namespace std;
struct uomo {
char nome;
int eta;
int altezza;
float peso;
};
typedef struct uomo uomo;
int main(){
uomo u1,u2,aux;
cout<<"inserisci nome primo uomo";
cin>>u1.nome;
cout<<"inserisci eta' primo uomo";
cin>>u1.eta;
cout<<"inserisci altezza primo uomo";
cin>>u1.altezza;
cout<<"inserisci peso primo uomo";
cin>>u1.peso;
cout<<"inserisci nome secondo uomo";
cin>>u2.nome;
cout<<"inserisci eta' secondo uomo";
cin>>u2.eta;
cout<<"inserisci altezza secondo uomo";
cin>>u2.altezza;
cout<<"inserisci peso secondo uomo";
cin>>u2.peso;
aux.altezza=u1.altezza;
aux.eta=u1.eta;
aux.peso=u1.peso;
aux.nome=u1.nome;
u1.altezza=u2.altezza;
u1.eta=u2.eta;
u1.peso=u2.peso;
u1.nome=u2.nome;
u2.altezza=aux.altezza;
u2.eta=aux.eta;
u2.peso=aux.peso;
u2.nome=aux.nome;
cout<<"dati primo uomo=";
cout<<" nome "<<u1.nome;
cout<<" eta' "<<u1.eta;
cout<<" peso "<<u1.peso;
cout<<" altezza"<<u1.altezza<<endl;
cout<<"dati seconodo uomo=";
cout<<" nome "<<u2.nome;
cout<<" eta' "<<u2.eta;
cout<<" peso "<<u2.peso;
cout<<" altezza"<<u2.altezza<<endl;
return 0;
}
2.2 Vettori di struttura e passaggio a funzione
Si possono definire anche vettori di struttura utilizzando la stessa notazione dei vettori e
indicando i campi preceduti da un punto.
Il passaggio di parametri è identico a quello degli altri tipi di dato.
Esempio dopo aver caricato un vettore di struttura che tiene conto delle caratteristiche di
un uomo (nome,eta,altezza,peso) lo visualizzi e determini e visualizzi le caratteristiche della persona più alta e la media delle età attraverso rispettivamente una procedura e una
funzione.
#include <iostream>
#define dim 100
using namespace std;
struct uomo {
char nome;
int eta;
int altezza;
float peso;
};
typedef struct uomo uomo;
int dimensiona();
void carica(int n,uomo u[dim]);
void visualizza (int n, uomo const u[dim]);
void calcolo(int n,uomo const u[dim], uomo &umax);
float funmedia(int n, uomo const u[dim]);
int main(){
uomo uvett[dim],uomomax;
float media;
int n1;
n1=dimensiona();
carica(n1,uvett);
visualizza(n1,uvett);
calcolo(n1,uvett,uomomax);
cout<<"\n dati dell' uomo con l'altezza' maggiore sono:"<<endl;
cout<<" nome "<<uomomax.nome<<endl;
cout<<" eta' "<<uomomax.eta<<endl;
cout<<" peso "<<uomomax.peso<<endl;
cout<<" altezza "<<uomomax.altezza<<endl<<endl;
media=funmedia(n1,uvett);
cout<<"la media dell'eta' e'"<<media<<endl;
return 0;
}
int dimensiona(){
int n;
do {cout<<"inserisci dimensione ninore di 100";
cin>>n;}
while(n<=0 || n>100);
return n;
}
void carica(int n,uomo u[dim]){
int i;
for(i=0;i<n;i++){
cout<<endl<<"dati uomo numero"<<i+1<<endl;
cout<<"inserisci nome uomo ";
cin>>u[i].nome;
do{cout<<"inserisci eta' ";
cin>>u[i].eta;}
while(u[i].eta<=0);
do{cout<<"inserisci altezza ";
cin>>u[i].altezza;}
while(u[i].altezza<=0);
do{cout<<"inserisci peso ";
cin>>u[i].peso;}
while(u[i].peso<=0);
}
}
void visualizza (int n, uomo const u[dim]){
int i;
cout<<"\n tabella dati"<<endl<<"num | nome | eta' | peso | altezza "<<endl;
for(i=0;i<n;i++){
cout<<i<<"\t"<<u[i].nome<<"\t"<<u[i].eta<<"\t"<<u[i].peso<<"\t"<<u[i].altezza<<"\t"<<e
ndl;
}
}
void calcolo(int n,uomo const u[dim], uomo &umax){
int i;
umax.eta=-1;
umax.peso=-1;
umax.altezza=-1;
for(i=0;i<n;i++){
if(umax.altezza<u[i].altezza)
{
umax.nome=u[i].nome;
umax.eta=u[i].eta;
umax.peso=u[i].peso;
umax.altezza=u[i].altezza;
}
}
}
float funmedia(int n, uomo const u[dim]){
int i;float acc,m;
for(i=0;i<n;i++){
acc=acc+u[i].eta;
}
m=acc/n;
return m;
}
3
ESERCITAZIONE (Matrici)
1. Dimensionare,caricare e visualizzare A(5,5). Letto un intero da tastiera visualizzare la
riga corrispondente a quell’intero. Eseguire un controllo sul valore inserito da tastiera.
2. Data una matrice costruire un algoritmo che crea un vettore contenete la media dei valori contenuti nelle righe ed un secondo vettore contenente la media dei valori contenuti nelle colonne.
3. Considerare una matrice di interi S di dimensione Nx2 in cui la prima colonna riporta i
numeri di matricola di N studenti e la seconda colonna il voto da essi riportato nell’esame di programmazione. Ad esempio :
mat voto
121
24
286
27
138
18
231
21
153
30
215
27
Scrivere un programma che legga e visualizzi la matrice e successivamente effettui le seguenti operazioni :
Letto il valore di un voto compreso tra 18 e 30, visualizzare le matricole di tutti gli
studenti che hanno riportato quel voto, inviando un messaggio nel caso che nessuno
lo abbia riportato.
Letto il valore di una matricola, verificare se lo studente con tale matricola ha sostenuto o no l’esame.
Scambiare di posto nella tabella lo studente che ha la matricola più alta con quello
che ha la matricola più bassa ( scambiando anche i voti )
4. Si consideri una matrice di interi S di dimensione Nx3 n cui la prima colonna riporta i
numeri di matricola di N studenti e la seconda colonna il voto da essi riportato in un
esame e la terza il codice dell’esame . Ad esempio :
matr. voto esame
121
24
1
286
27
2
151
25
1
286
28
1
138
24
5
Nella prima colonna, compaiono più volte le matricole degli studenti che hanno sostenuto
più di un esame.
Scrivere un programma che generi e visualizzi la matrice e successivamente effettui le seguenti operazioni :
letto un numero di matricola, visualizzi la media dello studente che ha quella matri cola oppure un messaggio se lo studente non ha sostenuto nessun esame.
Visualizzi per ogni codice di esame la media riportata dagli studenti che lo hanno
sostenuto.
5. Considerare una matrice di interi S di dimensione Nx2 in cui la prima colonna riporta i
numeri di matricola di N studenti e la seconda colonna il voto da essi riportato nell’esame di programmazione. Ad esempio :
mat voto
121
24
286 27
138 18
231 21
153 30
215 27
Scrivere un programma che legga e visualizzi la matrice. Ordinare la tabella per matricole
crescenti (scambiando anche i voti).
4
ESERCITAZIONE (Strutture)
1. Scrivere un programma che, utilizzando il tipo struttura, costruisca la seguente tabella.
matricola
Cognome e Nome
Corso
voto
121
Piras Giorgio
A
24
286
Zucca Mario
B
27
138
Rossi Paolo
A
18
231
Pinco Pallino
A
21
153
Vacca Sergio
B
30
215
Serra Marcello
B
27
Acquisire i dati da tastiera e visualizzare la tabella.
Inserire nel precedente programma una funzione che riceve la tabella come argomento, legge il codice di un corso e visualizza tutti gli studenti di quel corso. La vi sualizzazione avviene all’interno della funzione.
2. Utilizzando una struttura, descrivere il menù di un ristorante in modo che sia utilizzabile da una persona che segue una dieta.
Nella struttura sono riportati il nome del piatto (es. minestra, pastasciutta), le calorie, il
tipo (0=antipasto, 1=primo, 2=secondo, 3=con-torno, 4=frutta, 5=dessert), il prezzo.
Memorizzare i piatti disponibili in una tabella acquisita dal main e visualizzarla.
Inserire nel programma una funzione che riceve dal main un intero tra 0 e 5 che indica un tipo di piatto e visualizza il nome del piu’ costoso piatto di quel tipo.
Inserire nel programma precedente una funzione che legge i piatti scelti dal cliente
(mediante la loro posizione nel menù) e rende al main il prezzo del pasto e le calorie
fornite.
Inserire nel programma una funzione che ordina i piatti disponibili in ordine di tipo.