Czas ucieka, człowiek już zrobił dokumentację i zamknięcie roku w księgowości, a w związku z tym dużo więcej czasu spędza w algorytmach, programowaniu czy na technologiach sieciowych (i od dwóch dni walczy z ISPem, IPv4/IPv6, NAS326, …) i innych przyjemnych rzeczach
Niestety, w każdej beczce miodu może znaleźć się łyżeczka dziegciu. Co jednak, jeżeli proporcje wyniosą 1:1?
Na SPOJu nie brakuje uwag dotyczących stylu kodzenia, który u niektórych jest na prawdę fatalny. Trudno się dziwić. Jak słusznie zauważył @mariusz193 - na SPOJu mamy mnóstwo osób początkujących, kilka zaawansowanych i być może zero osób na średnim poziomie zaawansowania. Taka sytuacja, w połączeniu z cechami SPOJa, sprzyja wyrabianiu złych nawyków. Nie ma nic złego w pisaniu kodu “na kolanie”, o ile ktoś wie, jak napisać kod porządnie. Analogicznie w codziennym życiu: nie ma nic złego w pisaniu SMSa z literówkami co trzecie słowo (bo to SMS), o ile ktoś potrafi napisać piękne wypracowanie (bo takie ma być wypracowanie). Obawiam się jednak, że mnóstwo osób nie wie tak na prawdę, w jaki sposób pisać ładny kod. Dla osoby początkującej kwieciste konstrukcje i zasady proponowane przez różne osoby i firmy są koszmarnie złożone. Jestem przekonany, że @narbej swoją wzmianką o szablonach nikogo nie zachęcił do ich używania, skoro od razu można użyć rzutowania typów w stylu C / copy pastować funkcje wprowadzając w niej drobne poprawki (jak zrobił pewien - niestety - mentor młodocianych speców z branży IT).
Szczęśliwie się składa, że w ramach działalności gospodarczej ludzie trafiają do takich miejsc, gdzie nigdy nie było kilkudziesięciu programistów po najlepszych kursach i studiach, porozumiewających się ze sobą jedynie w Pythonie i każdy z grupą krwi C lub C++. Różni informatycy (bo nie zawsze programiści), często ponad 20 lat temu, dokładali swoje trzy grosze do jakiegoś kodu i… znikali. Bo przechodzili do korpo, bo mieli dość, bo mieli innego klienta, … Po ok. 30 latach pewna firma posiada kod źródłowy, który nosi wszelkie cechy kodu NIE nadającego się do dalszej pracy nad nim. Bywa, że łatwiej napisać od nowa całą funkcjonalność (włącznie z tłumaczeniem przez klienta, co ona ma robić, jak ma wyglądać interfejs, …) niż wyjaśnić co robi już stworzony kod (o poprawkach nie mówiąc)
Co ciekawe, kod przypomina dzieła wielu początkujących programistów, tylko liczba linii kodu jest min. 100x większa 
Wiem, że większość używa tutaj C++, natomiast poniższy kod jest napisany w Harbour 2.0. Większość uwag jest jednak ogólna. Oczywiście, jak zawsze, zachęcam do przedstawiania własnych pomysłów i krytykowania moich uwag. Styl programowania to nie nauka ścisła i każda opinia jest bardzo cenna. Co nieco np. tu, ale też np. tu.
Ad rem. Poniższe fragmenty kodu skopiowałem z corowego programu pewnej firmy, bez którego to programu firma leży. Wprowadziłem jedynie drobne poprawki redakcyjne i usunąłem zaciemniające obraz rzeczy. Zastanówcie się nad tym, dlaczego tak trudno wprowadzać zmiany w tym kodzie i dlaczego jeden z programistów po zobaczeniu sytuacji, pod pretekstem pójścia po kanapki do samochodu, zwiał z firmy pierwszego dnia pracy 
PROCEDURE CallAveTrans()
LOCAL sel,ord,magfoot,rec
LOCAL oldKonOrd := kontr->(IndexOrd())
PRIVATE ad := ai := ap := ak := {},acounted:=.t.,mag:=mag->nrmag
PUBLIC LICZENIE_S := .T. //uruchamianie modulu z liczeniem srednich
prog := par_prog
zsel = Select()
rec := Recno()
PRIVATE SumIlosc:=0, SumWartosc:=0
Oto piękny przykład kodu, którego działania nikt nie rozumie. CallAveTrans - ok, osoby wdrożone kod zrozumieją nazwę funkcji, a ściślej - procedury (w Harbour istnieje różnica, zainteresowanych odsyłam do dokumentacji samego Harboura, ale można też korzystać z (polskojęzycznej!) dokumentacji Clippera: link). Myślę, że każdy domyśla się też, że sumWartosc to będzie jakaś suma, np. wartości jakiegoś towaru. I teraz zaczynają się schodki…
Dlaczego nazwy polskie są pomieszane z angielskimi? Ze względu na profil firmy, stosowanie nazw polskich jest wskazane. Ba! Ich stosowanie ułatwia pracę z kodem - nikt nie zastanawia się, czy funkcja average(x) jest funkcją standardową, czy z jakiejś powszechnie dostępnej biblioteki czy też stworzoną - być może kilkanaście lat temu, przez osobę, która już nie pracuje - specjalnie na potrzeby projektu. Jeżeli funkcja zdaje się nie działać dla jakiegoś przypadku, mamy problem - gdzie szukać informacji na jej temat? Dobra nazwa funkcji może nam coś zasugerować, zła - wymusić szukanie wszędzie.
Zagadka! Po co jest zmienna sumIlosc? By sumować ilości? Tak, masz rację (choć mylące nazwy też się zdarzają w innych miejscach…). A co robią wymienione zmienne: ad := ai := ap := ak := {}? Też chciałbym wiedzieć
I dowiem się, jak przelecę się przez ileś plików i ileś funkcji i może napiszę program wołający część z nich w określonej kolejności, czyli za tydzień lub dwa, biorąc pod uwagę wielkość plików z kodem. Oczywiście to wersja optymistyczna i nikt z IT nie podziela mojego entuzjazmu.
public to jedna z najgorszych rzeczy w kodzie. Ile razy różne osoby walczyły z programem, który nie działał jak trzeba, bo - jak się okazało - w pliku nietykanym od lat tkwił public, który spowodował konflikt z nowo wprowadzoną zmienną tego zakresu.
private to prawie że public (odsyłam do dokumentacji). Użycie tego tworu zmniejsza ilość klepania przy wołaniu funkcji i procedur, natomiast życzę szczęścia każdemu, kto potem próbuje ustalić sens kolejnych zmiennych. Zresztą wyobraźcie sobie poniższy kod w C++. Oczywiście funkcje są dużo bardziej złożone, a plików jest ok. 20. Życzę powodzenia w analizowaniu takiego kodu, który kompilowałby się, gdyby cpp było zbliżone do xBase. Swoją drogą - czy ktoś wie, czym są zmienne prog albo zsel? Opcje są dwie: albo procedura CallAveTrans jest wołana z innego miejsca w kodzie i tam (może) dowiemy się o co chodzi, albo ktoś tworzy te zmienne de novo jako private, ale z lenistwa nie zaznacza tego w kodzie…
//plik 1.cpp
void g() {
int x = 1;
h();
}
//plik 2.cpp
void h() {
f();
}
//plik 3.cpp
int f() {
return x;
}
Po temacie samoobjaśniających się zmiennych pozwolę sobie zauważyć, ze zmienne typu ai nie bez powodu zawierają prefiks ‘a’. Jest to próba (wg mnie skrajnie nieudolna) zastosowania notacji węgierskiej, która w jednych miejscach występuje, w innych mnie, w jeszcze innych występuje, ale źle (np. zmienne rzekomo znakowe w rzeczywistości przechowują wartości true/false)
Notacja węgierska jest w Harbourze wskazana, bo typów jest stosunkowo mało, a dynamiczne typowanie w Harbourze jest bardzo, może wręcz za bardzo dynamiczne.
Czy na prawdę zainicjalizowanie takiego rec w momencie deklaracji było aż tak trudne? Kod byłby krótszy, co akurat w tym przypadku pomaga…
Dlaczego private są tworzone w środku kodu? To przykład bardzo złej praktyki - zamiast zerknąć na “górną część” kodu funkcji i wiedzieć co może wystąpić w jej ciele, muszę przygotować się na błądzenie w celu znalezienia odpowiednich zmiennych.
PROCEDURE AverageTrans
PROCEDURE CallAveTrans()
PROCEDURE SumTransFor(wysw,aveonly,hanonly)
Ok… czyli raz funkcje/procedury będą wyglądały jak funkcje/procedury: xxx(), a raz nie. Jeżeli komuś nie chciało się poświęcić sekundy na wklepanie znaków ‘(’ oraz ‘)’, strach pomyśleć, co będzie dalej. Btw - ileż razy na SPOJu i poza nim ludziom nie chciało się np. zrobić wcięcia w kodzie…
IF Alias() == "MAG"
@ t+1, l+7 SAY Transform(mag->bo + mag->ilosc - mag->il_blok, "@E 99,999,999")+IF(mag->il_blok>0,'+',' ')
ELSE
PRIVATE xKey := field->symbol
Ciąg dalszy zabawy. Tworzymy xKey gdzieś tam daleko w kodzie w jakimś else. Super. Z pewnością 100 linii i pięć miesięcy dalej będę pamiętał, że xKey istnieje tylko wtedy, jeżeli Alias() != “MAG”. Na szczęście ktoś był miły i dodał, że xKey jest nieokreślonego typu - przynajmniej nie będę się dziwił, gdy nagle z field->symbol (czyli typ znakowy) przejdę do field->ilosc (typ numeryczny).
IF Type("d2")#"D"
IF Type('frkontr')<>"L"
IF !frkontr
Może jeszcze jakiś sposób na negację? Mamy #, mamy <>, mamy (choć nie tutaj) !=, … Może nieprawda_ze_jest_prawda(x)?
N_ZapKeys()
IF Len( ai ) < 9
Komunikat( "Przekaż administratorowi: [AI =" + ATS( Len( ai ) ) + "]" )
RETURN
ENDIF
To w końcu jak nazywamy funkcje? AlaMaKota, Alamakota, ala_ma_kota, ALAMAKOTA, … ? A moze AllaHasACat? Wbrew pozorom jasne nazewnictwo jest kluczowe - o ile treść ma być samoobjaśniająca się w sensie “co to robi” (np. przechowuje sumę liczb), o tyle sposób zapisu ma objaśniać “czym to jest” (np. funkcją, zmienną, constem, …). Naciska na to Google, naciskają na to ludzie siedzący w Javie, … ale jak widać, nie dotarło do wszystkich. Bo można szybko, a czas to pieniądz. A że potem już będzie skrajnie wolno - cóż…
@ Row()+1,l+41 SAY SubStr(ak[i],r-l-41,r-l-42) //Druga czesc listy kontr
Przykład zapisu, który jest dopuszczalny, ale wymaga bardzo dobrych komentarzy tu i wcześniej. Dlaczego? Dlatego, że po tygodniu nikt nie wie, dlaczego r-l-41, a nie np. r-l-42.
klawisz := 0
Takie coś znalazłem sobie w środku jednej funkcji… i tak sobie to tam było. Ciekawe, co to robi…
#define medSUMWORKING .F.
Takie coś znalazłem w środku pliku przed jakąś funkcją. Zalecam define robić na górze pliku. I nie tylko ja. Przyczyna? Każdy wie, na jakie define zwracać uwagę i jakie są ustawione, a nie szuka ich po pliku
STATIC FUNCTION shTimeLIne()
Kolejny przykład niechlujstwa w nazewnictwie… ale to oczywiście ujdzie w projekcie liczącym dziesiątki tysięcy linii kodu, zwłaszcza, że Harbour jest case insensitive
PROCEDURE L_Trans //Wyswietlanie listy rozchodow/przychodow pozycji magazynowej
STATIC FUNCTION shTimeLIne()
// Wyswietlic timeline
RETURN //-- shTimeLIne --
PROCEDURE LTrKontr()
PROCEDURE Call_SumtransFor()
A tutaj akurat przykład tego jak bardzo można namieszać w kodzie. Co do idei funkcje są po to, by coś zwracać. Np. w matematyce f(x)=x+3 zwraca nam x powiększone o 3. W kodzie panuje zasada, że procedury mają nic nie zwracać (return nil; odpowiednik nulla), a funkcje mają coś zwracać. Jak to się ma do shTimeLIne()?
LOCAL oldFoot := QFooter('TAB - Wszyscy ESC - Wyjscie')
@t+4,l+17 SAY " powy"+cpl('z')+"ej "+Alltrim(Str(prog))+" %"
retval = PadR(Left( If( !Empty( dost2 ), '*','' ) + kontr->skrot,offset),offset)
PRIVATE retval := '',dic
IF Faktura(field->rej)
PadR(QDShort(dic,Right(field->rej,1))+'/'+Right(field->okres,4)+"/"+AllTrim(Str_Nrdok(field->nrdok)),16)
FUNCTION checkMagShow()
RETURN ( sbRegGet("CURRENT_MAG_ONLY",0) == 1)
Znowu błąd w sztuce. W końcu t + 4 i x := y czy t+4 i x:=y czy jak? Odpowiedzcie sami próbując to przeczytać 
retval += Transform(Iif(trans->typ="+",trans->cena_zak,trans->cena),"@E 99999.9999")+;
Transform(Iif(trans->typ="+",trans->cena_wal,trans->cena),"@EZ 9999.99999")+;
IF(trans->waluta="U","$",trans->waluta)+;
Transform(Iif(trans->typ="+",trans->cena_cpt,trans->cena),"@EZ 9999.999999")
Udzieliliście odpowiedzi? To przeczytajcie to
Oczywiście na SPOJu cin>>t jest zrozumiałe, ale jest też świat poza SPOJem!
PRIVATE myArr := {}
Ok. Czyli wiemy, że ktoś ma swoją, własną, prywatną (if you know what I mean) tablicę
Ciekawe po co… hm…
PROCEDURE Trans_ProcLogPrint()
LOCAL oldScr, oldKeys := N_Savekeys(), cSql
PRIVATE myArr := {}, myProcLog := ""
SAVE SCREEN TO oldScr
N_ZapKeys()
Wcięcia?
SET FILTER TO trans->nrmag = m->mag //AMI-050224 m->mag // AJ 20120308 ?
SET ORDER TO 0
SELECT trans
//Czyscimy
DBSetOrder( oldTransOrd )
SELECT (oldSel)
Pomijam komentarze, ale mieszanie funkcji i rozkazów nie jest wskazane. Więcej informacji o rozkazach w dokumentacji. Można przyjąć, że rozkaz to taki #define upraszczający wywołanie funkcji.
// 040413 Poprawki do Analizy ¦rednich
Nie używamy polskich znaków, jeżeli nie jest to konieczne. A nie jest.
Verify_Index('',file,"field->kontr",file,.T.) //cs_path // BC 20120308: moze problem z indeksami jak w drukowaniu pz-tek?
// TODO AJ 20110908 Bardzo Zle Zle Zle !!!
// ale 'na razie'
// TODO AJ 20110914 - Jeszcze gorzej !!!
// 1. Plik to jest .dbf
// 2. Indeks jest w podkatalogu a nie na sciezke
Komentarz ma coś objaśniać, wnosić coś do kodu. Te komentarze nie wnoszą niczego (dobrego)… Hardcore to Delete_File(file) // BC 20120308 kasuje calosc, gdzie komentarz jest akurat dobry - kasujemy plik, który jest tworzony… randomowo 
ELSE
AV(1)
AV(2)
AV(3)
AV(4)
AV(5)
AV(6)
AV(9)
AV(12)
AV(24)
ENDIF
A teraz niech ktoś powie co robi AV(x) i dlaczego nie dostaje tablicy / nie ma w sobie zaszytej lokalnej tablicy i nie przelatuje jej for eachem albo “zwykłą” pętlą od i := 1 (Harbour - numeracja od 1) do len(tablica)?
PROCEDURE BezDuzych()
FUNCTION Flt_Search
Polak, Anglik, dwa bratanki?
PRIVATE x1,x2,x3,x4,x5,x6,x7,x8,x9
sel:=Select()
DO WHILE len(ap)<9
Aadd(ap,0)
ENDDO
DO WHILE len(ak)<9
Aadd(ak,'')
ENDDO
// kalina:=0
SELECT tempik
SUM field->i1,field->i2,field->i3,field->i4,field->i5,field->i6,field->i7,field->i8,field->i9;
TO ap[1], ap[2], ap[3], ap[4], ap[5], ap[6], ap[7], ap[8], ap[9]
Ktoś wie, co tu się dzieje?
Oczywiście są to tylko przykłady w Harbour i prośba do was, byście nie robili czegoś takiego. W żadnym języku programowania.
Jeżeli ktoś zaprzyjaźni się z xBase, może się zainteresować, czy w sprzyjającym śmieciowemu kodowaniu języku można stworzyć coś, co działa. Odpowiedź jest taka sama jak w przypadku Ruby czy Pythona. Dużym ułatwieniem w Harbour jest podejście obiektowe, które pozwala ustatycznić typowanie i umożliwia dbanie o porządek w kodzie na dużo wyższym poziomie niż stosowane w firmie podejście proceduralne:
/*
* $Id: drzewko.prg v 3.0 2018-01-17 BA$
*/
/*
* !!! NIE PROWOKOWAC TRAGEDII PROBAMI OPTYMALIZACJI KODU !!!
*
* Zlozonosci (generowanie drzewa):
* Pamieciowa: O(n^k)
* Czasowa: O(n^k)
* n - stala pesymistycznie nie mniejsza niz liczba mnoznikow, k - liczba poziomow drzewa
* Zlozonosci (ocenianie drzewa):
* Pamieciowa: O(n)
* Czasowa: O(k)
* n - liczba rozwiazan, tzn. ciagow wygenerowanych liczb, k - liczba wezlow drzewa
* Zachodzi warunek: n <= sufit(k / liczba_poziomow_drzewa)
*
* Nie modyfikowac klasy clElement i nie uzywac jej poza klasa clDrzewo!
* Klasa clElement sluzy do opisu elementow drzewa potrzebnego przy "wycenach".
* Z tego powodu moze zawierac elementy niepotrzebne i niewydajne dla innych drzew.
* Najlepiej ograniczyc sie do modyfikacji metod oceniajacych klasy clDrzewo,
* ewentualnie zmieniac stale i definy, co jednak wymaga testowania.
*
* Przed uzyciem clDrzewo nalezy zapoznac sie z uwagami dotyczacymi konstruktora
*
* Wersja zatwierdzona przez wodza to 2.1, wersja 3.0 jest z poprawkami w kodzie
* Wersja 2.1 jest dostepna na 192.168.1.73 (BA)
*/
#include "hbclass.ch"
#include "inkey.ch"
#define ROZMIAR_DRZEWA 9000000 //Najlepiej aby byl z duzym nadmiarem, ogranicza go tylko ilosc RAMu
#define POPRAWKA_PROCENTOWA 0.34 //Program m. in. szuka liczb ludzkich i krotnosci opakowan w przedziale +/- POPRAWKA_PROCENTOWA [%]
#define KROTNOSCI_NA_STRONE 1 //Program wybiera KROTNOSCI_NA_STRONE w lewo i prawo od danej liczby, o ile mieszcza sie w przedziale +/- POPRAWKA_PROCENTOWA [%]
#define INF 999999999.0 //Jezeli cena hurtowa przekroczy ta wartosc, program nie zadziala. Wartosc okresla ostatni, raczej hipotetyczny przedzial <x, INF)
#define EPSILON 0.001 //Wielkosc infinitezymalna uzywana na potrzeby arytmetyki zmiennoprzecinkowej (dzielenia w metodzie oceniajacej)
//#define DEBUG
create class clElement
exported:
method new_element(nWartosc, nPrzodek, nPoziom) block {| Self, nWartosc, nPrzodek, nPoziom | ::nWartosc := nWartosc, ::nPrzodek := nPrzodek, ::nPoziom := nPoziom, Self} constructor
method dodaj_syna(nIndeksSyna, nWartoscSyna) inline (hb_hset(::anSynowie, nWartoscSyna, nIndeksSyna))
method czy_istnieje_syn(nSzukanySyn) inline (hb_hhaskey(::anSynowie, nSzukanySyn))
method get_wartosc() inline (::nWartosc)
method get_przodek() inline (::nPrzodek)
method get_poziom() inline (::nPoziom)
method get_liczba_synow() inline (len(::anSynowie))
hidden:
data nWartosc as numeric
data nPrzodek as numeric
data nPoziom as numeric
data anSynowie as array init hb_hash()
endclass lock
create class clDrzewo
exported:
//Tablica anOpakowania nie moze zawierac zer jako pustych opakowan. Patrz: definicja metody
method new_drzewo(nIloscNaMagazynie, anOpakowania, nCenaHurtowa, lWBazieJestKZW, cSciezkaDoBazy, cSciezkaDoIndeksu)
method get_wyniki() inline (::anRozwiazanie)
//cOpis musi byc wyslany przez referencje, aby zostala zapisana wartosc
method debuguj(cOpis) block {| Self, cOpis | cOpis := ::cOpisBledu, ::lBlad}
method new_drzewo(nIloscNaMagazynie, anOpakowania, nCenaHurtowa, lWBazieJestKZW, cSciezkaDoBazy, cSciezkaDoIndeksu) class clDrzewo
local nIndeksBiezacegoElementu := 1
local nIlePoziomow := 5
local lKZW
local anIlosciIParametry
local nWartoscNowegoElementu
local nIl1
local nPozycjaTablicyZMnoznikami
local nNajwiekszaIlosc
local nIleMnoznikow
local i
local j
#ifdef DEBUG
local cCoWyplujeAlgo
#endif
::pobierz_parametry(cSciezkaDoBazy, cSciezkaDoIndeksu, nCenaHurtowa)
//Wybieram mnozniki adekwatne do ceny
if(nCenaHurtowa < 2.0)
nPozycjaTablicyZMnoznikami := 1
elseif(nCenaHurtowa < 50.0)
nPozycjaTablicyZMnoznikami := 2
else
nPozycjaTablicyZMnoznikami := 3
endif
if(!::lBlad)
/*
* Aby program uznal przypadek KZW musza byc spelnione dwa warunki konieczne:
* 1) Kartoteka dla ktorej generowane sa widelki musi pochodzic z KZW wg bazy danych
* 2) Istnieje tylko opakowanie J2 > 1 przy ilosci w kartotece <= J2 i J2 < il1
* Drugi warunek pozwala odfiltrowac przypadki, ktore choc sa KZW, powinny byc normalnie traktowane
* Aby metoda zadzialala osoba uzywajaca klasy odpowiada za niewysylanie do niej zerowych opakowan
* Dla J2 = 0 nalezy wyslac tablice, w ktorej J2 = J1 = 1 (jak w WMS wg AMA)
* Opisana metoda nie jest doskonala, ale nie istnieje lepsza przy dostepnych danych
*/
if(lWBazieJestKZW .and. len(anOpakowania) == 1 .and. anOpakowania[1] > 1 .and. nIloscNaMagazynie <= anOpakowania[1] .and. anOpakowania[1] < ::nIl1)
lKZW := .t.
nNajwiekszaIlosc := anOpakowania[1]
//Jezeli nie ma KZW to il1 biore z tabeli chyba ze istnieje opakowanie w przedziale do +30% * il1 z tabeli
else
lKZW := .f.
nNajwiekszaIlosc := ::nIl1
aeval(anOpakowania, {| nOpakowania | iif(nOpakowania > nNajwiekszaIlosc .and. nOpakowania < (1 + POPRAWKA_PROCENTOWA) * ::nIl1, nNajwiekszaIlosc := nOpakowania, )})
endif
nIleMnoznikow := len(::anMnozniki[nPozycjaTablicyZMnoznikami])
//Tworze korzen zawierajacy ilosc minimalna, konstruktor inicjuje ::nLiczbaElementow wartoscia 1
::aoWezly[1] := clElement():new_element(::nIl5, 0, 1)
//Funkcje inkrementuja nIndeksBiezacegoElementu w miare dodawania elementow do drzewa
//Stad return nIndeksBiezacegoElementu jest rownowazny zwroceniu liczby elementow drzewa
//Fory przebiegaja kolejne elementy drzewa w celu stworzenia ich potomkow
for i := 1 to nIndeksBiezacegoElementu
if(::aoWezly[i]:get_poziom() < nIlePoziomow)
for j := 1 to nIleMnoznikow
nWartoscNowegoElementu := ::aoWezly[i]:get_wartosc() * ::anMnozniki[nPozycjaTablicyZMnoznikami][j]
if(nWartoscNowegoElementu <= nNajwiekszaIlosc)
::aoWezly[++nIndeksBiezacegoElementu] := clElement():new_element(nWartoscNowegoElementu, i, ::aoWezly[i]:get_poziom() + 1)
::aoWezly[i]:dodaj_syna(nIndeksBiezacegoElementu, nWartoscNowegoElementu)
++::nLiczbaElementow
//Jezeli J2 = 0, nalezy uznac, ze J2 = J1 (jak w WMS wg AMA)
//W najlepszym przypadku anOpakowania zawierajace zera jedynie wydluzaja czas obliczen, ale nie ma gwarancji poprawnosci dzialania tej metody dla zerowych opakowan
aeval(anOpakowania, {| nOpakowania | ::dodaj_krotnosci(@nIndeksBiezacegoElementu, nOpakowania, i, nNajwiekszaIlosc)})
::dodaj_ludzkie(@nIndeksBiezacegoElementu, i, nNajwiekszaIlosc)
endif
next
endif
next
anIlosciIParametry := ::ocen_drzewo(nIlePoziomow, anOpakowania, nNajwiekszaIlosc, nCenaHurtowa, lKZW)
//KZW koncze na J2
if(lKZW)
anIlosciIParametry[1] := nNajwiekszaIlosc
endif
//anRozwiazanie[i][1] zawiera i-ta ilosc il(i), a anRozwiazanie[i][2] i-ta cene c(i), zaczynajac od i = 1 (hurtu). Widelek jest len(anRozwiazanie)
::dodaj_ceny(anIlosciIParametry, ::nIl5, nNajwiekszaIlosc, ::nKrotnoscCeny, nCenaHurtowa)
#ifdef DEBUG
cCoWyplujeAlgo := ";"
for i := 1 to len(::anRozwiazanie)
cCoWyplujeAlgo += space(6 - len(alltrim(str(::anRozwiazanie[i][1])))) + alltrim(str(::anRozwiazanie[i][1])) + " w cenie " + alltrim(str(::anRozwiazanie[i][2])) + " zl;"
next
alert(cCoWyplujeAlgo)
#endif
endif
return Self
Dorzucam też fragment związany z dodawaniem elementów do drzewa wg bardzo złożonego algorytmu, zarówno logicznie (matematycznie) jak i implementacyjnie. O ile zrozumienie ifa jest trudne, o tyle komentarz obrazowo mówi, co ten if robi (i stąd - jakie będą konsekwencje jego usunięcia).
//Jezeli istnieje przodek, nie dubluje wartosci i miesci sie ona w przedziale to dodaje wujka wzgledem elementu biezacego
//Np: mam 10 -> 30 -> 90, dla 90 wywolala sie funkcja i 100 jest ludzkie oraz miesci sie w przedziale wiec dodaje 10 -> 100
if(::aoWezly[nIndeksWezlaTworzacego]:get_przodek() != 0 .and. !::aoWezly[::aoWezly[nIndeksWezlaTworzacego]:get_przodek()]:czy_istnieje_syn(nLudzka);
.and. nLudzka >= (1 - POPRAWKA_PROCENTOWA) * ::aoWezly[nIndeksWezlaTworzacego]:get_wartosc() .and. nLudzka <= (1 + POPRAWKA_PROCENTOWA) * ::aoWezly[nIndeksWezlaTworzacego]:get_wartosc();
.and. nLudzka > ::aoWezly[::aoWezly[nIndeksWezlaTworzacego]:get_przodek()]:get_wartosc())
::aoWezly[++nIndeksBiezacegoElementu] := clElement():new_element(nLudzka, ::aoWezly[nIndeksWezlaTworzacego]:get_przodek(), ::aoWezly[nIndeksWezlaTworzacego]:get_poziom())
::aoWezly[::aoWezly[nIndeksWezlaTworzacego]:get_przodek()]:dodaj_syna(nIndeksBiezacegoElementu, nLudzka)
++::nLiczbaElementow
Można mieć dużo zastrzeżeń i wiele będzie zasadnych - wszak to wciąż kod roboczy. Ale różnicę chyba widać. Podobnie jest w C++ i każdym innym języku - widać, ile ktoś czasu pracował nad kodem.
Dla dowodu kultowy C++. Poniższy kod nie jest idealny (bez dwóch zdań) i teraz napisałbym go inaczej niż daaawno temu, kiedy go pisałem. Ale sami zobaczcie na maina, który jest w oddzielnym pliku main.cpp:
#include <iostream>
#include "game.h"
int main(int argc, char* argv[])
{
try {
Game game(argc, argv);
game.execute();
}
catch (const char* string) {
std::cerr << std::endl << "Exception: " << string << std::endl;
}
catch (...) {
std::cerr << std::endl << "Unknown Exception!" << std::endl;
}
}
Popatrzcie nawet na headery - ich ułożenie ma informować o rodzaju includowanych treści, np. widać różnicę między plikami nagłówkowymi w projekcie a bibliotekami standardowymi - są w innych miejscach.