momente şi schiţe de informatică şi matematică
anti point—and—click

Repartizarea pe zile a încadrării profesorilor (VI.gafa)

R | orar şcolar
2021 feb

Bun ca idee, nostim de greşit ca aplicare

Funcţia alloc_by_class() din [1] avea o idee care nu-i rea: alocă pe zile – prin funcţia days2rnd() – orele clasei curente din lista de clase primită ca argument; dacă alocarea făcută nu îndeplineşte anumite condiţii, atunci permută aleatoriu profesorii clasei şi reia alocarea; dacă reapelând astfel days2rnd() pentru clasa curentă, rezultă eroarea de depăşire a capacităţii stivei de apeluri – atunci (prin tryCatch()) se permută aleatoriu clasele din lista iniţială şi se re-apelează alloc_by_class() (în noua ordine a claselor…):

alloc_by_class <- function(vCls) {  # Clasele ale căror ore trebuie alocate pe zile

    days2rnd <- function(Q) {  # 'Q' conţine liniile din FRM cu o aceeaşi 'cls'
        # Sau returnează alocarea pe zile a orelor clasei (îndeplinind anumite condiţii)
        # Sau permută profesorii şi se re-apelează, încercând o altă alocare 
    }

    tryCatch(  # blocul "principal" al funcţiei alloc_by_class()
        { FRM %>%  # încadrarea pe clase a profesorilor, oră de oră
          filter(cls %in% vCls) %>%
          split(., .$cls) %>% 
          map_df(., function(K) days2rnd(K))
        }, 
        error = function(err) {
                    tryCatch(
                        alloc_by_class(sample(vCls)),  # permută clasele şi reapelează
                        error = function(e) NULL
                    )
                }
    )  # Returnează distribuţia pe zile a orelor claselor indicate
}

Avem aici o greşală de programare, chiar nostimă; dar adevărata greşală constă în faptul că n-am testat separat, conţinutul blocului „principal” – următoarea imitare separată ar fi lămurit lucrurile:

exTest <- function() { 
    vCls <- sample(Cls)  # permută aleatoriu clasele 
    print(vCls)   
    scl <- FRM %>% 
           filter(cls %in% vCls) %>%
           split(., .$cls) %>%
           map(., function(K) K$cls[1])  # lista claselor
    scl <- do.call(c, scl)
    names(scl) <- NULL
    print(scl) 
}   # rezultă aceeaşi ordine a claselor, indiferent de permutarea făcută!

La fiecare invocare extest(), în vCls avem o nouă permutare a claselor, dar în final clasele sunt afişate într-o aceeaşi ordine: filter() produce subsetul de rânduri corespunzător claselor indicate, dar în aceeaşi ordine a rândurilor ca în FRM, iar split() separă pe clase rândurile respective, dar în ordinea existentă iniţial (nu în ordinea existentă după permutarea făcută asupra claselor).

Nu elementele vectorului de clase trebuie permutate, ci grupurile din lista rezultată prin split():

exTest1 <- function() { 
    scl <- FRM %>% 
           filter(cls %in% Cls) %>%  # numai pentru clasele indicate
           split(., .$cls) %>%  # separă pe clase
           sample(.) %>%  # permută grupurile asociate claselor
           map(., function(K) K$cls[1])  # clasele, în urma permutării
    print(scl) 
}

Este de tot hazul, că am observat greşala abia după ce am rulat câteva ore în şir alloc_by_class(), obţinând cele 1455 de distribuţii vizate în [1]… Este probabil şi mai de haz, faptul că mi-am dat seama de greşală oarecum întâmplător – luând aminte la ce zice help(filter): "Rows are a subset of the input, but appear in the same order.". Se verifică astfel, încă o dată, că cel mai bun obicei al unui programator (din afara sferei comerciale, probabil) este acela de a (re)citi şi a rescrie lucrurile de care se ocupă.

vezi Cărţile mele (de programare)

docerpro | Prev | Next