Obiectivele proiectului
Vei construi un calculator molecular complet care parsează automat formule chimice și calculează masa moleculară.
Dicționar: Chimie proiect ↔ C++
| Concept chimic | Cum se citește | În C++ | Observație |
|---|---|---|---|
| Parsare formulă | „citire caracter cu caracter" | formula[i] | acces indexat la string |
| Vectori paraleli | „simboluri[] și mase[]" | string s[N]; int m[N]; | aceeași poziție = același element |
| Simbol chimic (1-2 litere) | „ex: Na = sodiu" | string simbol=""; | prima MARE + eventual mică |
| M = Σ(cantitate × masă) | „suma produselor" | total += nr * masaElem; | acumulare în while |
| Compoziție procentuală | „procent fiecare element" | (double)(nr*m)/total*100 | cast la double! |
| Cifră multipla (ex: H₁₂) | „numărul 12, nu 1 și 2" | nr = nr*10 + (c-'0'); | construire Horner |
| 'A'...'Z' / 'a'...'z' | „majusculă / minusculă" | c >= 'A' && c <= 'Z' | comparatii ASCII |
| Element necunoscut | „nu e în baza de date" | return -1; | valoare santinelă |
Analogie: Cum citim o formulă chimică?
Rețeta de bucătărie
O formulă chimică e ca o rețetă: H₂O = „2 părți Hidrogen + 1 parte Oxigen". La fel cum calculăm costul rețetei (2×preț_ou + 1×preț_făină), calculăm masa moleculară: 2×masă_H + 1×masă_O = 2×1 + 16 = 18 u.
Exemplu: H₂SO₄ → 2×1 + 32 + 4×16 = 2 + 32 + 64 = 98 u
Tabelul elementelor în program
| Simbol | Element | Masă atomică (u) | Prezent în... |
|---|---|---|---|
| H | Hidrogen | 1 | H₂O, H₂SO₄, NH₃ |
| C | Carbon | 12 | CO₂, C₆H₁₂O₆, CH₄ |
| N | Azot | 14 | NH₃, HNO₃ |
| O | Oxigen | 16 | H₂O, CO₂, CaCO₃ |
| Na | Sodiu | 23 | NaCl, NaOH |
| Mg | Magneziu | 24 | MgO, MgCl₂ |
| Al | Aluminiu | 27 | Al₂O₃ |
| P | Fosfor | 31 | H₃PO₄ |
| S | Sulf | 32 | H₂SO₄, SO₂ |
| Cl | Clor | 35 | NaCl, HCl |
| K | Potasiu | 39 | KOH, KCl |
| Ca | Calciu | 40 | CaCO₃, CaO |
| Fe | Fier | 56 | Fe₂O₃, FeSO₄ |
| Cu | Cupru | 64 | CuSO₄, CuO |
| Zn | Zinc | 65 | ZnO, ZnCl₂ |
Simboluri cu 2 litere: Na, Mg, Al, Cl, Ca, Fe, Cu, Zn — prima literă e MARE, a doua e mică. Parsarea trebuie să distingă: „Ca" = Calciu, nu „C" + „a"!
Plan de lucru (3 faze)
Faza 1: Baza de date cu elemente (20 min)
Creează vectori paraleli pentru simboluri și mase atomice. Scrie funcția gasesteMasa(simbol) care returnează masa sau -1 dacă elementul e necunoscut.
Faza 2: Parsarea formulei (40 min)
Parcurge string-ul caracter cu caracter: literă mare = element nou, literă mică = continuare simbol, cifre = cantitate, fără cifră → cantitate 1.
Faza 3: Testare și meniu (30 min)
Testează cu formule cunoscute (H₂O=18, CO₂=44, glucoză=180), adaugă meniu interactiv, tratează erori.
Pseudocod: Algoritmul de parsare
Codul complet al proiectului
#include <iostream>
#include <string>
using namespace std;
// Baza de date cu elemente
const int NR_ELEM = 15;
string simboluri[NR_ELEM] = {"H","C","N","O","S","Na","Cl","Ca","Fe","P",
"Mg","K","Zn","Cu","Al"};
int mase[NR_ELEM] = {1, 12, 14, 16, 32, 23, 35, 40, 56, 31,
24, 39, 65, 64, 27};
// Functie: gaseste masa atomica dupa simbol
int gasesteMasa(string simbol) {
for (int i = 0; i < NR_ELEM; i++)
if (simboluri[i] == simbol)
return mase[i];
return -1; // element necunoscut
}
// Functie: calculeaza masa moleculara
int calculeazaMasa(string formula) {
int total = 0;
int i = 0, n = formula.length();
while (i < n) {
// Pas 1: citeste simbolul (litera mare + eventuale litere mici)
string simbol = "";
if (formula[i] >= 'A' && formula[i] <= 'Z') {
simbol += formula[i]; i++;
while (i < n && formula[i] >= 'a' && formula[i] <= 'z') {
simbol += formula[i]; i++;
}
}
// Pas 2: citeste numarul (daca exista)
int nr = 0;
while (i < n && formula[i] >= '0' && formula[i] <= '9') {
nr = nr * 10 + (formula[i] - '0'); i++;
}
if (nr == 0) nr = 1;
// Pas 3: cautam elementul si adaugam la total
int masaElem = gasesteMasa(simbol);
if (masaElem == -1) {
cout << "Element necunoscut: " << simbol << endl;
return -1;
}
total += nr * masaElem;
}
return total;
}
int main() {
string formula;
int optiune;
do {
cout << "\n=== CALCULATOR MOLECULAR ===" << endl;
cout << "1. Calculeaza masa moleculara" << endl;
cout << "2. Testeaza formule cunoscute" << endl;
cout << "3. Afiseaza elementele disponibile" << endl;
cout << "4. Compozitie procentuala" << endl;
cout << "0. Iesire" << endl;
cout << "Alege: "; cin >> optiune;
if (optiune == 1) {
cout << "Formula chimica: "; cin >> formula;
int masa = calculeazaMasa(formula);
if (masa != -1)
cout << formula << " = " << masa << " u" << endl;
}
else if (optiune == 2) {
string teste[] = {"H2O","CO2","NH3","C6H12O6","H2SO4","NaCl","CaCO3","Fe2O3"};
int asteptate[] = {18, 44, 17, 180, 98, 58, 100, 160};
int nrTeste = 8;
cout << "\n--- Teste automate ---" << endl;
int corecte = 0;
for (int i = 0; i < nrTeste; i++) {
int rez = calculeazaMasa(teste[i]);
bool ok = (rez == asteptate[i]);
cout << teste[i] << ": " << rez
<< " (asteptat " << asteptate[i] << ") "
<< (ok ? "OK" : "EROARE!") << endl;
if (ok) corecte++;
}
cout << corecte << "/" << nrTeste << " teste corecte." << endl;
}
else if (optiune == 3) {
cout << "\nElemente disponibile:" << endl;
for (int i = 0; i < NR_ELEM; i++)
cout << simboluri[i] << " = " << mase[i] << " u\t";
cout << endl;
}
else if (optiune == 4) {
cout << "Formula: "; cin >> formula;
int total = calculeazaMasa(formula);
if(total > 0)
cout << "Masa totala: " << total << " u" << endl;
}
} while (optiune != 0);
return 0;
}
Cum funcționează parsarea?
Trace pas cu pas: "C6H12O6" (Glucoză)
| Iterație | i | Simbol | Număr | Masă elem | Subtotal | Total | C++ echivalent |
|---|---|---|---|---|---|---|---|
| 1 | 0→2 | C | 6 | 12 | 72 | 72 | total += 6 * 12; |
| 2 | 2→5 | H | 12 | 1 | 12 | 84 | total += 12 * 1; |
| 3 | 5→7 | O | 6 | 16 | 96 | 180 | total += 6 * 16; |
Trace: "NaCl" (simbol cu 2 litere)
| Iterație | i | Simbol | Număr | Masă | Total | C++ echivalent |
|---|---|---|---|---|---|---|
| 1 | 0→2 | Na (N+a) | 1 (lipsește cifra) | 23 | 23 | simbol="Na"; nr=1; |
| 2 | 2→4 | Cl (C+l) | 1 | 35 | 58 | simbol="Cl"; nr=1; |
Observă: 'a' după 'N' e literă mică → face parte din simbol. 'l' după 'C' e literă mică → face parte din simbol.
Greșeală frecventă: Dacă nu verifici litera mică, "NaCl" se citește ca N-a-C-l = N(14) + eroare('a') + C(12) + eroare('l'). Trebuie: Na(23) + Cl(35) = 58.
Cifre mari: H₁₂ are cifra 12, nu 1 și apoi 2. Bucla nr = nr*10 + (formula[i]-'0') construiește numărul: nr=0→1→12.
Compoziție procentuală
Compoziția procentuală arată cât la sută din masa totală contribuie fiecare element. Formula: procent = (cantitate × masă_atom / masă_totală) × 100.
Exemplu: H₂O (M = 18 u)
H: 2×1/18 × 100 = 11.11% | O: 1×16/18 × 100 = 88.89% | Total = 100%
Trace parsare completă: "H2SO4"
| Pas | i | Caracter | Simbol | Nr | masaElem | Subtotal | total |
|---|---|---|---|---|---|---|---|
| 1 | 0 | 'H' (mare) | "H" | — | — | — | 0 |
| 2 | 1 | '2' (cifră) | "H" | 2 | 1 | 2 | 2 |
| 3 | 2 | 'S' (mare) | "S" | — | — | — | 2 |
| 4 | 3 | 'O' (mare, nu cifră) | "S" | 1 (default) | 32 | 32 | 34 |
| 5 | 3 | 'O' (mare) | "O" | — | — | — | 34 |
| 6 | 4 | '4' (cifră) | "O" | 4 | 16 | 64 | 98 |
void compozitie(string formula) {
int total = calculeazaMasa(formula);
if(total <= 0) return;
// Re-parsam formula si afisam procentul fiecarui element
int i = 0, n = formula.length();
cout << "Compozitia lui " << formula << " (M=" << total << " u):" << endl;
while(i < n) {
string simbol = "";
simbol += formula[i]; i++;
while(i < n && formula[i] >= 'a' && formula[i] <= 'z') {
simbol += formula[i]; i++;
}
int nr = 0;
while(i < n && formula[i] >= '0' && formula[i] <= '9') {
nr = nr*10 + (formula[i]-'0'); i++;
}
if(nr == 0) nr = 1;
int m = gasesteMasa(simbol);
double proc = (double)(nr * m) / total * 100;
cout << simbol << ": " << nr << " x " << m
<< " = " << nr*m << " u (" << proc << "%)" << endl;
}
}
Verificare cunoștințe
1. Ce returnează calculeazaMasa("NaCl")?
2. Ce face `if (nr == 0) nr = 1;`?
3. Cât e masa moleculară a H₂SO₄?
4. Cum distingem "Ca" (calciu) de "C" + "a"?
5. Ce procent de oxigen are H₂O?
6. Ce returnează gasesteMasa("X")?
Extensie: proiectarea tabelului de elemente
Când lista de elemente crește, multe if-uri devin greu de întreținut. O soluție mai bună este un tabel de perechi simbol-masă, căutat cu o funcție.
struct Element {
string simbol;
double masa;
};
Element tabel[] = {{"H", 1}, {"C", 12}, {"O", 16}, {"Na", 23}, {"Cl", 35.5}};Avantajul este că adăugarea unui element nou înseamnă o singură linie în tabel, nu modificări în toată logica programului.
Pași pentru o căutare robustă
- Citește simbolul ca
string, nu cachar. - Caută simbolul în tabel.
- Dacă nu există, returnează
-1și afișează mesaj de eroare.
Întrebări frecvente (FAQ)
Vectorii paraleli sunt mai simpli la acest nivel. Într-un program mai avansat, am folosi struct Element { string simbol; int masa; }; — dar conceptul de struct nu a fost încă predat.
Parsarea noastră le gestionează automat: 'F' e literă mare = start simbol, 'e' e literă mică = continuare. Deci simbol="Fe". La fel: Na, Mg, Al, Cl, Ca, Cu, Zn.
Corect! H = 1.008, C = 12.011, O = 15.999. Noi folosim mase rotunjite (int) pentru simplitate. Poți schimba int mase[] în double mase[] pentru precizie mai mare.
1. Mărește NR_ELEM. 2. Adaugă simbolul în vectorul simboluri[]. 3. Adaugă masa în vectorul mase[] (la aceeași poziție). Testează cu o formulă ce conține noul element.
PbInfo - Masa pentru H2O
Formula simpla
Enunț:
Cerință: Calculeaza masa moleculara pentru formula H2O.
Date de intrare:
Formula H2O.
Date de ieșire:
Masa moleculara.
Exemplu:
Intrare:
H2O
Ieșire:
18
Explicație: 2*1+16=18.
Cum gândim soluția:
- Citim formula ca string.
- Pentru H adaugam 1 inmultit cu numarul de atomi.
- Pentru O adaugam 16.
Tip: Pentru aceasta problema poti trata doar H, C, O. Daca dupa element apare o cifra, ea spune cati atomi sunt; daca nu apare, presupunem 1.
Scrie soluția în C++:
Soluție corectă:
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int masaAtomica(char e) {
if (e == 'H') return 1;
if (e == 'C') return 12;
if (e == 'O') return 16;
return 0;
}
int main() {
string f;
cin >> f;
int total = 0;
for (int i = 0; i < f.size(); i++) {
char e = f[i];
int nr = 1;
if (i + 1 < f.size() && isdigit(f[i + 1])) {
nr = f[i + 1] - '0'; // '2' devine numarul 2
i++; // sarim peste cifra deja folosita
}
total += masaAtomica(e) * nr;
}
cout << total;
}
PbInfo - Masa pentru CO2
Atom implicit
Enunț:
Cerință: Calculeaza masa moleculara pentru CO2.
Date de intrare:
Formula CO2.
Date de ieșire:
Masa moleculara.
Exemplu:
Intrare:
CO2
Ieșire:
44
Explicație: C=12 si O2=32.
Cum gândim soluția:
- C are numar implicit 1.
- O are numar 2.
- Adunam 12+32.
Tip: Numarul lipsa inseamna 1.
Scrie soluția în C++:
Soluție corectă:
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int masa(char e) {
if (e == 'H') return 1;
if (e == 'C') return 12;
if (e == 'O') return 16;
return 0;
}
int main() {
string f;
cin >> f;
int total = 0;
for (int i = 0; i < f.size(); i++) {
char e = f[i];
int nr = 1;
if (i + 1 < f.size() && isdigit(f[i + 1])) {
nr = f[i + 1] - '0';
i++;
}
total += masa(e) * nr;
}
cout << total;
}
PbInfo - Formula cu mai multi termeni
Parsare simpla
Enunț:
Cerință: Se citeste o formula formata din H, C, O si cifre de o singura cifra. Afiseaza masa.
Date de intrare:
Formula chimica.
Date de ieșire:
Masa moleculara.
Exemplu:
Intrare:
C6H6O
Ieșire:
94
Explicație: 6*12+6*1+16=94.
Cum gândim soluția:
- Parcurgem caracterele.
- Cand gasim element, citim cifra de dupa el daca exista.
- Adunam masa atomica * numar.
Tip: Aceasta varianta nu trateaza paranteze. isdigit(f[i+1]) verifica daca urmatorul caracter este cifra, iar f[i+1] - '0' transforma caracterul '6' in numarul 6.
Scrie soluția în C++:
Soluție corectă:
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int masa(char e) {
if (e == 'H') return 1;
if (e == 'C') return 12;
if (e == 'O') return 16;
return 0;
}
int main() {
string f;
cin >> f;
int total = 0;
for (int i = 0; i < f.size(); i++) {
char e = f[i];
int nr = 1;
if (i + 1 < f.size() && isdigit(f[i + 1])) {
nr = f[i + 1] - '0';
i++;
}
total += masa(e) * nr;
}
cout << total;
}
Exercițiul Practic: Calculator molecular
Extinde calculatorul molecular cu afișare detaliată, sortare și verificarea formulelor chimice. Pentru formule cu elemente de o literă poți folosi parserul simplu; pentru Na, Cl, Ca e mai clar să lucrezi direct cu perechi element-cantitate.
Limitarea parserului simplu: codul de bază citește simboluri de o singură literă și cifre de o singură cifră. Nu tratează paranteze, indici de două cifre sau elemente precum Na și Cl fără o extindere separată.
Sarcina 1: Afișare detaliată
Pași de rezolvare
- Localizează linia
total += nr * masaElem;în funcțiacalculeazaMasa() - Imediat după, adaugă
cout << simbol << " x " << nr << " = " << nr * masaElem << endl; - Înainte de
return total;, adaugă linia de separare și totalul - Testează cu "C6H12O6" → trebuie să afișeze C×6=72, H×12=12, O×6=96, Total=180
Modifică funcția calculeazaMasa să afișeze la fiecare pas: "C × 6 = 72", "H × 12 = 12", etc., apoi totalul.
De ce funcționează? Afișarea la fiecare pas al parsării ne ajută la debugging — vedem exact ce simbol a detectat programul, câte atome și masa parțială. Dacă totalul nu e corect, putem identifica imediat care element a fost citit greșit.
// Dupa linia "total += nr * masaElem;" adauga:
cout << simbol << " x " << nr // afiseaza simbol si cantitate
<< " = " << nr * masaElem << endl; // si subtotalul
// La final, inainte de return:
cout << "---\nTotal = " << total // linie separare
<< " u" << endl; // unitatea de masura
Sarcina 2: Top 3 cele mai grele
Pași de rezolvare
- Citește
nformule chimice (string) într-un vector + calculează masele cucalculeazaMasa() - Sortează descrescător vectorul de mase (cu swap simultan pe vectorul de formule)
- Afișează primele
min(n, 3)elemente - Testează cu: H2O (18), C6H12O6 (180), H2SO4 (98), NaCl (58) → top: glucoză, acid, sare
Citește n formule chimice de la tastatură. Afișează top 3 cele mai grele substanțe cu masa lor.
De ce funcționează? Sortarea descrescătoare mută substanțele cu masa cea mai mare pe primele poziții. Interschimbăm simultan și vectorul de formule și cel de mase, ca fiecare formulă să rămână asociată cu masa ei corectă.
int n; cin >> n; // cate formule citim
string formule[100]; int maseCalc[100]; // vectori paraleli
for(int i=0; i<n; i++){
cin >> formule[i]; // citim formula
maseCalc[i] = calculeazaMasa(formule[i]); // calculam masa
}
// Sortare descrescatoare (selection sort)
for(int i=0; i<n-1; i++)
for(int j=i+1; j<n; j++)
if(maseCalc[j] > maseCalc[i]){ // j mai grea?
swap(maseCalc[i], maseCalc[j]); // inversam masele
swap(formule[i], formule[j]); // si formulele
}
int top = (n < 3) ? n : 3; // maxim 3 rezultate
for(int i=0; i<top; i++)
cout << i+1 << ". " << formule[i] // pozitia + formula
<< " = " << maseCalc[i] << " u" << endl;