Psaní a odlaďování programů

Nejdříve motivace:

Výňatek z e-mailove korespondence, do které adresát nijak nezasahoval, poslední mail přišel 4 hodiny po prvním:

Hotovo. Jsem trubka.
Už mi běhá první řádek, síla je pořád špatně, tak už to nechte já to
snad nějak doklepu.
Sin a cos v dJqz jsou prohozené, ale pořád tam něco nesedí … .
dobrý den,

mám zásadní problém dokončit úlohu X. Už nemůžu najít další
chybu. Našel jsem jich snad na desítky, ale teď už fakt netuším.
Vzhledem k tomu, že už jsem nad úlohou strávil více než 20 hodin času,
kterého nemám zrovna nazbyt, neboť DRR není jediný předmět, nemám už
sílu ani čas se v tom dál vrtat. Navíc nepředpokládám, že by mi
šťourání v programu a hledání chyb ve znaménkách apod. k něčemu bylo.
Zkoušel jsem už snad úplně všechno, co mě napadlo (ploty, numerické
výpočty, …) a netuším, kde může být chyba.

Můžete se prosím podívat na můj kód, co je špatně ?

Poučení

  • Většina z Vás je schopna domácí úlohy úspěšně vypracovat.
  • Značná část z Vás nemá správné programátorské návyky, a proto je pro Vás vypracování úloh pracnější než je nutné.
  • Domnívám se, že úspěšné vyřešení úlohy by Vám mělo přinést také uspokojení z dobře vykonané práce, z pokoření problému a podobně.
  • Cílem domácích úloh je procvičit danou látku a vypěstovat ve Vás správné programátorské a inženýrské návyky.

Jak tedy na to

  • Pořádně si rozmyslet řešení problému. Osobně doporučuji, pokud je to zvládnutelné, a to snad domácí úlohy jsou, nejdříve vyřešit na papír ručně a u složitějších úloh pak paralelně použít nástroj typu Maple, Mathematica, symbolicky toolbox Matlabu. Na papíře udělá člověk snadno chybu typu překlep, ale získá základní představu o tom, co mu má vyjít. V programovém nástroji se překlep nebo opomenutí nestane tak snadno, ale často vyjde cosi, v čem není vidět podstata výsledku. Výsledky porovnat a teprve poté implementovat.
  • Při analýze problému pečlivě dbejte na ošetření všech singularit. Pravidel typu nedělit nulou je málo, pro běžné programování asi 5, ale o to častěji se na ně zapomíná.
  • Lepší než ošetřovat singularity použitých funkcí a operací je používat funkce, které takové singularity nemají. Příkladem takové funkce je ATAN2, podobně je to s postupy pro řešení problému. Například je lepší geometrický problém řešit jako průnik dvou kružnic a se vzniklými body pak pracovat pomocí ATAN2 než použít kosinovou větu a pak přemýšlet, jestli spočítaný úhel mám přičíst či odečíst.
  • Pečlivě a s rozmyslem implementovat. Vzpomenout si na všechny rady z teorie programování:
  1. Pojmenovávat pěkně proměnné, delší smysluplné názvy pomáhají neplést jednotlivé proměnné.
  2. Zavádět proměnné pro konstanty: jestliže mám jedno rameno délky 2 a druhé rameno délky 3, ve vzorci y= cos(alfa) + 5*sin(alfa) už nejsem schopen rozpoznat, ze koeficient 1 u cosinu je rozdíl délek ramen a koeficient 5 je součet.
  3. Zavádět funkce, drobné funkce mohou být v Matlabu uvnitř funkce, která je používá. Například jsem byl svědkem mnoha “zázračných” rozchození programu, kdy se opakované ruční vypisování D-H matice nahradilo voláním funkce. Cílem není (ve vetšině kybernetických předmětů) napsat co nejúspornější kód, ale napsat co nejrychleji plně funkční kód.
  4. Kontrolovat a ladit každý řádek či malou skupinu řádků zvlášť (platí pro Matlab) či každou funkci zvlášť (pro většinu ostatních procedurálních jazyků). Napsat celou úlohu najednou a pak zjistit, že to nefunguje, k ničemu nevede.
  5. Úlohy z robotiky a počítačového vidění nejsou triviální, a proto je nelze vyřešit slepým tápáním typu: zde změním znaménko, zde vyměním sinus za cosinus, zkusím přičíst pi/2. Zkuste si ve Vašem i triviálním programu (i třeba jen na dvou řádcich, kde máte podezření, že by mohla být chyba) spočítat všechna podobná binární rozhodnutí (počet znamének, počet goniometrických funkcí,…) a pak si spočtěte 2 na tento počet. Výsledek dělený dvěma dává průměrný počet (různých!) pokusů, které musíte vyzkoužet, než se trefíte do správného řešení.
  • Při ladění, a to i dílčím, velmi doporučuji vizualizovat mezivýsledky. V robotice a počítačovém vidění je možné spoustu věcí zkontrolovat jedním pohledem, zatímco hleděním do kódu se překlep hledá velmi těžko. Zkuste si to, detailni vizualizace, a to již při prvním psaní programu, podstatně zkracují celkovou dobu psaní programu.
  • Při testování je vhodné na papíře vymyslet jednoduchou situaci, kterou jste schopni vyřešit ručně a pak porovnat ruční řešení s během programu krok po kroku. Testování na jednoduchých situacích nedokáže, že celý program je vždy funční, ani neotestuje vše, ale funkčnost v jednoduché situaci je nutnou podmínkou funkčnosti programu.
  • Automatické hodnocení odevzdávacího systému používejte jen jako poslední variantu, nejdříve program odlaďte na vlastních datech, lokální ladění je časově mnohem efektivnější.
  • V krizových situacích pomůže celou funkci přepsat.
Drobné rady
  • Mnoho programu, které mají chodit pro N situací (například počítá body trajektorie) pracuje správně jen s prvním bodem a pro další kroky se buď některá proměnná neupdatuje, neinicializuje znovu nebo se stále používá hodnota pro první situaci.
  • Občas se splete proměnná “i”, proměnná “l” a znak “1”.
  • Proměnná “i” je obzvláště nebezpečná, je to také symbol pro imaginární jednotku.
  • V řadě výpočtů by neměla vznikat komplexní čísla. Detekovat například neexistující řešení kontrolou, zda výsledek je reálný, je špatný přístup. Kontrolovat by se měl podmínky (například trojúhelníková nerovnost) před daným výpočtem. Taková kontrola pak ve skutečném programu vede ke smysluplné chybové hlášce, protože rozumíte tomu, co se skutečně stalo. Tedy například “Průsečík ramen 2 a 3 neexistuje.” místo “Úloha nemá řešení.”
  • Mnohokrát se ukázalo, že chyba byla v jednoduché, ale podceňované části programu. Studenti jsou schopni strávit hodiny překontrolováváním kódu počítajícím Jacobián, ale přehlédnou neinicializovanou proměnnou.
  • Nepřiměřeně častá chyba je to, že pro část cyklu se počítá vše správně pro danou iteraci, ale část výpočtu uvnitř cyklu se provádí opakovaně jen pro první iteraci (záměna 'i' za '1') nebo část výpočtu, která má být v cyklu, je vně cyklu a neupdatuje se.

Ještě jiný pohled

  1. To, ze jsou v programu chyby, je normalni. Inzenyr s tim musi umet zapasit a zit. Je to narocne, ale kdyz se to nenaucime na skole tak kdy?
  2. Nejprve si dobre rozmyslete strukturu problemu. Papir, tuzka, jazyk matematickych vzorcu a pohodlne kreslo daleko od klavesnice jsou zakladni nastroje.
  3. Pro kazdy podproblem si sestrojte (synteticky) testovaci priklad.
  4. Budujte reseni zespoda, nejprve vyreste mensi podproblemy, udelejte z nich funkce (procedury) s jasne definovanym rozhranim (argumenty) a pak to postupne spojujte dohromady, kazde dilci reseni nebo jejich spojeni ihned testujte a vysledky si vizualizujte. Matlab je na to skvely nastroj. Jakmile neco nesedi, je treba vratit se zpet, casto az ke kreslu a papiru se vzorecky. Pro slozitejsi veci si nevahejte v Matlabu udelat interaktivni nastroj, usili se bohate vyplati. Testovaci a verifikacni kody si vzdy schovejte, aspon do doby odevzdani vysledneho dila.
  5. Pri psani kodu vzdy pouzivejte assert na vsechny predpoklady, i na ty, o kterych si stoprocentne myslite, ze preci musi vzdy platit. Neni vetsi zrout ladiciho casu nez nenapadna chyba na jednom miste, ktera se bez zjevne souvislosti fatalne projevi na uplne jinem miste.
  6. Stravit na nepoddajnem problemu 20 nebo vice hodin neni zadna ostuda, ani pro ostrileneho borce. Borec se od neborce lisi jenom tim, na ktere hladine slozitosti zacinaji nepoddajne problemy.
  7. Spilec na zaver: Po odladeni je vzdy nejlepsi cely program zahodit a napsat ho od zacatku znovu, ale to si muze dovolit jen malokdo.

Role automatického hodnocení

Automaticke hodnoceni take neni zamysleno ani prezentovano jako nastroj k odladeni Vaseho programu, natoz pak jediny nastroj.

Pri vyvoji programu byste mel postupovat asi takto:

  1. Ulohu si poradne rozmyslet, napsat na papir v jazyku matematiky.
  2. Promyslene implementovat algoritmus.
  3. Ladit male casti programu, teprve pote celek.
  4. Ladit cely program na datech, spocitanych na papiru.
  5. Ladit primou a inverzni ulohu proti sobe (to je uz postup specificky pro ulohy z Robotiky, povsimnete si, ze vsechny ulohy v Robotice jsou “tam a zase zpatky”. Za normalnich okolnosti si protikus vyvojar musi napsat casto sam.
  6. Ladit pomoci dodane funkce checker_XXX. To plati pro ulohy 4, 5, 6.
  7. Na zaver si zkontrolovat vysledek pomoci automatickeho hodnoceni.

Další odkazy

help/common/ladeni_programu.txt · Last modified: 2018/09/25 09:48 by smutny