Laboratorio di Algoritmi e Strutture Dati II Semestre 2005/2006 Pile e Code Marco Antoniotti Pile (stacks) e code (queues) Tipi di dati fondamentali Applicazioni molteplici Le operazioni standard Aggiungi un elemento Rimuovi un elemento Controlla se vuoto La distinzione tra pile e code sta nell interpretazione dell operazione di rimozione Pile Rimuovi l elemento aggiunto più di recente (Last In, First Out, LIFO) Esempio: web surfing, vassoi in un self-service, chiamate a funzione Code Rimuovi l elemento aggiunto meno di recente (First In, First Out, FIFO) Esempio: nuove code all ufficio postale II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 1 1
Pile e code Push (aggiungi) Pop (rimuovi) Enqueue (aggiungi) Dequeue (rimuovi) II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 2 Interfaccia, Implementazione, Cliente È bene separare l interfaccia di un modulo software (libreria o tipo di dato) dalla sua implementazione Si possono costruire diversi strati di software Si riusa codice Esempi: code, pile, dizionari (symbol tables) Non è necessariamente un attività semplice Library design is language design Interfaccia: descrizione dei tipi di dati e delle operazioni base Cliente: l utilizzatore delle operazioni e dei tipi definiti nell interfaccia Implementazione: il codice che realizza effettivamente le operazioni II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 3 2
Interfaccia, implementazione, cliente Benefici Il cliente non conosce i dettagli dell implementazione, cosicchè può scegliere tra diverse implementazioni L implementazione non conosce i dettagli dei vari clienti, quindi il suo codice può venir riusato più volte Progettazione: si creano moduli riutilizzabili Prestazioni: si usano implementazioni ottimizzate quando necessario (ed utile, e dopo averne provate le proprietà) Interfaccia: descrizione dei tipi di dati e delle operazioni base Cliente: l utilizzatore delle operazioni e dei tipi definiti nell interfaccia Implementazione: il codice che realizza effettivamente le operazioni II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 4 Pile Un esempio in C di un programma (cliente) che usa una pila (stack) di stringhe Push: aggiungi un elemento Push (aggiungi) Pop: pop rimuovi l ultimo elemento aggiunto Is_empty: controlla se la pila è vuota #include <stdio.h> #include stacks.h /* Si leggono stringhe da input e le si stampa in ordine * inverso. */ intmain() { stack_string_stack s = stack_new_string_stack(); charinput[80]; while((input = fgets(input, 79, stdin))!= EOF) stack_push_string(s, input); while(! stack_is_em pty(s)) printf( popping %s\n, stack_pop_string(s)); stack_free(s); Pop (rimuovi) II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 5 3
Pile Il programma cliente usa una particolare versione dell interfaccia In C, siamo obbligati a fornire diverse versioni di una stessa interfaccia A meno di utilizzare unioni di tipi (union) o puntatori a void (void*) Questo è il problema dei contenitori che Ada, C++, Java (1.5) ed altri linguaggi risolvono mediante l uso di generics o templates Vedremo due implementazioni dell interfaccia Lista Array II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 6 Pila come lista: pop vita alla guida la the_item = ->item; alla guida la = ->next; alla guida la returnthe_item; II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 7 4
Pila come lista: push alla guida la second alla guida la second = ; second vita alla guida la vita alla guida la = new_node(); ->item = vita ; ->next = second; II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 8 Pila come lista: implementazione in C Il file string-item.h /* string-item.h */ #include <string.h> typedef char* string_item; /* end of file --string-item.h --*/ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 9 5
Pila come lista: implementazione in C Il file string-stack.h /* string-stack.h */ #include <string-item.h> struct_stack; typedef struct_stack * stack_string_stack; stack_string_stack stack_new_string_stack(); void stack_free(stack_string_stack); bool stack_is_em pty(stack_string_stack); string_item stack_pop_string(stack_string_stack); voidstack_push_string(stack_string_stack, string_item); /* end of file -- string-stack.h --*/ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 10 Pila come lista: implementazione in C Il file string-stack.c /* string-stack.c */ #include <string-stack.h> #include <assert.h> /* Vedremo perche`. */ #include <stdio.h> struct_node { string_item item; struct_node * next; ; typedef struct_node * node; struct_stack { node ; ; /* */ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 11 6
Pila come lista: implementazione in C Il file string-stack.c stack_string_stack stack_new_string_stack() { stack_string_stack new_s = (stack_string_stack)malloc(sizeof(struct_stack)); if(new_s) return new_s; else{ fprintf(stderr, Stack library: failed allocation.\n ); exit(1); void stack_free(stack_string_stack s) { /* Esercizio! */ bool stack_is_empty(stack_string_stack s) { assert(s!= NULL); return s-> == NULL; /* */ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 12 Pila come lista: implementazione in C string_item stack_pop_string(stack_string_stack s) { if(stack_is_empty(s)) { fprintf(stderr, Stack library: popping an empty stack.\n ); exit(1); else{ node _node = s->; string_item item = _node->item; s-> = _node->next; free(_node); returnitem; /* */ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 13 7
Pila come lista: implementazione in C void stack_push_string(stack_string_stack s, string_item item) { assert(s!= NULL); { node second = s->; node new_node = (node) malloc(sizeof(struct_node)); if(new_node!= NULL) { new_node->item = item; new_node->next = second; s-> = new_node; else{ fputs( Stack library: failed allocation.\n, stderr); exit(1); /* end of file --string-stack.h --*/ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 14 Pila come array Si usa un array per implementare lo stack Si usa un contatore (puntatore) top come indice dell elemento in cima allo stack Push inserisce un elemento in posizione top + 1 Pop decrementa il contatore top L interfaccia rimane la stessa! top Don t Panic Trillian! Poetry Vogon II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 15 8
Pila come array: implementazione in C Il file string-stack.c /* string-stack.c */ #include <string-stack.h> #include <assert.h> #include <stdio.h> static const intstack_n = 10; struct_stack { string_item * items; int top; int size; ; /* */ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 16 Pila come array: implementazione in C Il file string-stack.c stack_string_stack stack_new_string_stack() { stack_string_stack new_s = (stack_string_stack)malloc(sizeof(struct_stack)); assert(new_s); new_s->items = (string_item *) malloc(stack_n * sizeof(string_item)); assert(new_s->items); new_s->size = stack_n; new_s->top = -1; /* Valore convenzionale! */ return new_s; void stack_free(stack_string_stack s) { /* Esercizio! */ bool stack_is_empty(stack_string_stack s) { assert(s!= NULL); return s->top == -1; /* */ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 17 9
Pila come array: implementazione in C string_item stack_pop_string(stack_string_stack s) { if(stack_is_empty(s)) { fprintf(stderr, Stack library: popping an empty stack.\n ); exit(1); else{ return s->items[s->top--]; /* Da controllare. */ /* */ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 18 Pila come array: implementazione in C void stack_push_string(stack_string_stack s, string_item item) { assert(s!= NULL); if(s->top == s->size) { /* Lo stack e` pieno: che facciamo? */ else{ s->items[++s->top] = item; /* Controllare! */ /* end of file --string-stack.h --*/ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 19 10
Pila come array Cosa facciamo quando lo stack è pieno? Soluzione semplice: Si raddoppia l array e si compiano tutti gli elementi Se si aggiungesse una sola casella per volta, l operazione di copia per N operazioni di estensione risulterebbe quadratica Con il raddoppio, l operazione di copia per N operazioni di estensione risulta lineare stack_push(s, Whale ); top top Don t Panic Trillian Don t Panic Trillian Whale II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 20 Pila come array: estensione dell array void stack_push_string(stack_string_stack s, string_item item) { void extend(stack_string_stack); assert(s!= NULL); if(s->top == s->size) extend(s); s->items[++s->top] = item; /* Controllare! */ void extend(stack_string_stack s) { intnew_size = s->size * 2; inti = 0; string_item * new_stack = (string_item *) ma loc(sizeof(string_item) * new_size); assert(new_stack); for(; i < s->size; i++) new_stack[i] = s->items[i]; /* Copia. */ s->size = new_size; free(s->items); s->items = new_items; II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 21 11
Pila: diverse implementazioni Qual è il rapporto costo/benefici di un implementazione (lista) rispetto all altra (stack)? Lista Cresce e si riduce armonicamente Ogni operazione richiede O(1) tempo La gestione dei puntatori e dei nodi richiede del tempo e della memoria aggiuntiva Array Quasi tutte le operazioni richiedono tempo costante Il raddoppio della memoria ogniqualvolta si riempie lo stack è un operazione costosa Ogni sequenza N di operazioni a partire da una pila vuota richiede tempo lineare Limite risultante da un analisi ammortizzata II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 22 Code Operazioni sulle code Enqueue inserisce un elemento nella coda Dequeue rimuove l elemento che sta da più tempo nella coda Is_empty controlla se la coda è vuota #include <stdio.h> intmain() { queue_string q = queue_new_string_queue(); queue_enqueue(q, Don t ); queue_enqueue(q, Panic! ); queue_enqueue(q, Trillian ); while(! queue_is_empty(q)) printf( %s\n, queue_string_dequeue(q)); II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 23 12
Coda come lista: implementazione in C Il file string-queue.h /* string-queue.h */ #include <string-item.h> struct_queue; typedef struct_queue * queue_string_queue; queue_string_queue queue_new_string_queue(); void queue_free(queue_string_queue); bool queue_is_em pty(queue_string_queue); string_item queue_dequeue_string(queue_string_queue); voidqueue_enqueue_string(queue_string_queue, string_item); /* end of file -- string-queue.h --*/ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 24 Coda come lista: enqueue last new_node la guida alla vita last new_node la guida alla vita last new_node la guida alla vita II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 25 13
Coda come lista: dequeue last la guida alla vita last la guida alla vita item = ->item _node = last la guida alla vita = ->next free(_node) returnitem II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 26 Coda come lista: implementazione in C Il file string-queue.c /* string-queue.c */ #include <string-queue.h> #include <assert.h> #include <stdio.h> struct_node { string_item item; struct_node * next; ; typedef struct_node * node; struct_queue { node ; node last; ; /* */ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 27 14
Coda come lista: implementazione in C Il file string-queue.c queue_string_queue queue_new_string_queue() { queue_string_queue new_q (queue_string_queue) malloc(sizeof(struct_queue)); assert(new_q); new_q-> = new_q->last = (node) NULL; return new_q; void queue_free(queue_string_queue q) { /* Esercizio. */ bool queue_is_empty(queue_string_queue q) { assert(q); return q-> == (node) NULL; /* */ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 28 Coda come lista: implementazione in C void queue_enqueue_string(queue_string_queue q, string_item s) { assert(q); { node new_node = (node) malloc(sizeof(struct_node)); assert(new_node); new_node->item = s; if(queue_is_empty(q)) { /* La prossima istruzione e` per paranoici. */ if(q->last!= (node) NULL) free(q->last); q-> = new_node; q->last = new_node; else{ q->last->next = new_node; q->last = new_node; /* */ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 29 15
Coda come lista: implementazione in C string_item queue_dequeue_string(queue_string_queue q) { assert(q); if(queue_is_empty(q)) { fprintf(stderr, Queue library: dequeueing from an empty queue.\n ); exit(1); else{ node _node = q->; string_item item = _node->item; q-> = _node->next; free(_node); returnitem; /* end of file -- queue_string.c */ II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 30 Pile: applicazioni Varie applicazioni di pile Parsing in un compilatore Bottone back in un web browser Linguaggio Postscript (stampanti) Chiamate di funzione II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 31 16
Chiamate di funzione Come fa il runtime di un linguaggio ad implementare le chiamate di funzione secondo le istruzioni del compilatore? Chiamata di funzione: il runtime del linguaggio push un record di attivazione sullo stack Ritorno: il runtime del linguaggio pop un record di attivazione dallo stack Chiamate ricorsive: una funzione chiama se stessa Nota: la ricorsione si può sempre eliminare gestendo esplicitamente uno stack (FORTRAN 77 e precedenti) II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 32 Un semplice calcolatore postfisso Notazione postfissa L operatore segue gli operandi Si usa uno stack per valutare un espressione Operando push sullo stack Operatore pop tanti operandi quanto necessario dallo stack Con la notazione postfissa non sono necessarie parentesi (o precedenze) di alcun tipo Il programma esempio prompt$ pcalc 40 2 + 2 * 10 + 10 * II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 33 17
Un semplice calcolatore postfisso #include <stdio.h> #include <stdlib.h>/* Per la funzione `atoi */ #include integer_stack.h intmain() { int_stack s = stack_new_int_stack(); charinput[80]; while(scanf( %s, input)!= EOF)) { if(!strcmp(input, + )) stack_push_int(s, stack_pop_int(s) + stack_pop_int(s)); elseif(!strcmp(input, * )) stack_push_int(s, stack_pop_int(s) * stack_pop_int(s)); /* altri operatori */ else stack_push_int(s, atoi(input)); printf( = %d\n, stack_pop_int(s)); II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 34 Riassunto Pile e Code sono tipi di dati astratti fondamentali Pile Implementazione con liste Implementazione con array Code Implementazione con liste Applicazioni Stack: calcolatore postfisso II Semestre 2005/2006 Laboratorio Algoritmi - Marco Antoniotti 35 18