15 giugno 2017 - laboratorio
Linguaggi e Traduttori
Prof. Marco Gavanelli
15 giugno 2017
Esercizio 1 (punti 20)
Una potente funzionalità dei programmi SpreadSheet moderni è quella delle pivot table. L'utente ha la possibilità di selezionare un insieme di dati, in cui ogni riga rappresenta un record con vari campi, rappresentati nelle colonne.
Ad esempio, un commerciante ambulante vende ortaggi in vari mercati della regione; ha creato la seguente tabella, che indica le vendite dei vari ortaggi nei mercati della regione:
Ortaggio | luogo | giorno | vendite |
carote | Ravenna | venerdi` | 100 |
patate | Ravenna | giovedi` | 50 |
carote | Bologna | lunedi` | 20 |
patate | Bologna | martedi` | 30 |
patate | Ferrara | mercoledi` | 10 |
patate | Ferrara | giovedi` | 20 |
carote | Ravenna | martedi` | 10 |
patate | Ferrara | lunedi` | 20 |
carote | Ferrara | lunedi` | 20 |
L'ambulante desidera sapere, per ogni coppia (Ortaggio, Luogo) quanto è stato venduto, ovvero desidera creare una tabella come segue (detta, appunto, Pivot Table):
Bologna | Ferrara | Ravenna | |
carote | 20 | 20 | 110 |
patate | 30 | 50 | 50 |
Per creare una pivot table, l'utente seleziona:
- i dati da mettere in riga nella pivot table: DatiRiga (nell'esempio, la colonna "Ortaggio")
- i dati da inserire in colonna nella pivot table: DatiColonna (nell'esempio, la colonna "Luogo")
- i dati da inserire nelle celle della pivot table: DatiCelle (nell'esempio, la colonna "vendite").
L'utente seleziona poi come aggregare i dati che vanno nella stessa cella della pivot table (nell'esempio, l'ambulante vuole sapere la somma delle vendite fatte di un dato ortaggio in un certo luogo, per cui selezionerà la funzione somma come aggregazione).
Il software di spreadsheet a questo punto crea una tabella in cui
- è presente una riga per ogni valore presente nella colonna DatiRiga (nell'esempio, la colonna selezionata è "ortaggio" e in tale colonna sono presenti due possibili valori: "patate" e "carote")
- è presente una colonna per ogni valore presente nella colonna DatiColonna (nell'esempio, la colonna selezionata era la colonna "luogo", per cui i valori possibili sono "Ravenna", "Ferrara" e "Bologna").
- per ogni coppia (DatoRiga,DatoColonna) (dove DatoRiga è un valore presente in DatiRiga e DatoColonna è uno dei valori presenti in DatiColonna) è presente una cella che contiene l'aggregazione di tutti i dati della tabella di dati originari (Nell'esempio, la tabella 1),
Per comprendere meglio l'esempio, si può utilizzare il file EsempioPivot.ods , aprirlo con uno spreadsheet, selezionare tutti i dati, poi
- insert → pivot Table
- poi trascinare nel riquadro Row Fields il campo Ortaggio,
- poi trascinare nel riquadro Column Fields il campo luogo,
- poi trascinare nel riquadro Data Fields il campo vendite,
- infine cliccare ok e viene creata la Pivot Table.
Si crei una funzione Haskell pivot che crea una pivot table rappresentata come lista di liste (da intendersi come matrice). La funzione prende come parametri:
- una lista l
- tre funzioni, che servono a selezionare, della lista l
- il dato da mettere in riga
- il dato da mettere in colonna
- il dato da inserire nelle celle
- la funzione di aggregazione
- (A scelta dello studente): l'elemento neutro della funzione di aggregazione (se la funzione di aggregazione è la somma, si può inserire 0 in questo argomento).
L'interfaccia sarà dunque:
pivot :: [a] -> (a -> a1) -> (a -> a2) -> (a -> a3) -> (a3 -> b -> b) -> b -> [[b]]
Ad esempio, se i dati nella tabella 1 sono rappresentati in Haskell con il tipo
data Ambulante = Ambulante {ortaggio::String, luogo::String, giorno::String, vendite::Int}
ed inseriti in una lista chiamata lista, ad es:
let lista = [(Ambulante "carote" "Ravenna" "venerdi`" 100),(Ambulante "patate" "Ravenna" "giovedi`" 50),...]}
allora l'ambulante potrà invocare
pivot lista ortaggio luogo vendite (+) 0
ed ottenere:
[[20,20,110],[30,50,50]]
Se invece si volesse il prodotto come funzione di aggregazione:
Prelude> pivot lista ortaggio luogo vendite (*) 1 [[20,20,1000],[30,4000,50]]
Soluzione
pivot l sx sy sz agg null =
let valx = remdup (map sx l)
valy = remdup (map sy l)
in map (create_row l sx sy sz valy agg null) valx
create_row l sx sy sz ys agg null vx =
map (create_item l sx sy sz agg null vx) ys
create_item ls sx sy sz agg null vx vy =
let list = [ sz k | k <- ls, (sx k) == vx, (sy k) == vy]
in foldr agg null list
-- rimozione duplicati con quicksort
remdup [] = []
remdup (x:xs) =
(remdup [y| y<-xs, y<x]) ++ (x:(remdup [y| y<-xs, y>x]))