Psaní a ladě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á. Mezi funkce, používané v robotice, které je třeba ošetřit patří: dělení nulou, odmocňování záporného argumentu, arccos argumentu mimo interval od -1 do 1.
  • 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 většině kybernetických předmětů) napsat co nejúspornější kód, ale napsat co nejrychleji plně funkční kód. Kratší kód se ale většinou lépe ladí.
  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í v případě, že na žádnou kombinaci nezapomenete.
  • 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. 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.
  • Občas se splete proměnná “i”, proměnná “l” a znak “1”.
  • Proměnná “i” je obzvláště nebezpečná, v matlabu 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í.” Je třeba si ale uvědomit, že dva rozdílné kódy implementující matematicky ekvivalentní myšlenky se mohou lišit ve výsledku vlivem zaokrouhlování. Jedním způsobem tak můžete dostat například záporný, jiným kladný výsledek.
  • 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.
  • V Evropě nepracujeme v palcích, ale chvíli počítat ve stupních a chvíli v radiánech není tak neobvyklé.

Ještě jiný pohled

  1. To, že jsou v programu chyby, je normální. Inženýr s tím musí umět zápasit a žít. Je to náročné, ale když se to nenaučíme na škole, tak kdy?
  2. Nejprve si dobře rozmyslete strukturu problému. Papír, tužka, jazyk matematických vzorců a pohodlné křeslo daleko od klávesnice jsou základní nástroje.
  3. Pro každý podproblém si sestrojte (syntetický) testovací příklad.
  4. Budujte řešení zespoda, nejprve vyřešte menší podproblémy, udělejte z nich funkce (procedury) s jasné definovaným rozhraním (argumenty), a pak to postupně spojujte dohromady, každé dílčí řešení nebo jejich spojení ihned testujte a výsledky si vizualizujte. Matlab je na to skvělý nástroj. Jakmile něco nesedí, je třeba vrátit se zpět, často až ke křeslu a papírů se vzorečky. Pro složitější věci si neváhejte v Matlabu udělat interaktivní nástroj, úsilí se bohatě vyplatí. Testovací a verifikační kódy si vždy schovejte, aspoň do doby odevzdání výsledného díla.
  5. Při psaní kódu vždy používejte assert na všechny předpoklady, i na ty, o kterých si stoprocentně myslíte, že přeci musí vždy platit. Není větší žrout ladícího času než nenápadná chyba na jednom místě, která se bez zjevné souvislosti fatálně projeví na úplně jiném místě.
  6. Strávit na nepoddajném problémů 20 nebo více hodin není žádná ostuda, ani pro ostříleného borce. Borec se od neborce liší jenom tím, na které hladině složitosti začínají nepoddajné problémy.
  7. Špílec na závěr: Po odladění je vždy nejlepší celý program zahodit a napsat ho od začátku znovu, ale to si může dovolit jen málokdo.

Role automatického hodnocení

Automatické hodnocení také není zamýšleno ani prezentováno jako nástroj k odladění Vašeho programu, natož pak jediný nástroj.

Při vývoji programu byste měli postupovat asi takto:

  1. Úlohu si pořádně rozmyslet, napsat na papír v jazyce matematiky.
  2. Promyšleně implementovat algoritmus.
  3. Ladit malé části programu, teprve poté celek.
  4. Ladit celý program na datech, spočítaných na papíře.
  5. Ladit přímou a inverzní úlohu proti sobě (to je už postup specifický pro úlohy z Robotiky, povšimněte si, že všechny úlohy v Robotice jsou “tam a zase zpátky”. Za normálních okolností si protikus vývojář musí napsat často sám.
  6. Ladit pomoci dodané funkce checker_XXX. To platí pro úlohy 4, 5, 6.
  7. Na závěr si zkontrolovat výsledek pomoci automatického hodnocení.

Další odkazy

help/common/ladeni_programu.txt · Last modified: 2021/12/09 19:16 by smutny