FizzBuzz
Problema FizzBuzz este o problema tipica pe care multi candidati la interviurile din industria IT au primit-o spre rezolvare. Este o problema simpla, dar in cele mai multe cazuri problemele simple sunt foarte dificile pentru unii candidati.
Ea reprezinta un algoritm important care poate foarte bine testa abilitatile de dezvoltator ale candidatului la postul de web developer.
Acesta poate rezolva problema in orice limbaj de programare este dispus sa lucreze.
Ceea ce poate observa cel care intervieveaza candidatul este:
- felul in care intelege problema;
- stilul de programare abordat;
- complexitatea algoritmului redactat;
- scalabilitatea algoritmului si eficienta sa.
FizzBuzz poate fi enuntata astfel:
Se dau numerele de la 1 la 100. Sa se afiseze numerele de la 1-100 in urmatorul fel:
Numerele multiplu de 3 trebuie inlocuite cu textul „Fizz”.
Numerele multiplu de 5 trebuie inlocuite cu textul „Buzz”.
Numerele multiplu de 3 si 5 trebuie inlocuite cu textul „FizzBuzz”.
Vom incerca rezolvarea acestei probleme folosind JavaScript.
Pare un algoritm simplu care ar trebui sa afiseze la rulare un sir de genul:
1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz ...
Primul algoritm pe care il scriu majoritatea candidatilor este algoritmul clasic.
Primul pas
Problema incepe prin a tipari pe ecran primele 100 de numere.
Daca rulam vom obtine primele 100 de numere.
Sa discutam putin despre declararea variabilei „i”. In felul in care a fost declarata aici ea este o variabila globala folosita in functie. De obicei, o variabila folosita intr-o functie trebuie sa ramana locala in functie.
Functiile isi construiesc variable locale. Ele nu trebuie sa fie disponibile global. Dupa incheierea rularii functiei, variabilele locale trebuie dealocate si nu mai trebuie sa fie disponibile.
Deci mi se afiseaza 101 la sfarsit – exact valoarea cu care i paraseste bucla. Pentru ca variabila „i” sa devina locala in functie, putem proceda astfel:
Este normal! Apelarea la final a lui „i” imi aduce o eroare. Imi raporteaza ca nu cunoaste simbolul „i” pe care incerc sa il tiparesc in linia 7 pozitia 13 din cod. Putem elimina aceasta apelare din codul nostru.
Al doilea pas
Pasul urmator in construirea solutiei problemei initiale este ca functia sa inlocuiasca valoarea multiplilor lui 3 cu textul „Fizz”.
Pentru aceasta voi folosi o structura alternativa DACA_ATUNCI_ALTFEL in felul urmator.
Observam o linie finala in output care imi aduce „undefined”. Folosirea in codul sursa a unei functii care nu returneaza nimic, imi provoaca in output aceasta linie.
Functia poate returna la final un raspuns generic.
In regula! Daca si aceasta problema s-a rezolvat putem trece la pasul urmator.
Al treilea pas
Multiplii lui 5 din sirul tiparit pe ecran sa fie inlocuiti cu cuvantul „Buzz”. Adica in loc de 5, 10, 15, …. sa apara cuvantul „Buzz”.
Gata! Am rezolvat si aceasta problema!
Ultimul pas
Ultimul pas ar fi ca in locul multiplilor de 3 si de 5 sa punem cuvantul „FizzBuzz”. Putem reduce afisarea valorilor pana suntem in faza de dezvoltare a programului. Cerem afisarea numerelor pana la 20.
Mergand in virtutea inertiei, majoritatea candidatilor rezolva astfel problema:
Nu am obtinut un rezultat bun! 15 este de exemplu divizibil si cu 3 si cu 5, deci el trebuie inlocuit cu „FizzBuzz”. Candidatul se poate bloca aici si nu poate sa isi explice ce s-a intamplat?
Cand folosesc in cod mai multe instructiuni IF_ELSE care se indeplinesc in anumite conditii trebuie sa respect o logica corecta. Trebuie sa ma duc de la cele mai specifice la cele mai putin specifice.
In cazul nostru, deoarece se indeplinea prima conditie in cazul lui 15 care este divizibil cu 3, nu mai intra in ultimul IF. Se afiseaza „Fizz” nu „FizzBuzz” cum ma asteptam.
Remediem si aceasta problema:
Gata! S-a rezolvat.
Sa vedem mai departe cum poate fi optimizat acest program.
O prima optimizare
O a doua optimizare
Optimizare mai puternica
Ar fi util sa reducem functia „toUniqueNr”, adica sa o scriem mai compacta. Calculam un „index” intr-o variabila cu acelasi nume care poate lua valorile:
- 1 daca gaseste un numar multiplu de 3 (number %3 ==0)
- 2 daca gaseste un numar multiplu de 5 (number %5 ==0)
- 1+2 = 3 daca gaseste un numar multiplu de 3 si 5 (number %3 ==0 && number%5==0).
De asemenea, raspunsul ii cerem sa il dea intr-un vector de raspunsuri care va contine numai i la index 0 si 3 elemente la index 1, 2 si 3:
let responses = [i, "Fizz","Buzz","FizzBuzz"];
Codul functioneaza in continuare!
Mai pot reduce codul prin eliminarea:
- variabilei „index” si inlocuirea ei cu expresia completa
(number % 3 == 0 ? 1 : 0) + (number % 5 == 0 ? 2 : 0)
- variabilei „responses”.
Codul ar putea arata cam asa:
In final, eliminam si functia „toUniqueNr”. Mutam raspunsul returnat de ea in interiorul instructiunii „console.log” din functia „fizzbuzz(n)”.
Atentie insa! Trebuie inlocuita variabia „number” cu variabila „i” pentru ca in functia „fizzbuzz(n)” nu avem varaiabila „number” definita.
Asa arata aceasta problema simpla rezolvata complet si optim.
Algoritmii folositi in pasii intermediari nu sunt unici. Exista si alte metode prin care putem scrie codul.
Ne-am propus sa „surprindem” evolutia codului de la o forma bruta catre aceasta forma finala foarte compacta.
In concluzie
Excelent zic eu! Ce stim acum?
Candidatul care rezolva asa problema intelege cu adevarat cum se scrie un cod optimizat si ce inseamna scalabilitate, mentenabilitate, modularitate, testare, eficienta.
Candidatul a primit o problema relativ simpla, dar a stiut sa il duca pe cel care il asculta, pe carari fine si clare catre un rezultat optimizat.
In mod normal, majoritatea programatorilor se opresc atunci cand codul functioneaza corect si returneaza solutia pe care au obtinut-o prima data.
Un programator care poate scrie un astfel de cod merita intreaga consideratie!
Succes si voua sa reusiti!