Programarea Calculatoarelor: Laborator 4
Vom trata același tip de probleme cu prelucrări repetitive,
făcând trecerea de la rezolvarea recursivă la cea iterativă.
E important să învățăm de asemenea descompunerea unei probleme în
subprobleme mai mici, folosind decizia și secventierea. Exemple:
- la citirea unui număr întreg cu semn: decizia se face examinând
primul caracter (+, -, cifră, altceva), după care prelucrările pe
fiecare ramură sunt similare: apelăm aceeași funcție care citește
un șir de cifre și returnează valoarea întreagă corespunzătoare,
și eventual schimbăm semnul valorii rezultate.
- la fel, la citirea unui număr care poate fi în baza 8, 10 sau 16,
decizia se ia după prefix: 0x sau 0X pentru număr
hexazecimal, doar 0 pentru număr octal, si direct o cifră
nenulă pentru număr zecimal. Conversia șirului de cifre în valoare
numerică se face după același tipar (formulă), diferă doar valoarea bazei
(8, 10, 16), și implicit valoarea celei mai mari cifre permise (baza - 1).
Aceasta evidențiază un alt principiu de bază în programare:
generalizarea și evitarea duplicării de cod: scriem o singură
funcție de conversie care ia ca parametru suplimentar baza, și o
folosește în formula de calcul a valorii (baza * valoare anterioara + cifra),
precum și la verificarea că nu au fost introduse cifre prea mari.
- pentru citirea unui număr real, putem scrie și apela succesiv
(secvențiere) două funcții: una care citește și returnează valoarea părții
întregi, și una care citește și returnează valoarea părții reale (scrisă
similar, doar că aici trebuie să ținem minte puterea negativă a lui 10
la care s-a ajuns în partea zecimală)
- funcții scrise tot cu citire și prelucrare caracter cu caracter
a unor texte (nenumerice): pentru numărarea cuvintelor sau caracterelor
sau spațiilor etc. dintr-o linie de text
Discutăm în continuare scrierea recursivă și iterativă a problemei prezentate
la curs, de citire a unui întreg. Scriem
întâi ambele funcții cu doi parametri: caracterul curent și valoarea
parțial calculată:
#include <ctype.h>
#include <stdio.h>
int readint_r(int c, int val)
{
return isdigit(c) ? readint_r(getchar(), 10*val + (c - '0'))
: val;
}
int readint_it(int c, int val)
{
while (isdigit(c)) {
val = 10 * val + (c - '0');
c = getchar();
}
return val;
}
int main(void)
{
printf("%d\n", readint_r(getchar(), 0));
printf("%d\n", readint_it(getchar(), 0));
return 0;
}
Prelucrarea se efectuează la fel în ambele funcții, cu aceeasi secvență
la fiecare pas:
-- se testează valoarea caracterului curent
-- dacă acesta nu e cifră, prelucrarea se încheie și se returnează
valoarea curentă calculată
-- dacă acesta e cifră, se actualizează valoarea curentă calculată,
și se citește următorul caracter
Diferă modul în care se exprimă repetiția acestui pas. În varianta
recursivă, valorile actualizate (pentru caracterul nou citit și
numărul calculat) sunt transmise ca argumente la un nou apel al aceleiași
funcții -- care va efectua deci aceeași prelucrare ca și apelul curent,
însă lucrând cu noile valori transmise ca argumente. Remarcăm faptul că
funcția nu conține atribuiri, nici pentru c, nici pentru
val, fiind suficientă transmiterea de parametri.
Varianta nerecursiva exprimă repetiția prin intermediul instructiunii
while, care revine la test după ce a executat corpul său.
Următorul pas de prelucrare se execută cu noi valori pentru val
si c, întrucât aceastea au fost atribuite în corpul instrucțiunii.
Valorile inițiale pentru caracter (primul caracter citit), și numărul
calculat (0) se transmit la fel în ambele cazuri.
Valoarea finală e returnată direct în cazul iterativ; în cazul recursiv,
ea returnată pe ramura de "fals" din ultimul apel făcut, si transmisă
mai departe până la primul apel, deoarece pe ramura de "adevărat" funcția
e scrisă să returneze valoarea calculată în următorul apel recursiv pe
care l-a generat.
Putem rescrie funcțiile transformând caracterul din parametru în variabilă
locală, iar pentru varianta nerecursivă, și valoarea calculată. Structura
codului se modifică minimal, reflectând faptul că trebuie acum citită în
funcție valoarea caracterului, înainte de a fi testată, și funcțiile au
mai puțini parametri. În varianta iterativă, val trebuie inițializată
cu aceeași valoare (0) pe care o primea anterior la primul apel.
#include <ctype.h>
#include <stdio.h>
int readint_r(int val)
{
int c;
c = getchar();
return isdigit(c) ? readint_r(10*val + (c - '0')) : val;
}
int readint_it()
{
int c, val = 0;
c = getchar();
while (isdigit(c)) {
val = 10 * val + (c - '0');
c = getchar();
}
return val;
}
int main(void)
{
printf("%d\n", readint_r(0));
printf("%d\n", readint_it());
return 0;
}
Marius Minea
Last modified: Sun Mar 54 17:38:25 EET 2006