P - ADT Flashcards
Perché un dato è detto astratto?
Perché maschera la sua implementazione mediante la coppia di moduli interfaccia/implementazione
Quali sono le caratteristiche di un quasi ADT?
- il nuovo tipo viene definito con la typedef nel file.h
- il client include il file.h quindi vede i dettagli interni del dato
- non richiede allocazione dinamica
- il quasi ADT per composti viola la regola 1 mentre quello per le collezioni viola la 2
Quali sono le caratteristiche di un ADT di I classe e le regole? Fare un esempio di file .h e .c con un dato per numeri complessi.
- il nuovo tipo viene dichiarato nel file.h ma lasciato incompleto; viene completamente definito nel file.c
- di solito ciò che viene messo nel file.h è un puntatore a struct incompleta
- il client può usare solo il puntatore a struct
- il puntatore è opaco e si dice handle
Regole:
1) nasconde al client i dettagli
2) permette al client di creare più variabili del tipo ADT
Esempio
complex.h
typedef struct complex_s *Complex;
void funzione(Complex c);
complex.c
struct complex_s { float Re; float Im; };
Quali sono i modi per definire l’ADT Item?
1) typedef int Item;
typedef int Key;
2) typedef char *Item;
typedef char *Key;
3)
typedef struct {
char name[MACX];
int num;
} Item;
typedef char *Key
4)
typedef struct {
char name*;
int num;
} Item;
typedef char *Key
Qual è il prototipo della KEYget in base al tipo di Item per i quasi ADT?
Key KEYget(Item val); nei casi 1 e 2
Key KEYget(Item *pval); nei casi 3 e 4
Quasi ADT, scrivere per il tipo di Item 1 le funzioni:
- Key KEYget(Item val);
- int KEYcompare(Key k1, Key k2);
- Item ITEMscan();
- void ITEMshow(Item v);
- int ITEMless(Item val1, Item val2);
Key KEYget(Item val){
return val;
}
int KEYcompare(Key k1, Key k2){
return k1-k2;
}
Item ITEMscan(){
Item i;
scanf(“%d”, &i);
return I;
}
void ITEMshow(Item v){
printf(“%d”, v);
}
int ITEMless(Item val1, Item val2){
return(KEYget(val1) < KEYget(val2));
}
Quasi ADT, scrivere per il tipo di Item 2 le funzioni:
- Key KEYget(Item val);
- int KEYcompare(Key k1, Key k2);
- Item ITEMscan();
- void ITEMshow(Item v);
- int ITEMless(Item val1, Item val2);
static char buf[MAXC];
Key KEYget(Item val){
return val;
}
int KEYcompare(Key k1, Key k2){
return (strcmp(k1, k2));
}
Item ITEMscan(){
scanf(“%s”, buf);
return strdup(buf);
}
void ITEMshow(Item v){
printf(“%s”, v);
}
int ITEMless(Item val1, Item val2){
return (strcmp(KEYget(val1),KEYget(val2))<0);
}
Quasi ADT, scrivere per il tipo di Item 3 le funzioni:
- Key KEYget(Item *pval);
- int KEYcompare(Key k1, Key k2);
- Item ITEMscan();
- void ITEMshow(Item v);
- int ITEMless(Item val1, Item val2);
static char buf[MAXC];
Key KEYget(Item *pval){
return pval->name;
}
int KEYcompare(Key k1, Key k2){
return (strcmp(k1, k2));
}
Item ITEMscan(){
Item val;
scanf(“%s%d”, val.name, &val.num);
return val;
}
void ITEMshow(Item v){
printf(“%s %d”, v.name, v.num);
}
int ITEMless(Item val1, Item val2){
return (strcmp(KEYget(&val1),KEYget(&val2))<0);
}
Quasi ADT, scrivere per il tipo di Item 4 le funzioni:
- Key KEYget(Item *pval);
- int KEYcompare(Key k1, Key k2);
- Item ITEMscan();
- void ITEMshow(Item v);
- int ITEMless(Item val1, Item val2);
static char buf[MAXC];
Key KEYget(Item *pval){
return pval->name;
}
int KEYcompare(Key k1, Key k2){
return (strcmp(k1, k2));
}
Item ITEMscan(){
Item val;
scanf(“%s%d”, buf, &(val.num));
val.name = strdup(buf);
return val;
}
void ITEMshow(Item v){
printf(“%s %d”, v.name, v.num);
}
int ITEMless(Item val1, Item val2){
return (strcmp(KEYget(&val1),KEYget(&val2))<0);
}
In quali casi di Item ha senso la deallocazione?
Solo nei casi 2 e 4.
Quali tipi di Item usiamo per gli ADT di I classe?
Cosa si trova nel file.h?
Usiamo il 3 e il 4.
file.h
typedef struct item *Item;
typedef struct char *Key;
…
Scrivere le seguenti funzioni per gli ADT di prima classe:
- Key KEYget(Item val);
- int KEYcompare(Key k1, Key k2);
- void ITEMshow(Item val);
- int ITEMless(Item val1, Item val2);
Key KEYget(Item val){
return val->name;
}
int KEYcompare(Key k1, Key k2){
return strcmp(k1, k2);
}
void ITEMshow(Item val){
printf(“%s %d”, val->name, val->num);
}
int ITEMless(Item val1, Item val2){
return strcmp(KEYget(val1), KEYget(val2));
}
Scrivere le funzioni di allocazione, deallocazione e scan di Item per ADT di I classe (3 e 4)
- Item ITEMnew(void);
- void ITEMfree(Item val);
- Item ITEMscan() (3) e void ITEMscan(Item val) (4)
Item ITEMnew(void){
Item val;
val = malloc(sizeof(struct item));
if (val == NULL) return ITEMsetvoid();
val->name = ‘\0’; // 4) val->name = NULL;
val->num = 0;
return val;
}
void ITEMfree(Item val){
free(val->name); //solo nel 4)
free(val);
}
Item ITEMscan(){
Item val = ITEMnew();
scanf(“%s %d”, val->name, &val->num);
return val;
}
void ITEMscan(Item val){
scanf(“%s %d”, buf, &val->num);
val->name = strdup(buf);
}
Quali sono i criteri possibili di Delete nelle code in generale?
- FIFO
- LIFO
- priorità
- casuale
- contenuto (tabella di simboli)
In che modo si possono implementare le code, in generale, e quali sono vantaggi e svantaggi?
Si possono implementare con vettori o liste, in versione quasi ADT o ADT di I classe.
I vettori hanno uno spazio sempre alloccato al massimo previsto quindi sono vantaggiosi per code piene o quasi; per le liste lo spazio è proporzionale al numero effettivo di elementi.
Il quasi ADT prevede delle variabili globali e invisibili ad altri (NON ADT), l’ADT di I classe prevede una struct puntata da handle; la struct ha come campi le variabili globali del quasi ADT
ADT Stack: quali funzioni lo caratterizzano e qual è la loro complessità?
stackpush e stackpop, O(1)
ADT Stack: implementazione con vettore quasi ADT. Scrivere Stack.h e Stack.c con strutture dati e funzioni di inizializzazione, push e pop.
- void STACKinit(int MAXN);
- int STACKempty();
- void STACKpush(Item val);
- Item STACKpop();
stack.h
void STACKinit(int MAXN);
int STACKempty();
void STACKpush(Item val);
Item STACKpop();
stack.c
static Item *s; //vettore di Item
static int N;
void STACKinit(int MAXN){
s = malloc(MAXN*sizeof(Item));
N = 0;
}
int STACKempty() { return N == 0; }
void STACKpush(Item val){
s[N++] = val;
}
Item STACKpop(){
return s[–N];
}
ADT Stack: implementazione con vettore ADT di I classe. Scrivere Stack.h e Stack.c con strutture dati e funzioni di inizializzazione, push e pop.
- STACK STACKinit(int MAXN);
- int STACKempty(STACK sp);
- void STACKpush(STACK sp, Item val);
- Item STACKpop(STACK sp);
stack.h
typedef struct stack *STACK;
STACK STACKinit(int MAXN);
int STACKempty(STACK s);
void STACKpush(STACK s, Item val);
Item STACKpop(STACK s);
stack.c
struct stack { Item *s; int N; };
STACK STACKinit(int MAXN){
STACK sp;
sp = malloc(sizeof(sp));
sp->s = malloc(MAXNsizeof(Item));
sp->N = 0;
return sp;
}
int STACKempty(STACK sp){
return sp->N == 0;
}
void STACKpush(STACK sp, Item val){
sp->s[sp->N++] = val;
}
Item STACKpop(STACK sp){
return sp->s[–(sp->N)];
}
ADT Stack: implementazione con lista, quasi ADT. Scrivere Stack.h e Stack.c con strutture dati e funzioni di inizializzazione, push e pop.
- void STACKinit(int MAXN);
- int STACKempty();
- void STACKpush(Item val);
- Item STACKpop();
stack.h
void STACKinit(int MAXN);
int STACKempty();
void STACKpush(Item val);
Item STACKpop();
stack.c
typedef struct STACKnode *link;
struct STACKnode { Item val; link next; };
static link head;
static link NEW (Item val, link next){
link x;
x = malloc(sizeof(*x));
x->val = val; x->next = next;
return x;
}
void STACKinit(int maxN) { head = NULL; }
int STACKempty() { return head == NULL; }
void STACKpush(Item val) {
head = NEW(val, head);
}
Item STACKpop() {
Item tmp;
tmp = head->val;
link t = head->next;
free(head);
head = t;
return tmp;
}
ADT Stack: implementazione con lista, ADT di I classe. Scrivere Stack.h e Stack.c con strutture dati e funzioni di inizializzazione, push e pop.
- STACK STACKinit(int MAXN);
- int STACKempty(STACK s);
- void STACKpush(STACK s, Item val);
- Item STACKpop(STACK s);
stack.h
typedef struct stack *STACK;
STACK STACKinit(int MAXN);
int STACKempty(STACK s);
void STACKpush(STACK s, Item val);
Item STACKpop(STACK s);
stack.c
typedef struct STACKnode *link;
struct STACKnode { Item val; link next; };
struct stack { link head; };
static link NEW(Item val, link next){
link x;
x = malloc(sizeof(*x));
x->val = val; x->next = next;
return x;
}
STACK STACKinit(int MAXN){
STACK s = malloc(sizeof(*s));
s->head = NULL;
return s;
}
int STACKempty(STACK s){
return s->head == NULL;
}
void STACKpush(STACK S, Item val){
s->head = NEW(val, s->head);
}
Item STACKpop(STACK s){
Item tmp;
tmp = s->head->val;
link t = s->head->next;
free(s->head);
s->head = t;
return tmp;
}
ADT QUEUE: quali operazioni lo caratterizzano e che complessità hanno?
QUEUEput e QUEUEget, O(1)
FIFO
ADT QUEUE: come si può implementare? Quali sono le caratteristiche?
Attraverso lista e vettore.
Il vettore può essere sia lineare che circolare.
- vettore lineare: O(n), head = 0 è fisso e quindi bisogna “scalare” gli elementi
- vettore circolare: O(1), head e tail sono incrementati modulo N e non è più garantito
Come funziona il buffer circolare?
- head e tail sono incrementati modulo N
- se la coda è vuota vuol dire che head ha raggiunto tail
- se è piena tail ha raggiunto head
- si allocano maxn+1 elementi in modo che non si possa mai avere head == tail
- per convenzione all’inizio head = N e tail =0
- coda vuota: head%N == tail
ADT QUEUE: implementazione con vettore, quasi ADT. Scrivere queue.h e queue.c con strutture dati e funzioni di inizializzazione, può e get.
- void QUEUEinit(int MAXN);
- int QUEUEempty();
- void QUEUEput(Item val);
- Item QUEUEget();
queue.h
void QUEUEinit(int MAXN);
int QUEUEempty();
void QUEUEput(Item val);
Item QUEUEget();
queue.c
static Item *q;
static int N, head, tail;
void QUEUEinit(int MAXN){
q = malloc((MAXN+1)*sizeof(Item));
N = MAXN+1; head = N; tail = 0;
}
int QUEUEempty() {
return head%N == tail;
}
void QUEUEput(Item val){
q[tail++] = val;
tail = tail%N;
}
Item QUEUEget() {
head = head%N;
return q[head++];
}