Хрумна ми идеята да ви покажа част от кода, който аз самият съм писал... и да ви покажа най-лошите си грешки, с цел да ги избегнете когато се учите да програмирате.
В тази поредица ще ви покажа ПРОВАЛИТЕ си, за да видите, че всъщност програмирането НЕ е нещо, което научаваш веднъж като да караш колело и след това вече всичко си стои на мястото завинаги.
Истината е, че в началото няма да знаеш какво правиш и ще правиш някои доста нелепи грешки, без дори да ги осъзнаваш. В това няма проблем, но е хубаво да се учиш от грешките си и постепенно да изглаждаш нещата където е необходимо.
---
Истинска красота, нали? 😀
Нека анализираме постъпково колко ужасен е този код всъщност!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 24. Да се състави програма, която проверява дали всичките цифри на зададено цяло число са четни. | |
#include "stdafx.h" | |
using namespace std; | |
#include <iostream> | |
#include <stdlib.h> | |
#include <conio.h> | |
#include <math.h> | |
void main(){ | |
system("chcp 1251"); | |
int M; // Цяло число | |
int N = 0; // Същото цяло число, но с цифри в обратен ред | |
int digit; // Променлива за отделяне на цифрите от числото M | |
int brDigits = 0; // Брой на цифрите в числото | |
int a[30]; // Масив за цифрите на числото N (т.е цифрите на M в правилен ред) | |
int i; // Брояч в цикъл | |
cout << "Enter M: "; cin >> M; | |
// Получаване на число с цифрите в обратен ред и броя на цифрите (за масива "a"). | |
while (M>0) | |
{ | |
digit = M % 10; | |
N = N * 10 + digit; | |
M = M / 10; | |
brDigits++; | |
} | |
// Цикъл за извличане на цифрите от новото число N като елементи на масива "a". | |
for (i = 0; i < brDigits; i++) | |
{ | |
a[i] = N % 10; | |
N = N / 10; | |
} | |
// Цикъл за определяне дали цифрите са в нарастваща последователност. | |
for (i = 0; i < brDigits && a[i] % 2 == 0; i++); | |
if (i == brDigits) | |
{ | |
cout << "Всички цифри са четни."; | |
} | |
else | |
{ | |
cout << "Не всички цифри са четни..."; | |
} | |
_getch(); | |
} |
Истинска красота, нали? 😀
Нека анализираме постъпково колко ужасен е този код всъщност!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 24. Да се състави програма, която проверява дали всичките цифри на зададено цяло число са четни. | |
#include "stdafx.h" | |
using namespace std; | |
#include <iostream> | |
#include <stdlib.h> | |
#include <conio.h> | |
#include <math.h> |
Какъв е проблемът тук? Първо, че съм използвал имменото пространство std. Написах цял пост за това защо това в повечето случаи не е добра практика.
Следващият проблем идва от факта, че просто импортирам много библиотеки, които не ползвам в тази програма или пък дори и да ги ползвам... няма нужда да ги ползвам. Къде например влиза в употреба библиотеката math.h? Отговорът е никъде.
Всъщност причината да включа тези редове в програмата си е доста забавна - просто мислех, че са абсолютно необходими, за да може кодът да се компилира успешно! Това е добър урок да проучвате всеки един ред, който пишете, а не да минавате с оправданието "еми така го пишеше в учебника".
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void main(){ | |
system("chcp 1251"); | |
int M; // Цяло число | |
int N = 0; // Същото цяло число, но с цифри в обратен ред | |
int digit; // Променлива за отделяне на цифрите от числото M | |
int brDigits = 0; // Брой на цифрите в числото | |
int a[30]; // Масив за цифрите на числото N (т.е цифрите на M в правилен ред) | |
int i; // Брояч в цикъл | |
cout << "Enter M: "; cin >> M; |
Тук вече става мазало! 😅
Първо - void main()? Не, само не и void main()! Не знам защо постоянно ни даваха примери, в които main() е void функция, докато по конвенция в C++ тя винаги е int функция, която връща 0 при успешно завършване на програмата...
Следващото - имената на променливите. M, N, a, brDigits!? Първо, тези имена въобще не са описателни и ме правят да изглеждам добър по състезателно програмиране (а не съм, наистина), а второ си позволяват и някаква странна смесица от български и английски в едно име.
Правилото е, че имената на променливите трябва да са достатъчно описателни, за да не се налага да бъдат коментирани след това (кодът се самодокументира). Освен това - никога не слагайте български имена. Обожавам примера от книгата на Светлин Наков - представете си, че виетнамец ви чете кода и се чуди какво по дяволите е "br" (идва от "брой" и чувствам, че има нужда да го обясня, защото дори на български е неясно). А сега си представете, че вие четете кода на виетнамеца, който е използвал малко виетнамски в имената на променливите... не бъдете този гадняр.
Само английски имена и при това описателни! Например "numberOfDigits" или "digitCount", но никога тази пихтия горе!
Следващ проблем - обхватът на променливите. Декларирам M и i, хубаво, но ги ползвам много по-надолу. Колкото по-близко до първото им използване са декларирани променливите, толкова по-добре. Това е едно от правилата, които изглеждат безсмислени, но тяхната стойност става явна когато видите някой по-сложен проект, състоящ се от много много код.
Особено променлива за брояч във for цикъл... просто декларацията й няма работа извън самия цикъл.
И последно - някои по-дребни, но все пак съществуващи проблеми.
Защо да не използвам динамичен масив вместо статичен? Защо просто не преброя колко са цифрите на числото и след това да си заделя точно толкова памет колкото ми трябва, вместо да предполагам, че ще са 30 (това няма и как да стане с типа int)...
Последно - коментарите! Излишни са! М е цяло число... уау, наистина ли? Кое го издаде, типа му int или факта, че има най-лошото име на променлива в историята? Просто такива коментари запълват кода с излишен текст и нямат работа там. Вместо да ги пиша, по-добре би било да измисля по-разумно име на променливата, което само по себе си обяснява каква стойност се съдържа в нея.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Получаване на число с цифрите в обратен ред и броя на цифрите (за масива "a"). | |
while (M>0) | |
{ | |
digit = M % 10; | |
N = N * 10 + digit; | |
M = M / 10; | |
brDigits++; | |
} | |
// Цикъл за извличане на цифрите от новото число N като елементи на масива "a". | |
for (i = 0; i < brDigits; i++) | |
{ | |
a[i] = N % 10; | |
N = N / 10; | |
} |
Тук забележките са по-малко, но пак ги има!
Отново нещо за коментарите - те също трябва да са само и единствено на АНГЛИЙСКИ! Това е единственият универсален език в света на програмирането. Точка.
В условието на while цикъла е хубаво да има интервали, за да може кода да е по-четим. Отново, това няма значение в такава дребна ситуация, но какво става ако условието беше:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
while ((K<0&&M<5)||(4!=A)&&lifeMakesSense(myLife)^abc.hasMagicPowers()) |
Отговорът е "боза". Интервали, интервали (и скоби) - те са ваши приятели и правят кода много по-четим!
Някой други дреболийки. Бях написал статия за преинкрементирането на брояча във for цикли - има значение, защото дяволът се крие в детайлите! ++i e по-добре от i++.
Последно - в повечето програми на C++, най-добре е отварящата скоба { да е на същия ред, в който е условието на цикъла (но, разбира се, най-добрата практика е да си ПОСТОЯНЕН в стила, който ползваш и той да съответства на стила на хората, с които работиш).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Цикъл за определяне дали цифрите са в нарастваща последователност. | |
for (i = 0; i < brDigits && a[i] % 2 == 0; i++); | |
if (i == brDigits) | |
{ | |
cout << "Всички цифри са четни."; | |
} | |
else | |
{ | |
cout << "Не всички цифри са четни..."; | |
} | |
_getch(); | |
} |
Тук главоболията пак са в пълна сила, но за радост стигнахме края на програмата! 😅
Първото, което ми прави неприятно впечатление, е индентацията. Използвайте този Tab клавиш, но го ползвайте правилно! Тези условни конструкции долу... част от цикъла ли са? Ами не са!
Самият цикъл е трагедия. Ако погледнете този странен for цикъл отблизо, ще забележите, че той всъщност няма тяло. Дори това да е валидно, много по-добре е тялото му да бъде заградено от { и }. По-четим код и всички са щастливи накрая!
И накрая... празни редове. Празните редове всъщност са полезен инструмент когато разграничават отделните части от логиката, за да може кодът да стане по-четим, но тук... 4 реда преди _getch()? Няма смисъл и просто показва едно несериозно отношение на програмиста.
---
Вижте само колко много глупави грешки и то в такава проста програма! Самата логика на програмата почти не посмях да я коментирам, но със сигурност виждате някои прозорци за оптимизация и там.
Сигурен съм, че след време ще се смея и на сегашната си "прозорливост" в програмирането. Просто с времето ставаме по-добри, защото знаем повече и сме правили повече грешки.
А сега отивайте да трупате провали и да ставате ПО-ДОБРИ. 😉
Peace.
Определено в първи курс не се замислях над повечето забележки, които си посочил и просто хамалската преписвах, докато след това разбрах как става. Радвам се че ми припомни първата година в РУ специалност КН :D Поздрави и пожелание за upgrade code style :)
ОтговорИзтриванеБлагодаря ти! ;)
Изтриване