În [1] am vizat orarele profesorilor pe o zi şi am distins între ore "libere" şi "ferestre". O zi are 12 ore, acoperind intervalul orar 8-20 corespunzător claselor care funcţionează în primul "schimb" (8-15) şi respectiv în cel de-al doilea (13-20). În interiorul secvenţei de ore propriu-zise ale unui profesor pot apărea subsecvenţe de ore libere consecutive şi - din motive de ordin practic - am ales să considerăm "ferestre" numai acele astfel de subsecvenţe care au lungimea 1 sau 2 (nu mai mult).
Următoarea funcţie "utilitară" (preluată din [1]) scanează orarul unui profesor, determinând rangul primei şi ultimei ore, precum şi numărul de ore propriu-zise:
function TR_data(arr) { // `arr` = [nume, '-', '9B', '11A', ...] var first = last = nore = 0; for(var i = 1, n = arr.length; i < n; i++) if(arr[i] != '-' && arr[i] != '~') { // dacă este oră propriu-zisă nore ++; if(!first) first = i; // reţine rangul primei ore propriu-zise last = i; // în final - rangul ultimei ore a profesorului } return {'first': first, 'last': last, 'nore': nore}; };
În [1] foloseam structura de date returnată de TR_data()
şi pentru a filtra vizual tabelul HTML care conţine orarul zilei: rândurile <tr> atributate cu data-first
> 6 corespund profesorilor liberi în primul schimb, iar cele care au data-last
< 7 corespund celor care sunt liberi în al doilea schimb. Dar trebuie să recunoaştem că acţiunile de filtrare respective (asociate butoanelor "oreAM" şi "orePM") au o valoare practică foarte mică; omiterea lor nu afectează valenţele aplicaţiei din [1].
Pe de altă parte, obţineam numărul de ore libere prin calculul (last + 1) - first - nore
şi deduceam numărul de ferestre comparând rezultatul cu 2; dar această "deducere" este chiar greşită:
- - 12C - 12E 11D ~ ~ ~ ~ 10E 10G
Rangul ultimei ore este 12, rangul primeia este 3, iar numărul de ore este 5; deci profesorul respectiv are 12+1 - 3 - 5 = 5 ore libere. Constatând că 5 > 2 (2 fiind numărul maxim de ore libere consecutive pe care l-am considerat drept fereastră) - funcţia get_scor()
ne dădea 0 ferestre, ceea ce este evident greşit (numărul corect de ferestre fiind 1: ora liberă dintre orele "12C" şi "12E").
Dar nu mai poate fi vorba de a corecta această greşală, câtă vreme deja am observat mai sus că de fapt, contextul în care foloseam first
, last
şi nore
este mai degrabă inutil. Pentru a determina numărul de ferestre nu avem nevoie de rangurile şi numărul de ore considerate în TR_data()
, apoi şi în _init()
(unde montam aceste trei atribute pe elementele <tr> ale tabelului HTML) şi în get_scor()
.
Rezolvarea cea mai firească angajează expresii regulate; definim câte un şablon pentru cazurile de "fereastră" şi vedem câte potriviri avem în şirul de caractere asociat tabloului orelor:
var one_gap = /\w[-~]\w/g; // identifică o fereastră var two_gap = /\w[-~]{2}\w/g; // două ferestre consecutive function gaps(ore) { // o listă ca ['nume','-','-','12C','-','12E','11D','~',...] var sore = ore.slice(1).join(''); // şirul '--12C-12E11D~...' var gap1 = sore.match(one_gap); // tabloul ferestrelor de câte o oră var gap2 = sore.match(two_gap); // tabloul ferestrelor de câte 2 ore var g1 = gap1? gap1.length : 0; // numărul de "potriviri" cu primul şablon var g2 = gap2? 2 * gap2.length : 0; return g1 + g2; // numărul total de ferestre };
/
…/
construieşte o expresie regulată ("şablon" - instanţă de obiect javaScript RegExp()
), la fel cum "
…"
construieşte un şir de caractere (instanţă de obiect javaScript String()
). Şablonul /\w[-~]\w/
se potriveşte de exemplu cu "C-1
" (o potrivire = o fereastră), iar /\w[-~]{2}\w/
s-ar potrivi cu "C-~1
" (când o potrivire = două ferestre); modificatorul /…/g
("global") asigură identificarea tuturor potrivirilor (nu numai a primeia), în cadrul şirului respectiv.
Putem testa comod funcţia de mai sus, folosind nodejs: adăugăm în fişierul în care vom fi înscris-o,
console.log( gaps(['nume','-','-','12C','-','12E','11D','~','~','~','~','10E','10G']) );
şi invocăm pentru fişierul respectiv:
vb@Home:~/_ORAR/orar_wg$ nodejs gaps.js --12C-12E11D~~~~10E10G // am inserat console.log(sore); în funcţia gaps() 1 // = numărul de ferestre
Bineînţeles că am reflectat pe Ajustarea orarului, schimbările evidenţiate mai sus: am înlocuit vechea funcţie "TR_data()" cu gaps()
(eliminând şi atributările instituite în [1] pentru elementele <tr>) şi am renunţat la operaţiile "oreAM" şi "orePM"; în schimb, am adăugat o operaţie într-adevăr utilă - "undo
", asigurând inversarea operaţiei "swap
".
vezi Cărţile mele (de programare)