Mi a szemantikai hiba az alábbi C# kódrészletben?
public static Ember[] Különbségszámítás(Ember[] napok)
{
for(int i=0; i<napok.Length; i++)
{
if(i+1==napok.Length)
{
if(napok[0].Nap+365-napok[i].Nap<napok[i].Nap-napok[0].Nap)
napok[i].Nap=napok[0].Nap+365-napok[i].Nap;
else
napok[i].Nap=napok[i].Nap-napok[0].Nap;
}
else
napok[i].Nap=napok[i+1].Nap-napok[i].Nap;
}
return napok;
}
A teljes kód arra szolgálna, hogy megmondja emberek egy csoportjáról, hogy melyik kettő születésnapja van a legközelebb egymáshoz. Ez a függvény számítaná a dátumok közti napok számát. Bemenetként egy növekvő sorrendbe rendezett objektumtömböt kap. Minden objektumnak van lekérdezhető Nap tulajdonsága, ami megadja, hogy az a dátum az év hanyadik napja (szökőnap nélkül).
A for ciklusban folyamatosan vizsgáljuk, hogy elértük-e már az utolsó napot (ha i+1 egyenlő lesz a tömbméretével, akkor ugye a 0-val való indexelés miatt az már kimutatna a tömbből, így i az utolsó index).
Ha még nem értük el, akkor folyamatosan átadjuk a Nap tulajdonságnak új értékként az i és az utánna lévő nap különbségét (mivel növekvően rendezett a tömb, ezt megtehetjük, és visszafele is egyértelműen megmondható, melyik különbség melyik két i-hez tartozik, hiszen a különbségek i-je nem tér el a napok i-jétől).
Eddig jó is, a probléma viszont akkor van, ha elérjük az utolsó i-t. Ilyenkor az ehhez tartozó Napban az év első és utolsó dátumának különbségét kéne eltárolnunk, mivel az évek is ugye körbemennek, és meg kell vizsgálnunk, azt is, hogy nem-e ez a kettő van-e a legközelebb egymáshoz (pl. jan. 1. és dec. 31.).
Ezt úgy gondoltam megvalósítani, hogy megvizsgáltatom vele, hogy melyik kisebb, első dátum + 365 - utolsó dátum vagy az utolsó dátum- első dátum. És amelyik kisebb, azt az értéket tároljuk el a tömb utolsó helyén (amiről később szintén egyértelműen tudjuk, hogy az utolsó és az első i-hez tartozik).
Valami viszont egyértelműen rossz a gondolatmenetemben és/vagy a kódomban, mert a debugger szerint az utolsó Nap helyre (ami megint mondom, már a különbségeket tárolja) az második és az első elem különbsége került, nem az utolsó és az elsőé.
Hol rontottam el?
Atyúristen, mi a szar ez a gányolás??
Ez egyszerűen elmondhatatlanul rossz, kb az összes "hogyan ne programozzunk" esete megtalálható benne.
1. Szar a függvény neve. "Különbségszámítás". Milyen különbség? Miknek a különbség? Mi lesz az eredmény?
2. Ember tömböt adsz át, aminek a neve napok. Akkor ezek most emberek vagy napok? A kettő elég messze áll egymástól szemantikailag.
3. "for(int i=0; i<napok.Length; i++)
{
if(i+1==napok.Length)"
Ez mi a szaaaar? Írsz egy for ciklust napok.Length-ig majd bepánikolsz és elkezdesz heggesztgetni ha szélső értékhez érsz? Mi lenne ha a ciklus napok.Length-1-ig menne? Esetleg olvasható lenne a kód?
4. Átadsz egy paramétert, amit össze vissza módosítasz a függvényben (ember legyen a talpán, aki ezt le tudja követni) majd visszaadod ugyanazt visszatérési értékként?? Minek? Még kéne egy második out paraméter is, ahol visszadadod megint ugyanezt harmadjára, nem?
5. "Ez a függvény számítaná a dátumok közti napok számát." Tehát ugyanazt az adatszerkezetet használod fel arra, hogy az emberek születésnapját tárold, meg arra is, hogy a születésnapok különbségét tárold. Nem furcsa egy kicsit, hogy két teljesen különböző dolgot akarsz ugyanabba az objektumba gyömöszölni? Ember legyen a talpán, aki tudja, hogy most épp mi van az adott változóban.
6. Ésatöbbi...
Ez eddig úgy szar ahogy van, törölni az egészet és újragondolni 0-ról.
Ezt úgy lehetne értelmesen megcsinálni, hogy az Ember osztályodnak van egy Születésnap DateTime tulajdonsága (nem Nap, mert mi a szar az, hogy Nap? Névnap, születésnap, égitest??).
Majd egy olyan függvényt írsz, ami egy listányi, tömbnyi, ésatöbbi (leginkább IEnumerable<Ember>-ből) visszaad kettőt, akinek a legközelebb van egymáshoz a születésnapja.
Hogy minek mi a neve, és hol tárolódik, az maradjon rám, mert én pontosan tudom. Azért "gyömöszölök" két különböző dolgot ugyanabba az objektumba, mert az elsőre már nincs szükség, így kevesebb objektummal kell dolgozni.
A for ciklusnak semmi köze az utána lévő feltétel vizsgálathoz. Ott azt vizsgálom, hogy i felvette-e már az utolsó értéket, amit onnan tudunk, hogyha eggyel megnövelnénk, akkor az a tömb méretével lenne egyenlő.
Lényegében kb mindenre adtál választ, csak arra nem ami a kérdés volt. Miért ad rossz eredményt vissza utolsó különbségnek?
"Hogy minek mi a neve, és hol tárolódik, az maradjon rám, mert én pontosan tudom."
A szar programozó legfőbb ismérve, ilyenek miatt szokás leginkább elküldeni az embert, mert totál karbantarthatatlan kódot ír.
"Azért "gyömöszölök" két különböző dolgot ugyanabba az objektumba, mert az elsőre már nincs szükség, így kevesebb objektummal kell dolgozni."
Hiba, hiba, hiba. Átláthatatlanná és zavarossá teszi a kódot.
"A for ciklusnak semmi köze az utána lévő feltétel vizsgálathoz. Ott azt vizsgálom, hogy i felvette-e már az utolsó értéket, amit onnan tudunk, hogyha eggyel megnövelnénk, akkor az a tömb méretével lenne egyenlő. "
Olvass utána mi az a for ciklus és hogyan lehet paraméterezni. Ezek alapján fogalmad sincs, csak bemagoltad, hogy 0-tól megy Length-ig oszt jóvan.
Az az index vizsgálattad az elágazásban pont azt hekkeled, hogy túl sokáig megy a túl ciklusod és túlindexelnél, ezért inkább speciális esetet hozol be, fölöslegesen bonyolítva a kódot.
"Lényegében kb mindenre adtál választ, csak arra nem ami a kérdés volt. Miért ad rossz eredményt vissza utolsó különbségnek?"
Pontosan erről beszélek, hogy ki a fene tudja, hogy miért ad vissza rossz értéket, ez a kód megfejthetetlen.
Az előbb belinkeltem hogyan kell ezt szépen megoldani.
Már az elképzelésed legeljén elvérzik az egész, hogy mint int akarod tárolni, hogy melyik napon született az emberünk az évben. Hülyeség. Minek? Mennyivel értelmesebb ezt tárolni, mint magát a születési dátumát?
Arról nem beszélve, hogy a te elképzelésed szerint az aki Január 1. és December 30.-án született, azok születésnapja közelebb van egymáshoz, mint azoknak akik Január 1.-én és December 31-én születtek, mert hogy én nem nagyon látok modulót a kódodban.
Tehát alig 364 napot csal csak.
Akkor mondom még egyszer: a for ciklus 0-tól Length-ig megy, mert mint minden jó for ciklus, végigmegy az összes elemen. Azt, hogy az utolsó elemhez ért, pedig ÉN úgy tudom meg, hogy belép az elágazás igaz ágába, és onnantól kezdve tudok ezzel a nekem kellő speciális esettel dolgozni.
Date.Time-mot pedig lerendezném azzal, hogy Prog I.-en nem szeretik, ha olyan parancsot használunk, amit még nem is tanultunk.
"Arról nem beszélve, hogy a te elképzelésed szerint az aki Január 1. és December 30.-án született, azok születésnapja közelebb van egymáshoz, mint azoknak akik Január 1.-én és December 31-én születtek, mert hogy én nem nagyon látok modulót a kódodban."
Fogalmam sincs te ezt a megállapítást a leírásomból honnan szűrted le és arról se, hogy hol kellene modulót használnom, amikor összeadásnál és kivonásnál magasabb rendű műveletre nem is lenne szükség két nap távolságának kiszámításához.
"Akkor mondom még egyszer: a for ciklus 0-tól Length-ig megy, mert mint minden jó for ciklus, végigmegy az összes elemen"
Ezt hol tanítják? Sűrgősen iratkozz ki onnan.
Akkor másképp kérdezem: Melyik két hónap van közelebb egymáshoz: január és május vagy január és december?
""Akkor mondom még egyszer: a for ciklus 0-tól Length-ig megy, mert mint minden jó for ciklus, végigmegy az összes elemen"
Ezt hol tanítják? Sűrgősen iratkozz ki onnan."
Úgy értettem, hogy a kódrészemben biztosan addig megy, mert arra kell hogy végigmenjen az elemeimen. Igen, tudom, hogy a for ciklus paraméterei tetszőlegesen változtathatók, nem vagyok hülye.
Természetesen a január és a december, az egész hajcihő arra megy ki, hogy ezt a programmal is megértessem.
De egyébként ne fáradj rájöttem magamtól is a hibára. Mire az utolsó elemhez ért a ciklus, és belépett az elágazásba, napok[0].Nap helyen már az első két elem különbsége volt eltárolva, így ott már azzal számolt tovább. Az elején ki kellett menteni egy segéd változóba és arra át írni az if-ágon belül a napok[0].Nap-ot és már gyönyörűen le is futott helyesen a program.
"De egyébként ne fáradj rájöttem magamtól is a hibára. Mire az utolsó elemhez ért a ciklus, és belépett az elágazásba, napok[0].Nap helyen már az első két elem különbsége volt eltárolva, így ott már azzal számolt tovább."
Na EZÉRT nem gányolunk minden szart ugyanabba az adatszerkezetbe. Saját magad mutattad meg, miért átláthatatlan, és karbantarthatatlan a kódod. De amúgy már az előző kérdésedhez is leírtam, hogy ha neked lennék, akkor nem próbálnám ugyanabba az adattagba belepakolni a teljesen más szemantikával rendelkező értéket. Akkor még az volt a vita tárgya, hogy te elmented az ember születésnapját Hónap, Nap adattagokba, majd kitalálod hogy a Nap adattagba rakod bele, az év hanyadik napján született. Aztán kap a szerencsétlen programozó egy kódot, ahol az szerepel, hogy a Nap értéke 28. Ez most az év napja, vagy a hónapé? El lehet dönteni, de szerencsétlen ott köti fel magát. És amúgy ahhoz képest, hogy te vagy, aki kérdez, és te vagy az, aki nem tud, elég nagy szád van, és jusztis meg akarod magyarázni, hogy miért jó az, amit összehánytál. Az, hogy szar kódot írsz, nem baj, de az, hogy még neked áll feljebb, mikor közlik, hogy ez számos okból sem jó... Jaigen, mert te tudod, mire fogod használni. Csak egy megjegyzés: programkódot úgy írunk, hogy azt egy olyan programozó is azonnal megértse, aki soha nem dolgozott ezen a feladaton. Merthogy ha egyszer dolgozni fogsz, akkor nem fogod meghatni az értetlen kollégáidat, meg a vezetőséget azzal, hogy "Én tudom mit csinál".
Amúgy a for ciklushoz, csak hogy megértsd:
Amit te csináltál: Írtál egy for ciklust, ami végig megy az összes indexen, és minden loopban megvizsgálja, hogy az utolsó indexen van-e. Ha igen, akkor más működést hajt végre.
Amit minden épeszű programozó csinál: Ír egy for ciklust, ami elmegy az utolsó ELŐTTI indexig, ott elvégzi a működését, majd a ciklus UTÁN lekezeljük külön a szélső indexet. Mert mi a frásznak ellenőrizgessük az egész cikluson át egy feltételt, amikor pontosan tudjuk, hogy a legutolsó indexen lesz igaz? Kiszervezed a cikluson kívülre, és jóreggelt.
"De egyébként ne fáradj rájöttem magamtól is a hibára. Mire az utolsó elemhez ért a ciklus, és belépett az elágazásba, napok[0].Nap helyen már az első két elem különbsége volt eltárolva, így ott már azzal számolt tovább. Az elején ki kellett menteni egy segéd változóba és arra át írni az if-ágon belül a napok[0].Nap-ot"
Pontosan erről beszéltem, hogy Te magad sem érted mit csinál a programod, mert össze vissza csinál mindenfélét, követhetetlenül.
Először az év napja van egy változóban? Utána két nap különbsége? Egyremegy, jó lesz az...
Nem, nem, ilyet soha nem!
"és már gyönyörűen le is futott helyesen a program."
Azt értsd meg végre, hogy a jó program nem az, ami lefut, hanem ami könnyen olvasható, értelmezhető, karbantartható. Főleg különösen ha C# kódról beszélünk, az egész nyelvnek ez a fő csapásiránya...
Értsd meg végre, hogy nem jó amit csinálsz és nem kicsit nem jó, hanem nagyon nem jó.
Nem tudom honnan tanulsz programozni, de váltani kéne és teljesen 0-ról elkezdeni valami oktató anyagot.
Az összes általad elkövetett hibát leírják valahol, és azt is, hogy miért ne csináld.
Hát én is rég láttam ilyen gány kódot. Ha a tanárod lennék, erre bizony kövér nulla pontot kapnál, mert egyszerűen szörnyű.
Kb. minden hibát leírtak már előttem. A szélső index kezelésének semmi keresni valója a ciklusmagban, ha az csak egyszer kell, hogy lefusson, mehet a ciklus UTÁN.
Ha most a tömbben azt kapod meg, hogy egyes emberek az év hányadik napján születtek és a cél csak annyi, hogy azt a KÉT embert kell visszaadni, akiknek a legközelebb esik a születésnapjuk, akkor sokkal jobb egy új, 2 elemű tömb, amibe csak ezt a két embert teszed, és ha találtál olyan párt, akinél közelebb esik a két érték, akkor cseréled. Ez, hogy az eredeti imput tömböt írod fölül, hatalmas gányolás.
Függvénynevek, változónevek: a programozás manapság NEM egyemberes meló, tehát úgy kell dolgoznod, hogy ha valaki más ránéz a kódodra, értse, hogy miről is van szó. De ha még egyedül programozol egy kis cégnél, akkor is kell a kommentezés és a beszédes függvény- és változónév, mert ha lelépsz a cégtől, akkor valaki másnak kell majd átvennie a gányolásodat. Épp ezért, ha most nem tanulod meg ezt, akkor sehol nem fogsz megmaradni huzamosabb ideig, mert páros lábbal rúgnak majd ki, ha meglátják a gányolást.
Tl;dr: Sokat kell még tanulnod, barátom. Ha pedig kérdezel és segítséget vársz, akkor illik elfogadni a kritikát is. A jó programozó nem hiszi azt, hogy "majd ő tudja", hanem mindig tanulni, fejlődni akar és értékeli, ha valaki rámutat a gyenge pontjaira.
Kapcsolódó kérdések:
Minden jog fenntartva © 2024, www.gyakorikerdesek.hu
GYIK | Szabályzat | Jogi nyilatkozat | Adatvédelem | Cookie beállítások | WebMinute Kft. | Facebook | Kapcsolat: info(kukac)gyakorikerdesek.hu
Ha kifogással szeretne élni valamely tartalommal kapcsolatban, kérjük jelezze e-mailes elérhetőségünkön!