Table of Contents

Tema 2 PP 2023

DEADLINE 28 aprilie 2023
  • Temele trebuie submise pe curs.upb.ro, in assignment-ul numit Tema 2.
  • Temele ce nu sunt acceptate de validatorul de arhive NU vor fi puncate.
  • Va sugeram ca dupa ce ati incarcat o arhiva, sa o descarcati si sa o testati cu validatorul de arhive.
Folosiți un stil de programare funcțional. NU se vor accepta:
  • Efecte laterale (de exemplu modificarea parametrilor dați ca input la funcție)
  • var (val este ok!)
Scheletul se poate găsi la: tema2_2023_pp-skel.zip
Validatorul de arhive se poate găsi la: archive_validator_hw2.zip
Formatul arhivelor este:
  • util/Util.scala
  • Table.scala
  • TestTables.scala
  • ID.txt - acest fisier va contine o singura linie, formata din ID-ul unic al fiecarui student

Numele arhivelor trebuie sa fie de forma <Nume>_<Prenume>_<Grupa>_T2.zip (daca aveti mai multe prenume sau nume, le puteti separa prin '-')

Scopul temei

Cerintele temei va vor ghida pentru a implementa operatii uzuale pe tabele, folosind tipuri de date definite de voi pentru a facilita implementarea.

In final, veti obtine un Query Language ce ofera posibilitatea de a aplica operatii complexe si de a manipula datele din tabele intr-un mod flexibil si usor de extins cu noi functionalitati.

Tabele

Un tabel este in esenta o matrice ce contine elemente de tip String, alaturi de cate un nume asociat fiecarei coloane, necesar pentru a descrie cu usurinta operatii peste acestea. In aceasta tema, vom folosi doua reprezentari pentru o linie a unei matrici, fiecare potrivita in contexte diferite:

// a row contains the column name and the value
type Row = Map[String, String]
// a line is just a row without column names
type Line = List[String]

1. Tipul de date Table si parsarea datelor (10p)

Pentru inceput, trebuie sa definim o clasa prin care sa modelam ce este un tabel.

Vom implementa clasa Table in acest sens:

class Table (column_names: Line, tabular: List[List[String]]) { 
    ???
}

Pentru a putea rula checkerul, este nevoie sa putem parsa input si output. Prin urmare, avem nevoie de o functie de citire si una de afisare a unui tabel.

1.1. (5p) Suprascrieti metoda toString a unui tabel, astfel incat sa puteti reprezenta un tabel in forma csv. Liniile vor fi separate prin \n, iar coloanele vor fi delimitate prin ,.

1.2. (5p) Definiti functia apply intr-un companion object al clasei Table. Functia trebuie sa parseze un sir de caractere si sa returneze un tabel.

def apply(s: String): Table = ???

2. Functii de procesare a datelor (50p)

In continuare, vom implementa operatii pe tabele, ca membri ai clasei Table.

2.1. (5p) Implementati functia select care selecteaza valorile din tabel asociate unor coloane date ca input. Daca operatia face referire la coloane inexistente, ea va intoarce None.

def select(columns: Line): Option[Table] = ???

2.2. (20p) Implementati functia filter care aplica un filtru pe liniile din tabel, fiind pastrate doar acele linii care verifica conditia. Daca conditia de filtrare face referiri la coloane inexistente, operatia va intoarce None.

def filter(cond: FilterCond): Option[Table] = ???

Pentru acest exercitiu, este nevoie sa implementam si filtrele ce se aplica pe o linie din tabel. Definim un filtru recursiv, astfel:

 
<filter> ::= <filter> && <filter> | <filter> || <filter> | (<column_name> <predicate>)

Unde <column_name> este un nume de coloana, iar <predicate> este un predicat ce poate sau nu sa fie satisfacut de valoarea de la coloana respectiva.

trait FilterCond {
  // these are useful for the intuitive infix notation
  // e.g. the following expression is a filter condition:
  // Field("PL", x=>true) && Field("PL", x=> false)
  def &&(other: FilterCond): FilterCond = ???
  def ||(other: FilterCond): FilterCond = ???
 
  // fails if the column name is not present in the row
  def eval(r: Row): Option[Boolean]
 
}
 
case class Field(colName: String, predicate: String => Boolean) extends FilterCond{
  override def eval(r: Row): Option[Boolean] = ???
}
 
case class And(f1: FilterCond, f2: FilterCond) extends FilterCond {
  override def eval(r: Row): Option[Boolean] = ???
}
 
case class Or(f1: FilterCond, f2: FilterCond) extends FilterCond {
  override def eval(r: Row): Option[Boolean] = ???
}

2.3. (5p) Implementati functia newCol ce adauga o noua coloana la finalul unui tabel, fiecare intrare in acea coloana asociata unei linii fiind completata cu o valoare default.

def newCol(name: String, defaultVal: String): Table = ???

2.4. (20p) Implementati functia merge care uneste 2 tabele. Identificarea liniilor ce trebuie combinate se va face folosing coloana comuna key. Daca exista suprapuneri intre valorile asociate unei chei in cele doua tabele, se vor pastra amandoua valorile (daca sunt diferite) sub forma unui singur sir de caractere (cele doua intrari vor fi separate prin ;), iar acolo unde nu exista corespondent in unul dintre tabele, coloanele ramase necompletate vor fi umplute cu un sir de caractere gol. Daca coloana key nu exista in vreunul din tabele, se va intoarce None.

def merge(key: String, other: Table): Option[Table] = ???

Exemplu

Exemplu

TABEL 1:
PL      Functional Description
Haskell yes        nice
Scala   yes        cool
Python  yes        good

TABEL 2:
PL      OO         Description
Scala   yes        cool
Python  yes        whoa

MERGE intre TABEL 1 si TABEL 2 cu cheia PL:
PL      Functional Description OO
Haskell yes        nice        ""
Scala   yes        cool        yes
Python  yes        good;whoa   yes

3. Query Language (40p)

Obiectivul acestui task este acela de a:

3.0. (25p) Veti implementa un Query Language ce reprezinta un API pentru o varietate de transformari de tabele, deja implementate anerior ca functii. Un Query este o secventa/ combinatie de astfel de transformari.

Query-urile sunt descrise recursiv astfel:

<query> ::=  <table>                               // query atomic - reprezinta tabelul initial pe care se va realiza query-ul.   
             | Select <column_list> <query>        //selecteaza o lista de coloane dintr-un tabel dat,
                                                   //esueaza daca se doreste o coloana ce nu se gaseste in tabel
             | NewCol <column_name> <query>        //creeaza o noua coloana cu nume dat intr-un tabel, completand coloana cu o valoare default
                                                   //operatia e posibila doar daca cheia se gaseste in ambele tabele
             | Merge <column_name> <query> <query> //combina 2 tabele pe baza unei chei comune
                                                   //operatia e posibila doar daca cheia se gaseste in ambele tabele
             | Filter <filter_cond> <query>        //aplica un filtru pe liniile tabelei

Spre exemplu, pentru a evalua query-ul: Select “PL” <Q>, se va evalua intai query-ul <Q> apoi se va efectua operatia Select pe rezultatul intors de Q. Scheletul de implementare pentru Query-uri se gaseste mai jos:

trait Query {
  def eval: Option[Table]
}
 
case class Value(t: Table) extends Query {
  override def eval: Option[Table] = ???
}
 
case class Select(columns: Line, target: Query) extends Query {
  override def eval: Option[Table] = ???
}
 
case class Filter(condition: FilterCond, target: Query) extends Query{
  override def eval: Option[Table] = ???
}
 
case class NewCol(name: String, defaultVal: String, target: Query) extends Query{
  override def eval: Option[Table] = ???
}
 
case class Merge(key: String, t1: Query, t2: Query) extends Query {
  override def eval: Option[Table] = ???
}

In finalul acestei teme, vom scrie propriul nostru Query, folosind tabelele Functional, Object-Oriented si Imperative, ce contin doar 3 coloane: “Language”, “Original purpose”, “Other paradigms”.

3.1. (5p) Creati tabelul TestTables.programmingLanguages1 combinand functiile newCol si merge:

3.2. (5p) Eliminati limbajele al caror scop original este “Application” si sunt “concurrent”. Rezultatul se va numi TestTables.programmingLanguages2.

3.3. (5p) Selectati doar coloanele “Language”, “Object-Oriented” si “Functional” din tabelul obtinut anterior. Rezultatul se va numi TestTables.programmingLanguages3.