Víte, co to jsou programovací paradigmata? Budu mít tří programátory s různou úrovní znalostí. Zadám jim stejný úkol. Tento úkol bude spočívat ve vytvoření programu pro sečtení celočíselné řady, která se nachází v textovém souboru. Programátoři jsou různě zkušení, a proto každý zvolí odlišný přístup.
První programátor je začátečník. Vytvoří jednoduchý program.
// sum1.c #include <stdio.h> int main(int argc, char* argv[]) { int res = 0; int n; /* Cyklus se přeruší, pokud scanf() obdrží ze std. vstupu řetězec, který nelze konvertovat na celé číslo, nebo dojde-li k uzavření vstupního proudu. */ while (scanf("%d", &n) == 1) { res += n; } printf("\nResult: %d\n", res); return 0; }
A sečte číselnou řadu:
$ ./sum1 < numbers.txt
Druhý programátor je zkušenější. Zadaný úkol rozdělí na dvě části – načtení souboru a součet číselně řády. Pro každou část vytvoří funkci.
// sum2.c #include <stdio.h> #include <stdlib.h> /* Funkce načte pole celých čísel ze souboru file_name. V případě úspěchu nastaví do nums ukazatel na začátek pole a vrátí počet načtených prvků. V případě neúspěchu vrátí hodnotu -1. */ int load_file_nums(int** nums, const char* file_name) { int nums_count = 0; int* ret_ptr; int* tmp; FILE* fp; int n; if (!nums) { return -1; } // Otevřeme zdrojový soubor. fp = fopen(file_name, "r"); if (!fp) { return -1; } // Postupně čteme čísla ze souboru while (fscanf(fp, "%d", &n) == 1) { // Pokud je počet přečtených čísel > 0 if (nums_count) { // Zvětšíme velikost paměti pro další číslo. tmp = realloc(ret_ptr, (nums_count + 1) * sizeof(int)); if (tmp) { ret_ptr = tmp; } else { free(ret_ptr); fclose(fp); return -1; } } else { // Alokujeme paměť pro první číslo. ret_ptr = malloc(sizeof(int)); if (!ret_ptr) { fclose(fp); return -1; } } // Uložíme číslo do pole. ret_ptr[nums_count++] = n; } // Uzavřeme soubor. fclose(fp); // Vrátíme ukazatel na pole a počet prvků v poli. *nums = ret_ptr; return nums_count; } /* Funkce sečte celá čísla v poli nums o délce len a vrátí výsledek. */ int sum(int* nums, int len) { int res = 0; int i; if (nums) { for (i = 0; i < len; i++) { res += nums[i]; } } return res; } int main(int argc, char* argv[]) { int* nums; int nums_count; int i; if (argc != 2) { fprintf(stderr, "Usage %s <file-name>\n", argv[0]); return 1; } // Načteme pole čísel ze souboru. nums_count = load_file_nums(&nums, argv[1]); if (nums_count == -1) { fprintf(stderr, "Error loading file!"); return 1; } // Sečteme čísla v poli a zobrazíme výsledek. printf("\nResult: %d\n", sum(nums, nums_count)); // Uvolníme paměť pro pole. free(nums); return 0; }
A sečte číselnou řadu:
$ ./sum2 numbers.txt
Třetí programátor je profesionál. Využije principy OOP.
// sum3.cpp #include <iostream> #include <fstream> using namespace std; class csum { private: int m_tsum; // mezisoučet public: // Konstruktor csum(): m_tsum(0) { } // Přičte číslo n k mezisoučtu. void add(int n) { m_tsum += n; } // Vrátí výsledek součtu. int result() { return m_tsum; } // Inicializuje nové sčítání. void reset() { m_tsum = 0; } // Sečte čísla ze vstupního proudu. istream& from_stream(istream& is) { int n; reset(); while (is >> n) { add(n); } return is; } }; // Přetížení operátoru >> pro vstupní proud. istream& operator>>(istream& is, csum& sum) { return sum.from_stream(is); } int main(int argc, char* argv[]) { fstream fs; csum sum; if (argc != 2) { cerr << "Usage: " << argv[0] << " <file-name>" << endl; return 1; } // Otevřeme soubor. fs.open(argv[1]); if (!fs.is_open()) { cerr << "Error opening file!" << endl; return 1; } // Sečteme čísla v souboru. fs >> sum; // Zobrazíme výsledek. cout << "Result: " << sum.result() << endl; return 0; }
A sečte číselnou řadu:
$ ./sum3 numbers.txt
Na konec všechny požádám, aby pomocí svého programu sečetli číselnou řadu ze souboru, který se nachází na webu. Procedurální programátor udělá další funkci. Objektově orientovaný programátor vytvoří novou třídu. A co udělá naivní programátor? Chvíli bude přemýšlet, a potom napíše:
$ wget -qO- http://.../numbers.txt | ./sum
Pozn. Uvedené příklady jsou funkční. Hlavičkové soubory nejsou použity záměrně, aby byl ukázkový kód kratší.
Pouze skutečný Mistr dokáže dělat jednoduché a účelné věci.
Mistr nejsem, ale jde to IMHO mnohem jednodušeji.
#include <iostream> #include <iterator> #include <numeric> using namespace std; int main() { istream_iterator<int> numbers(cin); cout << accumulate(numbers, istream_iterator<int>(), 0); return 0; }
Srovnání by dávalo smysl, kdyby oba programy dělaly stejnou věc.Ten procedurální program načítá čísla do pole a ten OOP program je pouze sečte. Realokace před každým přidáním může být zbytečne neefektivní.
Díky za komentář. Máte samozřejmě pravdu. Mnohem lepší by bylo alokovat paměť po větších blocích. Rovněž přesnějším ekvivalentem u proc. programu by asi byla funkce int sum_from_file(FILE* fp);
Nešlo mi tolik o srovnání samotných přístupů, jako o srovnání reálných výsledků, které podle nich vznikají. Také to byla trochu nadsázka :)
A praktik ví, že programátor je nesprávný termín pro řešitele problémů. A že ne každý problém je třeba řešit otevřením editoru/IDE a psaním sáhodlouhého komplikovaného programu. Někdy stačí něco jednoduchého, klidně oneliner. A plně v duchu onoho wgetu na konci napíše něco jako
$ gawk '{s+=$1} END {print s}' < input.txt
No vidite a ja bych to ohodnotil opacne. Jestli jsem se za tu dobu co programuju neco naucil, tak psat kod co nejpruhlednejsi, nejjednodussi, nejsnazsi na porozumeni... protoze az ho pak nedo bude prochazet (hledat chybu, rozsirovat) a snazit se ho pochopit, tak to oceni. Nejhorsi na pochopeni byvaji tzvn. "chytra" reseni (ano je vetsi zabava je vymyslet).
U toho poloprofi reseni:
- proc je to tak dlouhy a slozity - nesnazi se tam nahodou nekdo implementovat neco co uz davno je davno napsano a lepe v nejake standardni knihovne?
Na příkladu je krásně vidět, jak je možné řešení triviální věci za použití filosofických nesmyslů / paradigmat / natáhnout do neskutečné a neefektivní pitominy, předpokládám, že to bylo i poselství článku, a tak tleskám :)
Bohužel jsem se setkal s mnoha lidmi, kteří na 2 a 3 nedali dopustit (a mysleli to vážně).
KISS
Chápu pointu toho článku a dám ti radu: vykašli se na C a nauč se Javu. Ten wget dostnaneš v předpřipravené knihovně a můžeš s ním udělat mnohem lepší kousky. Ano, nebude to na 1 řádek, ale třeba na 5, ale co má jako být? Je to univerzálnější než konzole. Jaký je rozdíl mezi pekně napsanou funkcionalitou zaobalenou do OOP třídy a příkazem v konzolí? Obojí se chová jako program. Jenže u OOP vidíš hned jaké to má metody, nemusíš luštit man stránku s přepínačema, má to univerzální použití, můžeš to paralelizovat, můžeš prostě co chceš. A v Javě si najdeš knihovnu pro všechno co chceš.