momente şi schiţe de informatică şi matematică
To attain knowledge, write. To attain wisdom, rewrite.

Determinarea numărului de ferestre dintr-un orar

javaScript | orar şcolar | regexp
2014 nov

Î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)

docerpro | Prev | Next