This is an old revision of the document!
Racket la supermarket
Descriere generală și organizare
Tema constă într-o aplicație care simulează fluxul clienților pe la casele unui supermarket.
Tema este împărțită în 4 etape:
una pe care o veți rezolva după laboratorul 2 (cu deadline în ziua laboratorului 3, la ora 23:59)
una pe care o veți rezolva după laboratorul 3 (cu deadline în ziua laboratorului 4, la ora 23:59)
una pe care o veți rezolva după laboratorul 4 (cu deadline în ziua laboratorului 5, la ora 23:59)
una pe care o veți rezolva după laboratorul 5 (cu deadline în ziua laboratorului 6, la ora 23:59)
Așa cum se poate observa, ziua deadline-ului variază în funcție de semigrupa în care sunteți repartizați. Restanțierii care refac tema și nu refac laboratorul beneficiază de ultimul deadline (deci vor avea deadline-uri în zilele de 23.03, 30.03, 06.04, 13.04).
Rezolvările tuturor etapelor pot fi trimise până în ziua laboratorului 6, dar orice exercițiu trimis după deadline se punctează cu jumătate din punctaj. Nota finală pe temă se calculează conform formulei: n = (n1 + n2) / 2 (n1 = nota obținută înainte de deadline; n2 = nota obținută după deadline). Când singura submisie este înainte de deadline, aceasta este și nota finală (întrucât n1 = n2).
În fiecare etapă, veți folosi ce ați învățat în săptămâna anterioară pentru a perfecționa simulatorul (ori ca performanță, ori ca număr de situații pe care este capabil să le modeleze).
Pentru fiecare etapă veți primi un schelet de cod (dar rezolvarea se bazează în mare măsură pe rezolvările anterioare). Intenția este să puteți rezolva tema utilizând doar indicațiile din schelet (fără a fi necesar să citiți enunțul). Enunțul încearcă să lămurească aspectele care poate nu sunt clare tuturor doar din schelet.
Etapa 1
În această etapă presupunem că supermarket-ul are fix 4 case (“counters” în engleză): C1, C2, C3, C4.
Fiecare casă este reprezentată ca o structură:
(define-struct counter (index tt queue))
index
tt
vine de la “total time”, și reprezintă timpul total de așteptare la această casă: dacă un client se așază acum la coadă, el va avea de așteptat tt unități de timp până să ajungă în față (pentru conveniență vom considera unitatea de timp ca fiind 1 minut, chiar dacă nu este realist)
depinde de numărul de produse cumpărate de clienții din coadă (1 produs = 1 minut) și de eventualele întârzieri suferite de casa respectivă
queue
este o listă de perechi (nume . nr_produse), reprezentând persoanele așezate la coadă la această casă (și câte produse au cumpărat ele)
primul element din listă trebuie să corespundă primului client care s-a așezat la coadă
Deși nu am studiat structuri la curs sau laborator, utilizarea lor este simplă (și ne ajută să avem un cod mai lizibil). Aveți aici un tutorial foarte scurt cu tot ce vă trebuie pe partea de structuri și pattern matching.
Statutul caselor diferă astfel:
În această etapă, simulatorul trebuie să modeleze doar 2 tipuri de situații:
Funcțiile principale pe care va trebui să le implementați sunt:
(add-to-counter C name n-items)
add-to-counter adaugă în coada casei C persoana name cu n-items produse (ceea ce se adaugă este o pereche care conține ambele informații)
ex: (add-to-counter C2 'ana 12)
va determina adăugarea perechii '(ana . 12)
în câmpul queue al lui C2
(min-tt counters)
min-tt determină casa din counters care are tt minim, și întoarce perechea dintre indexul acestei case și valoarea tt-ului ei) (când are de ales între mai multe case, o va alege pe cea cu index minim)
ex: (min-tt (list (counter 1 10 '()) (counter 2 12 '((ana . 12)))))
⇒ '(1 . 10)
(serve requests C1 C2 C3 C4)
serve primește o listă de cereri (așezări la coadă, respectiv întârzieri) și le tratează în ordine, în sensul că actualizează C1, C2, C3 și C4 pe măsură ce situația caselor evoluează
Exemplu:
(serve '((ana 12) (delay 1 5) (mia 2)) C1 C2 C3 C4)
unde presupunem că C1-C4 sunt în prezent lipsite de clienți, iar ITEMS = 5:
întâi caută să o așeze pe ana la cea mai avantajoasă casă posibilă
întrucât ana are 12 produse, ea se poate așeza doar la una dintre C2, C3, C4
întrucât toate cele 3 case sunt lipsite de clienți și niciuna nu a suferit întârzieri, vom alege C2 pentru că are index minim
apoi casa C1 este întârziată cu 5 minute
apoi caută să o așeze pe mia la cea mai avantajoasă casă posibilă
întrucât mia are 2 produse, ea se poate așeza la orice casă
situația curentă a caselor este: C1 este întârziată cu 5 minute (tt = 5), la C2 stă ana (tt = 12), C3 și C4 nu au clienți și nu sunt întârziate (tt = 0)
vom alege C3 pentru că, dintre cele cu tt minim, C3 are index minim
Etapa 2
Această etapă își propune exploatarea faptului că funcțiile sunt valori de ordinul întâi. Va trebui să folosiți funcții curry și să abstractizați funcții cu implementări similare. De asemenea, vă încurajăm să valorificați oportunitățile de utilizare a funcțiilor anonime și funcționalelor, deși enunțul nu impune acest lucru.
În această etapă, numărul de case din supermarket nu mai este fixat. Vom avea:
Pentru ca în etapa următoare să putem determina ordinea ieșirii clienților din supermarket, introducem acum un nou câmp în structura counter:
(define-struct counter (index tt et queue))
et
vine de la “exit time”, și reprezintă timpul rămas până când primul client din coadă va părăsi această casă
depinde de numărul de produse cumpărate de acest client (1 produs = 1 minut) și de eventualele întârzieri suferite de casa respectivă
În această etapă, simulatorul trebuie să modeleze atât situațiile de la etapa anterioară, cât și 2 noi situații:
situația în care cel mai avansat client (din punct de vedere al exit time-ului) părăsește supermarket-ul
situația în care este necesară deschiderea unor noi case pentru a micșora media timpilor totali de așteptare
În primul rând, va trebui să adaptați o serie de funcții de la etapa 1 astfel încât ele să țină cont de noua reprezentare (adică de numărul variabil de case și de prezența câmpului 'et
în structura de tip casă).
Exceptând aceste adaptări, funcțiile principale pe care va trebui să le implementați sunt:
(update f counters index)
Exemplu:
(update (λ (C) (struct-copy counter C [tt 0]))
(list (counter 1 2 2 '()) (counter 2 5 5 '()))
2)
⇒
(list (counter 1 2 2 '()) (counter 2 0 5 '()))
(remove-first-from-counter C)
Exemplu:
(remove-first-from-counter (counter 1 50 5 '((ana . 3) (leo . 35) (mia . 10))))
⇒
(counter 1 45 35 '((leo . 35) (mia . 10)))
(serve requests fast-counters slow-counters)
serve primește o listă de cereri (așezări la coadă, întârzieri, ieșiri de la casă, ajustări ale numărului de case) și le tratează în ordine, în sensul că actualizează casele din fast-counters și slow-counters pe măsură ce situația lor evoluează
Exemplu:
(serve '((ana 8) (mia 2) (mara 14) (ion 7) (remove-first) (ensure 5) (remove-first))
(list (empty-counter 1) (empty-counter 2))
(list (empty-counter 3) (empty-counter 4)))
pentru ITEMS = 5:
observăm că avem 2 case fast (pentru simplitate le vom numi C1 și C2) și 2 case slow (le vom numi C3 și C4)
primele 4 cereri distribuie cei 4 clienți astfel:
ana la C3 (prima casă slow cu tt=0) ⇒ C3 = (counter 3 8 8 '((ana . 8)))
mia la C1 (prima casă fast cu tt=0) ⇒ C1 = (counter 1 2 2 '((mia . 2)))
mara la C4 (casa slow cu tt minim) ⇒ C4 = (counter 4 14 14 '((mara . 14)))
ion la C3 (casa slow cu tt minim) ⇒ C3 = (counter 3 15 8 '((ana . 8) (ion . 7)))
remove-first caută cel mai avansat client pentru a-l scoate de la casă
ensure compară media timpilor totali cu 5
tt1 + tt2 + tt3 + tt4 = 0 + 0 + 15 + 14 = 29 ⇒ ttmed = 29 / 4 > 5
se adaugă o casă slow goală (C5) ⇒ ttmed = 29 / 5 > 5
se adaugă o casă slow goală (C6) ⇒ ttmed = 29 / 6 ⇐ 5 (deci putem trece la cererea următoare)
remove-first caută cel mai avansat client pentru a-l scoate de la casă
Precizări
Veți implementa funcțiile din fișierul supermarket.rkt. Pentru testare, veți rula codul din fișierul checker.rkt.
Tema se va încărca pe vmchecker. Acesta va fi disponibil în curând. Testele de vmchecker sunt aceleași cu cele din checker.rkt.
Tema este în primul rând o temă de programare funcțională - pentru care folosim Racket. Racket este un limbaj multiparadigmă, care conține și elemente “ne-funcționale” (de exemplu proceduri cu efecte laterale), pe care nu este permis să le folosiți în rezolvare.
Pentru fiecare etapă, checker-ul vă oferă un punctaj între 0 și 120 de puncte. Pentru a obține cele 1.33p din nota finală cu care este creditată tema de Racket, este suficient să acumulați 400 de puncte de-a lungul celor 4 etape. Un punctaj între 400 și 480 se transformă într-un bonus proporțional.
După completarea celor 4 etape, veți prezenta tema asistentului, care poate modifica punctajul dat de checker dacă observă nereguli precum răspunsuri hardcodate, proceduri cu efecte laterale, implementări neconforme cu restricțiile din enunț.
Resurse
Changelog
15.03 (ora 22:02) - Precizat că tema se încarcă sub forma unei arhive .zip cu fișierul supermarket.rkt.
13.03 (ora 23:12) - Adăugat etapa 2 (enunț și arhivă).
10.03 (ora 09:45) - Adăugat tutorialul și în secțiunea de resurse.
10.03 (ora 09:35) - Precizat deadline-urile pentru restanțieri.