Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
pp:2026:scala:t01 [2026/03/09 19:52]
ldaniel
pp:2026:scala:t01 [2026/03/25 19:19] (current)
ldaniel typo
Line 1: Line 1:
 ===== Tema 1 PP – Casa de marcat ===== ===== Tema 1 PP – Casa de marcat =====
-Publicare: ''​?? martie 2026''​\\ +Publicare: ''​25 martie 2026''​\\ 
-Deadline: ''​?? aprilie 2026''​+Deadline: ''​17 aprilie 2026''​
  
-Schelet de cod: {{:pp:2025:scala:tema1_pp_2025.zip|}} +Schelet de cod: {{:pp:2026:scala:tema1_pp_2026_skel_v1.zip|}}
- +
-După ce ați descărcat scheletul, creați un proiect nou din IntelliJ în Scala, în care copiați folderele și fișierul din arhivă în root-ul proiectului (folderul ''​src''​ va fi suprascris). Dați restart la IDE și ar trebui să funcționeze.+
  
 <note warning>​După deadline fiecare student va prezenta tema la laborator, explicând în detaliu implementarea.</​note>​ <note warning>​După deadline fiecare student va prezenta tema la laborator, explicând în detaliu implementarea.</​note>​
Line 11: Line 9:
 <note important>​În soluție nu aveți voie să programați cu efecte laterale. Folosiți declarații doar ''​val'',​ nu <​del>​var</​del>​.</​note>​ <note important>​În soluție nu aveți voie să programați cu efecte laterale. Folosiți declarații doar ''​val'',​ nu <​del>​var</​del>​.</​note>​
  
-[Obiectivul acestei teme este implementarea unui program care simuleaza functionarea unei case de marcat, proces care consta in scanarea unor produse si adaugarea acestora ​pe o lista pentru calculul sumei de platit. Pentru a implementa acest proiect, avem nevoie de doua module importante: modulul de decodificare a codului de bare de pe produs ​si modulul de baza de date, care stocheaza ​informatiile despre denumirea, pretul si cantitatea produselor disponibile.]+Obiectivul acestei teme este implementarea unui program care simuleaza functionarea unei case de marcat, proces care consta in scanarea unor produse si adaugarea acestora ​in cosul de cumparaturi. Pentru a implementa acest proiect, avem nevoie de trei module importante
 +  * Partea 1: modulul de decodificare a codului de bare de pe produs 
 +  * Partea 2: modulul de tabele folosit pentru a stoca informatiile despre ​produse, cum at fi denumirea, pretul si codul de bare 
 +  * Partea 3: operatii uzuale de gestionare a cosului de cumparaturi,​ de exemplu adaugarea de produse, stergerea sau modificarea cantitatii 
 ===== 1. Coduri de bare ===== ===== 1. Coduri de bare =====
 Un cod de bare este o reprezentare vizuală a datelor, ușor de citit de dispozitive. De obicei datele descriu proprietăți ale obiectului pe care se află codul de bare. Un cod de bare este o reprezentare vizuală a datelor, ușor de citit de dispozitive. De obicei datele descriu proprietăți ale obiectului pe care se află codul de bare.
Line 43: Line 45:
  
 Pentru fiecare cifră din al doilea grup (pozițiile 8->13) există o singură codificare posibilă (codificare **R** paritate ''​pară''​ ). Pentru fiecare cifră din al doilea grup (pozițiile 8->13) există o singură codificare posibilă (codificare **R** paritate ''​pară''​ ).
 +<hidden Tabelul codificarilor L, G si R pentru cifre>
 ^ Cifra ^ Codificare-L ^ Codificare-G ^ Codificare-R^ ^ Cifra ^ Codificare-L ^ Codificare-G ^ Codificare-R^
 | **0** | **0001101** | **0100111** | **1110010**| | **0** | **0001101** | **0100111** | **1110010**|
Line 55: Line 57:
 | **8** | **0110111** | **0001001** | **1001000**| | **8** | **0110111** | **0001001** | **1001000**|
 | **9** | **0001011** | **0010111** | **1110100**| | **9** | **0001011** | **0010111** | **1110100**|
 +
 +</​hidden>​
 +
 Observații:​ Observații:​
   * Codul de bare începe cu o cifră codificată ''​impar''​ și se termină cu o cifră codificată ''​par'',​ astfel scannerele pentru codurile de bare pot determina orientarea și să citească și de la stânga la dreapta, dar și invers.   * Codul de bare începe cu o cifră codificată ''​impar''​ și se termină cu o cifră codificată ''​par'',​ astfel scannerele pentru codurile de bare pot determina orientarea și să citească și de la stânga la dreapta, dar și invers.
Line 64: Line 69:
 După cum am spus anterior, prima cifră se numește cifră de paritate. ​ După cum am spus anterior, prima cifră se numește cifră de paritate. ​
 Cifra de paritate ''​nu''​ este reprezentată direct printr-o succesiune de bare și spații, ci este codificată indirect, prin alegerea unei combinații de moduri de codificare L sau G pentru primul grup de 6 cifre, conform tabelului de mai jos. Practic, este suficient să știm ce codificare a fost folosită pentru fiecare dintre cele 6 cifre din primul grup pentru a determina cifra de paritate asociată. Dacă combinația găsită nu este asociată cu o cifră din tabelul de mai jos codul este invalid. Cifra de paritate ''​nu''​ este reprezentată direct printr-o succesiune de bare și spații, ci este codificată indirect, prin alegerea unei combinații de moduri de codificare L sau G pentru primul grup de 6 cifre, conform tabelului de mai jos. Practic, este suficient să știm ce codificare a fost folosită pentru fiecare dintre cele 6 cifre din primul grup pentru a determina cifra de paritate asociată. Dacă combinația găsită nu este asociată cu o cifră din tabelul de mai jos codul este invalid.
 +
 +<hidden Tabelul codificarilor in functie de cifra de paritate>​
 +
 ^ Cifra de paritate ^ Primul grup ^ ^ Cifra de paritate ^ Primul grup ^
 | **0** | **LLLLLL**| | **0** | **LLLLLL**|
Line 76: Line 84:
 | **9** | **LGGLGL**| | **9** | **LGGLGL**|
  
 +</​hidden>​
  
 === Calculare cifră de control === === Calculare cifră de control ===
Line 107: Line 116:
 | Cifra | 5 | 9 | 0 | 1 | 2 | 3 | 4 | 1 | 2 | 3 | 4| 5 | | Cifra | 5 | 9 | 0 | 1 | 2 | 3 | 4 | 1 | 2 | 3 | 4| 5 |
 Suma produselor dintre cifre si ponderile asociate este 83. Cifra de contol este (10 - (83 % 10)) % 10 = 7, deci este corectă. Suma produselor dintre cifre si ponderile asociate este 83. Cifra de contol este (10 - (83 % 10)) % 10 = 7, deci este corectă.
-======  Implementare ​====== +====  Implementare ====
  
 Avem imagini cu codurile de bare (în format **.ppm**) ale unor produse care trebuie cumpărate. Veți implementa un decodificator simplificat de coduri de bare. Cu ajutorul acestuia magazinele obțin numărul EAN-13 si cu ajutorul bazei de date identifică produsul vândut și calculează prețul.\\ Avem imagini cu codurile de bare (în format **.ppm**) ale unor produse care trebuie cumpărate. Veți implementa un decodificator simplificat de coduri de bare. Cu ajutorul acestuia magazinele obțin numărul EAN-13 si cu ajutorul bazei de date identifică produsul vândut și calculează prețul.\\
Line 121: Line 130:
 </​code>​ </​code>​
  
-=== Algoritm ===+<hidden Explicarea in detaliu a algoritmului>​  
 +**1. Transformarea imaginilor** 
 Primim o matrice de biți 0 și 1, reprezentând imaginea în formatul alb-negru descris anterior. Decupăm din matricea de pixeli rândurile din mijloc și rulăm algoritmul de identificare a barelor pe toate aceste rânduri, pentru a avea șanse mai mari de succes. Vrem să găsim barele din imagine, așa că grupăm biții identici și consecutivi în tupluri de tipul **(<​număr_repetări>,​ <​bit>​)**,​ unde tuplul descrie grosimea și culoarea barei din imagine. Va rezulta următorul comportament:​\\ Primim o matrice de biți 0 și 1, reprezentând imaginea în formatul alb-negru descris anterior. Decupăm din matricea de pixeli rândurile din mijloc și rulăm algoritmul de identificare a barelor pe toate aceste rânduri, pentru a avea șanse mai mari de succes. Vrem să găsim barele din imagine, așa că grupăm biții identici și consecutivi în tupluri de tipul **(<​număr_repetări>,​ <​bit>​)**,​ unde tuplul descrie grosimea și culoarea barei din imagine. Va rezulta următorul comportament:​\\
 <​nowiki>​ <​nowiki>​
Line 127: Line 138:
 </​nowiki>​ </​nowiki>​
 \\  \\ 
 +
 +**2. Identificarea unui posibil cod de bare**
  
 Căutăm o secvență de 59 de bare pentru fiecare rând din imagine în care identificăm secvențele de start și de stop.\\ Căutăm o secvență de 59 de bare pentru fiecare rând din imagine în care identificăm secvențele de start și de stop.\\
 Secvențele de 59 de bare care conțin secvențele de start și stop din fiecare rând sunt returnate de funcția ''​checkRow'',​ a cărei implementare este dată de echipă.\\ Secvențele de 59 de bare care conțin secvențele de start și stop din fiecare rând sunt returnate de funcția ''​checkRow'',​ a cărei implementare este dată de echipă.\\
 +
 +**3. Identificare cifre**
  
 Din secvența de 59 de bare extragem grupuri de 4 bare care reprezintă codificarea unei cifre (după ce am eliminat secvențele de bare care marchează start, centru și sfârșit). Pentru primele 6 cifre găsim codificarea cea mai apropiată L sau G, iar pentru ultimele 6 cifre găsim cea mai apropiată codificare R. Din secvența de 59 de bare extragem grupuri de 4 bare care reprezintă codificarea unei cifre (după ce am eliminat secvențele de bare care marchează start, centru și sfârșit). Pentru primele 6 cifre găsim codificarea cea mai apropiată L sau G, iar pentru ultimele 6 cifre găsim cea mai apropiată codificare R.
 +
 +Pentru a putea face acest lucru, avem nevoie sa masuram cat de aproape este codificarea obtinuta din poza cu codificarea reala. O cifra e reprezentata prin 4 bare, fiecare bara avand o anumita latime. Se poate ca in realitate, patternul pe care il intalnim sa nu se potriveasca perfect cu niciuna dintre reprezentari. Priviti urmatorul exemplu (codificarea L a cifrei 4: 0100011):
 +<​code>​
 +░░░░ = 0
 +████ = 1
 +░░░░████░░░░░░░░░░░░████████ = Codificare L pentru cifra 4
 +░░░░█████░░░░░░░░░░█████████ = Codificare citita de fapt
 +</​code>​
 +
 +Codificarea pe care am citit-o de fapt difera putin de cea a cifrei 4. Cat de mare este aceasta diferenta va fi masurata de distanta. Va va fi explicata formula ei de calcul in exercitii.
  
 Pentru a identifica cifra codificată de un set de 4 bare vom calcula distanța dintre secvența de bare din poză și secvențele de bare pentru codificările L, G sau R. Vom alege codificarea cea mai apropiată (de distanță minimă) pentru fiecare din cele 12 cifre. Pentru a identifica cifra codificată de un set de 4 bare vom calcula distanța dintre secvența de bare din poză și secvențele de bare pentru codificările L, G sau R. Vom alege codificarea cea mai apropiată (de distanță minimă) pentru fiecare din cele 12 cifre.
 +
 +**4. Calcul lungimi relative**
  
 De la formatul descris pentru bare vom lua grupe de câte 4 bare care reprezintă o cifră și vom trece la formatul dimensiunilor relative ale segmentelor,​ impărțind numărul de aparitii la lungimea totală.\\ De la formatul descris pentru bare vom lua grupe de câte 4 bare care reprezintă o cifră și vom trece la formatul dimensiunilor relative ale segmentelor,​ impărțind numărul de aparitii la lungimea totală.\\
Line 145: Line 172:
 </​nowiki>​\\ </​nowiki>​\\
  
-Având codificările standard ale cifrelor în același format, vom alege codificarea care începe ​cu același bit și pentru ​care distanța ​(suma modulelor diferențelor dintre rapoartele aflate pe aceeași poziție) ​este minimă, adică ​codificarea pentru ​care barele respectă proporții ​de grosime similare.+Tinand seama de bitul cu care incepe codificarea rezultata **din poza** ​(in exemplul de mai sus este 0) vom alege codificarea ​cea mai similara ​pentru ​o cifra bazat pe distanta cea mai mica (vezi explicatia ​de la pasul 3). 
 + 
 +**5. Cifra de paritate si cifra de control**
  
 După ce am identificat ultimele 12 cifre ale codului de bare, vom determina cifra de paritate. Pentru a determina cifra de paritate ne vom uita la paritatea codificărilor identificate pentru cele 6 cifre din primul grup.   ​[[#​cifra_de_paritate| vezi mai sus]]. După ce am identificat ultimele 12 cifre ale codului de bare, vom determina cifra de paritate. Pentru a determina cifra de paritate ne vom uita la paritatea codificărilor identificate pentru cele 6 cifre din primul grup.   ​[[#​cifra_de_paritate| vezi mai sus]].
Line 152: Line 181:
  
 Dacă cifra de control este corectă, am identificat toate cifrele și întoarcem rezultatul. Altfel întoarcem None. Dacă cifra de control este corectă, am identificat toate cifrele și întoarcem rezultatul. Altfel întoarcem None.
 +
 +</​hidden>​
 +
 <hidden Recapitulare functii de ordin superior>​ <hidden Recapitulare functii de ordin superior>​
  
Line 212: Line 244:
 **Notă:** Poate fi rescris ca <code scala> list.foldRight(0)(_-_) </​code>​ **Notă:** Poate fi rescris ca <code scala> list.foldRight(0)(_-_) </​code>​
 </​hidden>​ </​hidden>​
-=====  1.1. Funcții elementare (20p) =====  +====  1.1. Funcții elementare (20p) ====  
-1.1. Avem nevoie de un TDA pentru biți. Realizați conversiile de la ''​Char''​ și ''​Int''​ la ''​Bit''​.+1.1.1. Avem nevoie de un TDA pentru biți. Realizați conversiile de la ''​Char''​ și ''​Int''​ la ''​Bit''​.
  
 <code scala> <code scala>
Line 222: Line 254:
 </​code>​ </​code>​
  
-''​Hint:''​ Inspectați fișierul **Types**.+''​Hint:''​ Inspectați fișierul **Types**. Mai multe despre conversii implicite [[https://​docs.scala-lang.org/​tour/​implicit-conversions.html|aici]].
  
-1.2. Extindeți tipul Bit cu funcția care întoarce complementul unui bit.+1.1.2. Extindeți tipul Bit cu funcția care întoarce complementul unui bit.
 <code scala> <code scala>
 extension(c:​Bit) extension(c:​Bit)
Line 230: Line 262:
 </​code>​ </​code>​
  
-1.3. Pornind de la ''​LStrings''​ care este lista codificărilor L ca ''​String''​ (pe fiecare poziție gasim codificarea sa L), definiți listele pentru toate cele 3 tipuri de codificări. **Soluțiile hardcodate nu vor fi luate în considerare,​ trebuie să vă folosiți de LStrings sau de listele deja create (pentru rightOddList și leftEvenList) împreună cu funcții de ordin superior.**+1.1.3. Pornind de la ''​LStrings''​ care este lista codificărilor L ca ''​String''​ (pe fiecare poziție gasim codificarea sa L), definiți listele pentru toate cele 3 tipuri de codificări. **Soluțiile hardcodate nu vor fi luate în considerare,​ trebuie să vă folosiți de LStrings sau de listele deja create (pentru rightOddList și leftEvenList) împreună cu funcții de ordin superior.**
  
 <code scala> <code scala>
Line 238: Line 270:
 </​code>​ </​code>​
  
-1.4. Extindeți tipul List cu funcția ''​groupedByEquality''​ care grupează elementele egale și consecutive dintr-o listă în liste separate.+1.1.4. Extindeți tipul List cu funcția ''​groupedByEquality''​ care grupează elementele egale și consecutive dintr-o listă în liste separate.
  
 <code scala> <code scala>
Line 247: Line 279:
 ''​Hint:''​ **<​nowiki>​group([1,​ 1, 2, 2, 3, 3, 1]) = [[1, 1], [2, 2], [3, 3], [1]]</​nowiki>​** ''​Hint:''​ **<​nowiki>​group([1,​ 1, 2, 2, 3, 3, 1]) = [[1, 1], [2, 2], [3, 3], [1]]</​nowiki>​**
  
-1.5. Implementați funcția ''​runLength''​ care grupează elementele egale și consecutive dintr-o listă în elemente noi de tipul **(<​număr_apariții>,​ <​element>​)**.+1.1.5. Implementați funcția ''​runLength''​ care grupează elementele egale și consecutive dintr-o listă în elemente noi de tipul **(<​număr_apariții>,​ <​element>​)**.
  
 <code scala> <code scala>
Line 255: Line 287:
 ''​Hint:''​ Folosiți ''​group''​ implementat anterior. ''​Hint:''​ Folosiți ''​group''​ implementat anterior.
  
-=====  1.2. Numere raționale (10p) ===== +====  1.2. Numere raționale (10p) ==== 
  
-2.1. Vrem să folosim tipul ''​RatioInt''​ pentru lucrul cu fracții. Implementați operațiile uzuale cu fracții în clasa ''​RatioInt''​.+1.2.1. Vrem să folosim tipul ''​RatioInt''​ pentru lucrul cu fracții. Implementați operațiile uzuale cu fracții în clasa ''​RatioInt''​.
  
 <code scala> <code scala>
Line 266: Line 298:
 </​code>​ </​code>​
  
-2.2. Implementați funcția de comparare a doua fracții. Întoarce **-1** daca fracția e mai mică decât **other**, **1** dacă e mai mare și **0** dacă sunt egale.+1.2.2. Implementați funcția de comparare a doua fracții. Întoarce **-1** daca fracția e mai mică decât **other**, **1** dacă e mai mare și **0** dacă sunt egale.
 <code scala> <code scala>
 def compare(other:​ RatioInt): Int = ??? def compare(other:​ RatioInt): Int = ???
 </​code>​ </​code>​
  
-===== 1.3. Transformarea inputului (30p) ===== +==== 1.3. Transformarea inputului (30p) ==== 
  
-3.1. Implementați funcția care primește o lista de elemente de tipul **(<​număr_apariții>,​ <​element>​)** și întoarce o listă cu elemente de tipul **(<​frecvență relativă>,​ <​element>​)**,​ unde+1.3.1. Implementați funcția care primește o lista de elemente de tipul **(<​număr_apariții>,​ <​element>​)** și întoarce o listă cu elemente de tipul **(<​frecvență relativă>,​ <​element>​)**,​ unde
 $math[\text{frecvență relativă}=\dfrac{\text{număr apariții}}{\text{număr total de elemente}}] $math[\text{frecvență relativă}=\dfrac{\text{număr apariții}}{\text{număr total de elemente}}]
 , unde elementele sunt cele din lista inițială pe care am rulat ''​runLength''​. , unde elementele sunt cele din lista inițială pe care am rulat ''​runLength''​.
Line 280: Line 312:
 </​code>​ </​code>​
  
-3.2. Implementați funcția care primeste o listă de biți și întoarce un tuplu format din primul bit și o listă cu dimensiunile relative ale segmentelor de biți egali.+1.3.2. Implementați funcția care primeste o listă de biți și întoarce un tuplu format din primul bit și o listă cu dimensiunile relative ale segmentelor de biți egali.
 <code scala> <code scala>
 def scaledRunLength(l:​ List[(Int, Bit)]): (Bit, List[RatioInt]) = ??? def scaledRunLength(l:​ List[(Int, Bit)]): (Bit, List[RatioInt]) = ???
 </​code>​ </​code>​
  
-3.3. Pornind de la un șir de caractere ce reprezintă parități, unde **G** înseamnă par, iar **L** impar, convertiți-l la ''​List[Parity]''​.+1.3.3. Pornind de la un șir de caractere ce reprezintă parități, unde **G** înseamnă par, iar **L** impar, convertiți-l la ''​List[Parity]''​.
 <code scala> <code scala>
 def toParities(s:​ Str): List[Parity] = ??? def toParities(s:​ Str): List[Parity] = ???
 </​code>​ </​code>​
  
-3.4. Creați lista de parițăți pe care o vom folosi pornind de la ''​PStrings''​. **Soluțiile hardcodate nu vor fi luate în considerare,​ trebuie să vă folosiți de PStrings**+1.3.4. Creați lista de parițăți pe care o vom folosi pornind de la ''​PStrings''​. **Soluțiile hardcodate nu vor fi luate în considerare,​ trebuie să vă folosiți de PStrings**
 <code scala> <code scala>
 val leftParityList:​ List[List[Parity]] = Nil val leftParityList:​ List[List[Parity]] = Nil
 </​code>​ </​code>​
  
-3.5. Rulați ''​scaledRunLength''​ pe fiecare element din listele create la task-ul **1.3**. **Soluțiile hardcodate nu vor fi luate în considerare,​ trebuie să vă folosiți de listele create anterior.**+1.3.5. Rulați ''​scaledRunLength''​ pe fiecare element din listele create la task-ul **1.3**. **Soluțiile hardcodate nu vor fi luate în considerare,​ trebuie să vă folosiți de listele create anterior.**
 <code scala> <code scala>
 type SRL = (Bit, List[RatioInt]) type SRL = (Bit, List[RatioInt])
Line 305: Line 337:
 ''​Hint:''​ Folosiți și ''​runLength''​. ''​Hint:''​ Folosiți și ''​runLength''​.
  
-=====  1.4. Identificarea cifrelor (40p) =====  +====  1.4. Identificarea cifrelor (40p) ====  
-4.1. Definiți funcția de distanță dintre două codificări,​ care primește tupluri cu+1.4.1. Definiți funcția de distanță dintre două codificări,​ care primește tupluri cu
 primul bit, respectiv lungimile relative ale segmentelor unei codificări (tip definit ca ''​SRL''​) și întoarce primul bit, respectiv lungimile relative ale segmentelor unei codificări (tip definit ca ''​SRL''​) și întoarce
 suma modulelor diferențelor dintre rapoartele aflate pe aceeași poziție în ambele codificări. suma modulelor diferențelor dintre rapoartele aflate pe aceeași poziție în ambele codificări.
Line 318: Line 350:
 ''​Hint:''​ Folosiți ''​zip''​ între listele primite. ''​Hint:''​ Folosiți ''​zip''​ între listele primite.
  
-4.2. Definiți funcția ''​bestMatch''​ care primește ca parametrii o listă de codificări L, G sau R în forma ''​SRL''​+1.4.2. Definiți funcția ''​bestMatch''​ care primește ca parametrii o listă de codificări L, G sau R în forma ''​SRL''​
 și codificarea binară a unei cifre și găsește cea mai bună potrivire din listă. Întoarce un tuplu cu cea mai mică distanță și cifra găsită. și codificarea binară a unei cifre și găsește cea mai bună potrivire din listă. Întoarce un tuplu cu cea mai mică distanță și cifra găsită.
 <code scala> <code scala>
Line 325: Line 357:
 ''​Hint:''​ Folosiți ''​zip''​ și ''​min''​. ''​Hint:''​ Folosiți ''​zip''​ și ''​min''​.
  
-4.3. Întoarce paritatea și cea mai bună potrivire pentru o cifră din grupul din stânga (cifrele 2-7).+1.4.3. Întoarce paritatea și cea mai bună potrivire pentru o cifră din grupul din stânga (cifrele 2-7).
 <code scala> <code scala>
 def bestLeft(digitCode:​ SRL): (Parity, Digit) = ??? def bestLeft(digitCode:​ SRL): (Parity, Digit) = ???
Line 331: Line 363:
 ''​Hint:''​ Folosiți-vă de ''​leftOddSRL''​ și ''​leftEvenSRL''​. ''​Hint:''​ Folosiți-vă de ''​leftOddSRL''​ și ''​leftEvenSRL''​.
  
-4.4. Întoarce cea mai bună potrivire pentru o cifră din grupul din dreapta (ultimele 6 cifre). Pentru codificările R vom folosi tipul NoParity.+1.4.4. Întoarce cea mai bună potrivire pentru o cifră din grupul din dreapta (ultimele 6 cifre). Pentru codificările R vom folosi tipul NoParity.
 <code scala> <code scala>
 def bestRight(digitCode:​ SRL): (Parity, Digit) = ??? def bestRight(digitCode:​ SRL): (Parity, Digit) = ???
Line 337: Line 369:
 ''​Hint:''​ Folosiți-vă de ''​rightSRL''​. ''​Hint:''​ Folosiți-vă de ''​rightSRL''​.
  
-4.5. Primiți ca argument rezultatul lui ''​runLength''​ pe o listă de biți. Trebuie să verificați că secvența dată are lungimea de 59 (vedeți în secțiunea [[#​Algoritm|Algoritm]] de ce).+1.4.5. Primiți ca argument rezultatul lui ''​runLength''​ pe o listă de biți. Trebuie să verificați că secvența dată are lungimea de 59 (vedeți în secțiunea [[#​Algoritm|Algoritm]] de ce).
 Delimitați primul grup (stânga) și al doilea grup (dreapta) folosindu-vă de ''​chunksOf''​. Ignorați verificarea pentru secvențele de start, centru și sfârșit (a fost făcută la checkRow). Pentru fiecare grup de 4 bare găsiți cea mai potrivită cifră. Reuniți rezultatele din cele două liste intr-una singură. Rezultatul va avea 12 cifre. Delimitați primul grup (stânga) și al doilea grup (dreapta) folosindu-vă de ''​chunksOf''​. Ignorați verificarea pentru secvențele de start, centru și sfârșit (a fost făcută la checkRow). Pentru fiecare grup de 4 bare găsiți cea mai potrivită cifră. Reuniți rezultatele din cele două liste intr-una singură. Rezultatul va avea 12 cifre.
  
Line 345: Line 377:
 ''​Hint:''​ Folosiți funcțiile drop și take pe listă pentru a separa grupurile de câte 6 cifre din partea stângă și din partea dreaptă. Va trebui să ignorați primele 3 bare, 5 bare de la mijloc și ultimele 3 bare. ''​Hint:''​ Folosiți funcțiile drop și take pe listă pentru a separa grupurile de câte 6 cifre din partea stângă și din partea dreaptă. Va trebui să ignorați primele 3 bare, 5 bare de la mijloc și ultimele 3 bare.
  
-4.6. Găsește prima cifră din codul de bare pe baza parităților cifrelor din primul grup (cel din stânga). Pentru prima cifră vom considera paritatea NoParity.+1.4.6. Găsește prima cifră din codul de bare pe baza parităților cifrelor din primul grup (cel din stânga). Pentru prima cifră vom considera paritatea NoParity.
 <code scala> <code scala>
 def firstDigit(l:​ List[(Parity,​ Digit)]): Option[Digit] = ??? def firstDigit(l:​ List[(Parity,​ Digit)]): Option[Digit] = ???
Line 351: Line 383:
 ''​Hint:''​ Puteți folosi ''​zipWithIndex''​ și ''​leftParityList''​. ''​Hint:''​ Puteți folosi ''​zipWithIndex''​ și ''​leftParityList''​.
  
-4.7. Calculează cifra de control (ultima cifră) pe baza primelor 12 cifre dintr-un cod de bare.+1.4.7. Calculează cifra de control (ultima cifră) pe baza primelor 12 cifre dintr-un cod de bare.
 <code scala> <code scala>
 def checkDigit(l:​ List[Digit]):​ Digit = ??? def checkDigit(l:​ List[Digit]):​ Digit = ???
Line 358: Line 390:
 ''​Hint:''​ Găsiți formula [[#​Calculare cifră de control|aici]]. ''​Hint:''​ Găsiți formula [[#​Calculare cifră de control|aici]].
  
-4.8. Pentru un cod de bare dat verificați dacă este valid. Funcția primește o listă cu perechi de (Paritate, Cifra) și verifică dacă sunt 13 cifre și dacă cifra de paritate și cifra de control sunt corecte.+1.4.8. Pentru un cod de bare dat verificați dacă este valid. Funcția primește o listă cu perechi de (Paritate, Cifra) și verifică dacă sunt 13 cifre și dacă cifra de paritate și cifra de control sunt corecte.
 <code scala> <code scala>
 def verifyCode(code:​ List[(Parity,​ Digit)]): Option[String] = ??? def verifyCode(code:​ List[(Parity,​ Digit)]): Option[String] = ???
Line 364: Line 396:
 ''​Hint:''​ Folosiți ''​firstDigit''​ și ''​checkDigit''​. ''​Hint:''​ Folosiți ''​firstDigit''​ și ''​checkDigit''​.
  
-4.9. Definiți funcția de solve care primește rezultatul lui ''​runLength''​ și întoarce cele 13 cifre reprezentate de codul de bare, dacă potrivirea găsită este un cod valid.+1.4.9. Definiți funcția de solve care primește rezultatul lui ''​runLength''​ și întoarce cele 13 cifre reprezentate de codul de bare, dacă potrivirea găsită este un cod valid.
 <code scala> <code scala>
 def solve(rle: ​ List[(Int, Bit)]): Option[String] = ??? def solve(rle: ​ List[(Int, Bit)]): Option[String] = ???
Line 370: Line 402:
 ''​Hint:''​ Folosiți funcțiile ''​findLast12Digits'',​ ''​firstDigit''​ si ''​verifyCode''​. ''​Hint:''​ Folosiți funcțiile ''​findLast12Digits'',​ ''​firstDigit''​ si ''​verifyCode''​.
  
-[===== 2. Baza de date =====]+===== 2. Baza de date ===== 
 +Vom implementa o mica baze de date pentru un supermarket,​ sub forma unui tabel.
  
-=====  ​Testare ​=====  +==== Reprezentarea Tabelelor ​====
-În folderul src/test din proiect se află Test.scala, care poate fi rulat din IntelliJ apăsând pe butonul de Run din partea stângă a codului. Fiecare test are un buton propriu și poate fi rulat separat. Există teste pentru cele mai importante funcții, vă recomandăm să testați funcțiile de bază înainte de a începe implementarea task-urilor avansate.+
  
-La rularea testelor veți primi și un punctajdupă specificațiile din enunț.+Considerati exemplul de mai jos. 
 +^ Nume ^ Prenume ^ Varsta ^ 
 +| Popescu | Ion | 30 | 
 +| Ionescu | Maria | 25 | 
 +Acest tabel poate fi reprezentat ca: 
 + 
 +<code scala> 
 +type Row = Map[String, String] // nume_coloana - valoare 
 +type Tabular = List[Row] 
 +</​code>​ 
 + 
 +<hidden Funcții utile pentru lucrul cu Map> 
 +<code scala> 
 +let map = Map(1 -> 2, 3 -> 4): Map[Int, Int] 
 +</​code>​ 
 +  * Adauga o noua pereche cheie-valoare  
 +<code scala> 
 +map + (5 -> 6)  // Map(1 -> 2, 3 -> 4, 5 -> 6) 
 +map + (3 -> 5)  // Map(1 -> 2, 3 -> 5)  -- if key exists, it updates the value 
 +</​code>​ 
 +  * Eliminarea unei perechi cheie-valoare 
 +<code scala> 
 +map - (3 -> 4)  // Map(1 -> 2) 
 +</​code>​ 
 +  * Accesarea valorii asociate unei chei 
 +<code scala> 
 +map get 1  // return 2 
 +map get 3  // return 4 
 +map getOrElse (1, 0)  // return 2 
 +map getOrElse (5, 0)  // return 0 - daca cheia nu exista, returneaza valoarea default 
 +map contains 1  // True 
 +map contains 5  // False 
 +</​code>​ 
 +  * Functii de orin superior 
 +<code scala> 
 +map mapValues (x => x + 5)  // Map(1 -> 7, 2 -> 9) 
 +map filterKeys (x => x <= 2)  // Map(1 -> 2) 
 +</​code>​ 
 +    * Combinarea a doua map-uri 
 +<code scala> 
 +val map1: Map[Int, Int] = Map(1 -> 2, 3 -> 4) 
 +val map2: Map[Int, Int] = Map(5 -> 6, 7 -> 8) 
 +map ++ map2  // Map(1 -> 2, 3 -> 4, 5 -> 6, 7 -> 8) 
 +</​code>​ 
 +</​hidden>​  
 + 
 +==== Clasa Table ==== 
 + 
 +Vom defini ​un tabel ca o clasa care are ca atribute numele tabelei ''​tableName''​ si datele ''​tableData''​. 
 + 
 +<code scala> 
 +case class Table (tableName: StringtableData: Tabular) { 
 +  def header: List[String] = ??? 
 +  def data: Tabular = ??? 
 +  def name: String = ??? 
 +
 +</​code>​ 
 + 
 +**2.1.1.** Definiti metoda ''​toString''​ care returneaza tabelul in forma CSV. 
 +<code scala> 
 +override def toString: String = ??? 
 +</​code>​ 
 + 
 +**2.1.2** Definiti operatia de inserare a unei linii in tabel. 
 +<code scala> 
 +def insert(row: Row): Table = ??? 
 +</​code>​ 
 + 
 +**2.1.3** Definiti operatia de stergere a tuturor liniilor exact egale cu cea primita ca parametru. 
 +<code scala> 
 +def delete(row: Row): Table = ??? 
 +</​code>​ 
 + 
 +**2.1.4.** Definiti operatia de sortare a liniilor din tabel dupa o anumita coloana. 
 +<code scala> 
 +def sort(column:​ String): Table = ??? 
 +</​code>​ 
 + 
 +**2.1.5.** Definiti functia select care primeste o lista de stringuri si returneaza un nou obiect de tip Table ce contine doar coloanele specificate. 
 +<code scala> 
 +def select(columns:​ List[String]):​ Table = ??? 
 +</​code>​ 
 + 
 +**2.1.6.** Definiti functia apply intr-un **companion object** al clasei ''​Table''​. Functia trebuie sa parseze un sir de caractere si sa returneze un tabel cu numele dat. 
 +<code scala> 
 +def apply(name: String, s: String): Table = ??? 
 +</​code>​ 
 + 
 + 
 + 
 +==== Filtre peste Tabele ==== 
 + 
 +<​code>​ <​filter>​ ::=  
 +    <​filter>​ && <​filter>​ |  
 +    <​filter>​ || <​filter>​ | 
 +    <​filter>​ == <​filter>​ | 
 +    !<​filter>​ | 
 +    any [ <​filter>​ ] | 
 +    all [ <​filter>​ ] | 
 +    operation [ <​filter>​ ] 
 +</​code>​ 
 + 
 +**2.2.1.** Vom defini operatia de filtrare a datelor dintr-o tabela sub forma unui TDA. Acest lucru ne permite sa definim operatii de filtrare complexe, compuse din mai multe conditii. Acest TDA are urmtorii constructori:​ 
 +  - Field - reprezinta o conditie de filtrare pe un camp al tabelei. Aceasta conditie este satisfacuta daca valoarea de pe coloana specificata respecta predicatul. 
 +  - Compound - reprezinta o conditie de filtrare compusa din mai multe conditii. Aceasta conditie este satisfacuta daca toate conditiile din lista conditions sunt satisfacute. 
 +  - Not - reprezinta negarea unei conditii de filtrare. 
 +  - And* - reprezinta conjunctia a doua conditii de filtrare. 
 +  - Or* - reprezinta disjunctia a doua conditii de filtrare. 
 +  - Equal* - reprezinta o conditie de egalitate intre doua conditii de filtrare. 
 +  - Any - reprezinta o conditie de filtrare care este satisfacuta daca cel putin una dintre conditiile din lista este satisfacuta. 
 +  - All - reprezinta o conditie de filtrare care este satisfacuta daca toate conditiile din lista sunt satisfacute. 
 + 
 +<code scala> 
 +trait FilterCond { 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 Compound(op:​ (Boolean, Boolean) => Boolean, conditions: List[FilterCond]) extends FilterCond { 
 +  override def eval(r: Row): Option[Boolean] = ??? 
 +
 + 
 +case class Not(f: FilterCond) extends FilterCond { 
 +  override def eval(r: Row): Option[Boolean] = ??? 
 +
 + 
 +def And(f1: FilterCond, f2: FilterCond):​ FilterCond = ??? 
 +def Or(f1: FilterCond, f2: FilterCond):​ FilterCond = ??? 
 +def Equal(f1: FilterCond, f2: FilterCond):​ FilterCond = ??? 
 + 
 +case class Any(fs: List[FilterCond]) extends FilterCond { 
 +  override def eval(r: Row): Option[Boolean] = ??? 
 +
 + 
 +case class All(fs: List[FilterCond]) extends FilterCond { 
 +  override def eval(r: Row): Option[Boolean] = ??? 
 +
 +</​code>​ 
 + 
 +**2.2.2.** Pentru a simplifica definirea conditiilor de filtrare, vom defini cateva operatori care sa ne permita sa scriem cod mai concis. 
 +Vom folosi urmatorii operatori ce extind clasa FilterCond:​ 
 +  - === - pentru a verifica egalitatea a doua conditii de filtrare. 
 +  - && - pentru a face conjunctia a doua conditii de filtrare. 
 +  - || - pentru a face disjunctia a doua conditii de filtrare. 
 +  - !! - pentru a nega o conditie de filtrare. 
 +<code scala> 
 +extension (f: FilterCond) { 
 +  def ==(other: FilterCond) = ??? 
 +  def &&​(other:​ FilterCond) = ??? 
 +  def ||(other: FilterCond) = ??? 
 +  def unary_! = ?? 
 +  // Puteti sa adaugati mai multi operatori :) 
 +
 +</​code>​ 
 + 
 +**2.2.3.** In plus vom abstractiza instantierea unui obiect Field, astfel incat sa putem folosi un tuplu de forma  
 +(String, String => Boolean) pentru a crea un obiect Field. 
 +<code scala> 
 +implicit def tuple2Field(t:​ (String, String => Boolean)): Field = ??? 
 +</​code>​ 
 + 
 +**2.2.4.** Definiti operatia de filtrare a liniilor din tabel care respecta o anumita conditie. 
 +<code scala> 
 +def filter(f: FilterCond):​ Table = ??? 
 +</​code>​ 
 + 
 +**2.2.5.** Definiti operatia de update a unei linii din tabel. Funcția primeste ca input o conditie care dicteaza liniile ce vor fi modificate. Valorile schimbate se găsesc intr-un Map[nume_coloana,​ valoare_noua]. 
 +<code scala> 
 +def update(f: FilterCond, updates: Map[String, String]): Tab 
 +le = ??? 
 +</​code>​ 
 + 
 +==== Query Language ==== 
 + 
 +Vom dezvolta un limbaj de interogare, care va servi ca API pentru o gama variata de transformari de tabele, anterior implementate sub forma de functii. Acest limbaj de query va permite secvente sau combinatii ale acestor transformari. 
 + 
 +In cadrul implementarii limbajului de interogare, ne vom concentra pe includerea functionalitatilor asemanatoare cu cele din SQL, precum si pe gestionarea erorilor. Limbajul va permite doar operatii pe un tabel, pentru a simplifica implementarea. 
 +Pentru tratarea erorilor, vom utiliza TDA-ul ''​Option'',​ unde ''​Some(_)''​ indica un rezultat valid al unei operatii, in timp ce ''​None''​ semnaleaza o eroare. In cazul in care un query genereaza o eroare, aceasta se va propaga daca rezultatul este necesar in executarea unui alt query. 
 + 
 + 
 +**2.3.1.** Vom defini operatiile ce se pot realiza pe o tabela folosind TDA-ul ''​PP_SQL_Table''​. Funcția ''​eval''​ trebuie sa apeleze metodele corespunzatoare ​ definite in ''​Table''​. Ne vom folosi de mostenire pentru a oferi implementari specifice functiei eval pentru fiecare tip de operatie. 
 + 
 +<code scala> 
 +trait PP_SQL_Table{ 
 +  def eval: Option[Table] 
 +
 + 
 +case class InsertRow(table:​Table,​ values: Tabular) extends PP_SQL_Table{ 
 +  def eval: Option[Table] = ??? 
 +
 + 
 +case class UpdateRow(table:​ Table, condition: FilterCond, updates: Map[String, String]) extends PP_SQL_Table{ 
 +  def eval: Option[Table] = ??? 
 +
 + 
 +case class SortTable(table:​ Table, column: String) extends PP_SQL_Table{ 
 +  def eval: Option[Table] = ??? 
 +
 + 
 +case class DeleteRow(table:​ Table, row: Row) extends PP_SQL_Table{ 
 +  def eval: Option[Table] = ??? 
 +
 + 
 +case class FilterRows(table:​ Table, condition: FilterCond) extends PP_SQL_Table{ 
 +  def eval: Option[Table] = ??? 
 +
 + 
 +case class SelectColumns(table:​ Table, columns: List[String]) extends PP_SQL_Table{ 
 +  def eval: Option[Table] = ??? 
 +
 + 
 +</​code>​ 
 + 
 +**Nota:** Am vrea sa avem o sintaxa mai usor de citit pentru aces Query Language. De aceea, vom defini ''​implicits''​ pentru fiecare ​din operațiile ''​eval''​ ale acestor 2 TDA-uriForma unui query, fie ca este pe toată baza de date, fie ca este pe o singura tabela are forma unui tuplu de tipul: 
 +<​code>​ 
 +(tabel, "​OPERATIE",​ ...parametri...) 
 +</​code>​ 
 +unde operatia este un string: 
 +  * SelectTables - "​SELECT"​ 
 +  * InsertRow - "​INSERT"​ 
 +  * UpdateRow - "​UPDATE"​ 
 +  * SortTable - "​SORT"​ 
 +  * DeleteRow - "​DELETE"​ 
 +  * FilterRows - "​FILTER"​ 
 +  * SelectColumns - "​EXTRACT"​ 
 + 
 +**2.3.2.** Implementati functii de conversie implicite intre tuplurile descrise mai sus si query-ul descris de acestea.  
 + 
 +**Nota:** Erorile ce apar in cardul primului element din tuplu vor fi propagate la rezultat, pentru a putea ulterior combina query-uri. 
 + 
 +<code scala> 
 +implicit def PP_SQL_Table_Insert(t:​ (Option[Table],​ String, Tabular)): Option[PP_SQL_Table] = ??? 
 + 
 +implicit def PP_SQL_Table_Sort(t:​ (Option[Table],​ String, String)): Option[PP_SQL_Table] = ??? 
 + 
 +implicit def PP_SQL_Table_Update(t:​ (Option[Table],​ String, FilterCond, Map[String, String])): Option[PP_SQL_Table] = ??? 
 + 
 +implicit def PP_SQL_Table_Delete(t:​ (Option[Table],​ String, Row)): Option[PP_SQL_Table] = ??? 
 + 
 +implicit def PP_SQL_Table_Filter(t:​ (Option[Table],​ String, FilterCond)):​ Option[PP_SQL_Table] = ??? 
 + 
 +implicit def PP_SQL_Table_Select(t:​ (Option[Table],​ String, List[String])):​ Option[PP_SQL_Tab 
 +le] = ??? 
 +</​code>​ 
 + 
 +===== 3. Casa de Marcat ===== 
 +Vrem sa simulam operatii elementare pe care le-am folosi la o casa de marcat de tip self checkout. Va veti folosi de **productsTable**,​ un tabel intitulat "​Products"​ ce contine coloeanele "​Barcode",​ "​Name"​ si "​Price"​. 
 + 
 +Pentru urmatoarele cerinte trebuie sa va folositi de implementarile de la 2.3.2. Va oferim un exemplu de utilizare al **queryT**, in care selectam(cu FILTER) acele randuri care pe coloana "​Name"​ contin cuvantul "​lapte":​ 
 +<code scala> 
 +queryT(Some(productsTable),​ "​FILTER",​ Field("​Name",​ _.contains("​lapte"​))) 
 +</​code>​ 
 + 
 +**3.1.1** Definiti operatia de initializare a unui nou cos de cumparaturi (tabela goala). 
 +<code scala> 
 +def START_SHOPPING() : Table = ??? 
 +</​code>​ 
 +**3.1.2** Stiind codul de bare a unui produs si cantitatea dorita, adaugati-l la lista de cumparaturi. Nu uitati sa va folositi de *productsTable* (datele despre produsele din tot magazinul). 
 +<code scala> 
 +def ADD_PRODUCT(shopList:​ Table, barcode: String, quantity: Int): Table = ??? 
 +</​code>​ 
 +**3.1.3** Definiti operatia de stergere a unui produs din lista de cumparaturi. 
 +<code scala> 
 +def DELETE_PRODUCT(t:​ Table, name: String): Table = ??? 
 +</​code>​ 
 +**3.1.4** Definiti operatia de editare a cantitatii unui produs din lista de cumparaturi. 
 +<code scala> 
 +def EDIT_QUANTITY(t:​ Table, name: String, newQuantity:​ Int): Table = ??? 
 +</​code>​ 
 +=====  Testare =====  
 +În folderul src/test din proiect se află testele. Testele pot fi rulate din terminal folosind comanda:
  
-Dacă folosiți terminalul: 
 <​code>​sbt test</​code>​ <​code>​sbt test</​code>​
 +
 +Puteti testa singuri functionalitatea temei folosind functia main din fisierul Main.scala dupa ce ati implementat toate functiile. Ar trebui ca primul cod de bare sa fie recunoscut si al doilea nu.
 +
 +Din terminal puteti rula proiectul folosind comanda:
 +
 +<​code>​sbt run</​code>​
  
 ===== Submisie arhiva ===== ===== Submisie arhiva =====