Warning
This page is located in archive.

7 - Funkce

Cílem tohoto cvičení je seznámit se se způsoby volání funkcí (metod), předávání argumentů a návratových hodnot v trochu složitějším programu. Konkrétně v programu, jehož účelem je zpracování textového záznamu z průběhu několika experimentů do podoby textové tabulky. Následně si vytvoříme graf průběhu sledovaných parametrů z experimentu.

Vedlejším cílem cvičení je pochopení již hotového kódu a schopnost jej rozšířit, což se ověří i na domácí úloze.

Zároveň si na začátek napíšeme krátký písemný test.

Výchozí program v Javě

Výchozí projekt pr1-lab07.zip se skládá z připravené třídy Lab07, v které se používá rozšířená třída TextIO pro zpracování souboru se záznamy. Projekt také obsahuje třídu Demo, která je určena pro testování předávání argumentů a návratových hodnot funkcí. Pro volání metody start z třídy Demo je nutné modifikovat funkci main ve třídě Start. Hlavní část programu je implementována v metodě processFile, ve které je opakovaně načítán vstupní soubor a průměrná hodnota je vypočtena metodou getAvg. Již používaná třída TextIO v předchozích cvičení je v tomto projektu rozšířena o načítání textového souboru se záznamy. Práce se soubory není náplní tohoto cvičení, proto do této třídy pouze nahlédněte, případně se pokuste porozumět jakým stylem je vstupní soubor zpracován.

Součastí archívu projektu jsou také vstupní datové soubory v adresáři dat a sada skriptů (programů) pro vykreslení grafu získaných hodnot z datových souborů se záznamy experimentů. Tyto skripty jsou uloženy v adresáři scripts a jejich volání předpokládá dva argumenty, první je jméno souboru s tabulkou (vytvoří náš program) a druhý argument specifikuje výstupní pdf soubor, do kterého je vykreslen graf průběhu hodnot.

Výchozí projekt je implementován s využitím pokud možno již probíraných konstruktů a standardních knihovních funkcí Java. Zejména implementace metod readLine a getColumnValue ve třídě TextIO je možné realizovat jinak - elegantněji a obecněji. Náplní dalších cvičení bude profilování tohoto kódu pro velké vstupní soubory, zobecnění a optimalizace zpracování s využitím asociativního pole a spojových seznamů. Zde použitá implementace však poskytuje prostor pro pochopení a rozvinutí zpracování souboru dat a ošetření výjimečných situací.

Informace k procvičovanému tématu

Předávání vstupních parametrů funkcí

Hlavní mechanismus předávání vstupních parametrů funkcí jsou v programovacím jazyku Java argumenty, které se formálně zapisují (a pojmenovávají) v hlavičce funkce (metody). Argumenty funkcí jsou předávány v podstatě pouze hodnotou, můžeme však rozlišit mezi předáváním základního typu nebo referencí (odkazem) na existující objekt (obdoba ukazatele). V prvním případě volání hodnotou se ve funkci vytváří kopie proměnné a nastaví se její hodnota, proto změna této proměnné nemění hodnotu proměnné předchozí do funkce vstupující. Tento způsob předávání je využíván pro základní datové typy, jako jsou int, float, double atd. Příklad použití je například:

 public void print(int n) {
   System.out.println("Input number is: " + n); //tisk hodnoty lokální proměnné na obrazovku, vytiskne 10
   n += 10;
   System.out.println("Now, the number is: " + n); //tisk hodnoty lokální proměnné na obrazovku, vytiskne 20
 }
 
 public void start() {
   int number = 10; //lokální proměnná s rozsahem platnosti v rámci metody start
   print(number); //předání proměnné (její hodnoty) funkci print
   System.out.println("Number is: " + number); //vytiskne 10, protože byla předána hodnota
 }

Druhým mechanismem je předávání odkazem (referencí), který se používá pro složitější datové typy (instance tříd). Při tomto předání není kopírován obsah celého objektu, ale pouze odkaz na vyhrazené místo v paměti, kde se jednotlivá data objektu nacházejí. Je dobré si uvědomit, že vlastně dochází pouze k předání (a kopírování) paměťové adresy, kde se objekt nachází, lokálně tak ve funkci vzniká nová proměnná typu reference, která však ukazuje na identické místo v paměti. Můžeme tak měnit obsah objektů, který takto do funkce vstupuje, ale také můžeme do této lokální proměnné (reference) přiřadit adresu jiného objektu, aniž bychom pak ovlivňovali stav předchozího objektu. To je demonstrováno na následujícím příkladu:

    public void print1(StringBuilder str) {
        System.out.println("Print1: Input string is " + str);
        str.append(", CTU in Prague");
        System.out.println("Print1: Now, the str is " + str);
    }
 
    public void print2(StringBuilder str) {
        System.out.println("Print2: Input string is " + str);
        str = new StringBuilder("CVUT"); //here, we create a new string and add the reference to the local variable str
        System.out.println("Print2: Now, the str is " + str);
    }
 
    public void printStrings(String[] args) {
        StringBuilder str = new StringBuilder("FEE");
        print1(str);
        System.out.println("Main: str is " + str);
        print2(str);
        System.out.println("Main: after calling print2 str is still " + str);
    }

Očekávaný výstup programu je:

Print1: Input string is FEE
Print1: Now, the str is FEE, CTU in Prague
Main: str is FEE, CTU in Prague
Print2: Input string is FEE, CTU in Prague
Print2: Now, the str is CVUT
Main: after calling print2 str is still FEE, CTU in Pragu

Obecně se při psaní programů snažíme vyvarovat přímému zápisu konkrétních “magických” hodnot ve zdrojovém kódu a raději je volíme jako parametry funkcí či atributy tříd/objektů. Jedním z důvodu je čitelnost kódu, ale také jeho znovupožitelnost, neboť parametry můžeme nastavit vně funkce (metody) a můžeme tak metodu přímo použít, aniž bychom byli nuceni zasahovat přímo do jejího zdrojového kódu.

Předávání výstupních hodnot z funkcí

Pro výstupní hodnoty funkce slouží primárně návratový typ funkce, který se specifikuje v definici funkce jménem typu před jménem funkce. Například pro funkci isNumber s návratovou hodnotu typu boolean lze použít následující deklaraci hlavičky funkce.

public boolean isNumber(String str) {
   ...
   return true;
}
Pro návratovou hodnotou je však nutné uvést v těle (definici) funkce klíčové slovo return s uvedením hodnoty nebo proměnné (reference). I zde dochází ke kopírování obsahu proměnné, tj. předání hodnoty základního typu nebo předání hodnoty reference pro proměnné typu objekt (dochází ke kopírování adresy objektu).

Jedním z dobrých zvyků při psaní programu je uvádět ve funkci pouze jediný return a to pokud možno na konci. To nejen zvyšuje čitelnost kódu, ale také umožňuje například vložení dodatečného testu na návratovou hodnotou. Zejména v bezpečnostních a kritických (“safety”) aplikacích je tento požadavek zpravidla součástí kódovací konvence.

Je-li dovoleno používat ve funkci více příkazů return, využíváme jej zpravidla pro předčasný návrat z volání funkce, pokud nejsou splněny vstupní předpoklady, např. hodnoty argumentů nejsou v požadovaných mezích

void print(String[] strs) {
   if (strs == null || strs.length == 0) {
     return;
   }
   ...
  return;
}

Požadujeme-li aby funkce vracela více hodnot nebo předávala více proměnných je nutné použít jako návratový typ složitější objekt, např. kontejner typu ArrayList. Lze také využít předávání argumentů odkazem, které jsou následně ve funkci vyplněny, ale tento způsob je běžný spíše v jazycích C/C++.

Průběh cvičení

Náplní cvičení je vyzkoušet si předávání vstupních parametrů a výstupních hodnot funkcí, které kromě kratičkých demonstračních kódů je dále použito v řešení načítání a zpracování dat. Hlavní náplní cvičení je tak seznámit se s poskytnutým projektem a realizací jednoduchého programu pro zpracování textového záznamu z průběhu několika experimentů, ve kterém je každý experiment dokumentován jedním řádkem, ve kterém je informace o metodě, hodnotě parametru MNE a dvou hodnotách studovaných proměnných DISTANCE a CPUTIME. Jedná se o konrétní data z průběhu experimentů robotického průzkumu, ve kterém studujeme vliv parametru MNE na rychlost průzkumu a požadovaný výpočetní čas. Parameter MNE udává jak často je prováděno plánování, které se skládá z generování cílů a jejich přidělení mobilním robotům provádějícím průzkum. Vliv tohoto parametru analyzujeme pro čtyři metody přidělování cílů robotům, které jsou označeny GA, IA, HA a MA. Ukázku experimentálních mobilních robotů při exploraci a také porovnání exploračních cest pro plánovací metody HA a MA jsou zobrazeny na přiložených obrázcích.

Bližší informace problému, použitích metodách a vyhodnocení exploračních strategii lze nalézt na:

Průběh experimentu je ovlivněn mnoha faktory jako je schopnost lokalizace robotu, a proto lze považovat měrené hodnoty délky nejdelší explorační cesty robotu a potřebný procesorový čas za náhodné veličiny a měření je opakováno pro několik běhu se stejnými parametry. Výsledky jednotlivých běhů jsou uloženy jako zaznámy v souboru, kde na každém řádku je uloženo jedno měření pro konkrétní plánovací metodu a parametr MNE. Soubor tak má strukturu:

METHOD:ia;MNE:10;DISTANCE:61.1212;CPUTIME:116.25
METHOD:ha;MNE:10;DISTANCE:62.1952;CPUTIME:119.09
METHOD:ia;MNE:11;DISTANCE:58.7025;CPUTIME:132.73

Ukázkový vstupní soubor lze najít v dat/results-jh-l3r5.log. Pro vyhodnocení a porovnání jednotlivých metod nás zajímají průměrné hodnoty DISTANCE a CPUTIME v závislosti na parametru MNE v rozsahu 5 až 20 pro každou plánovací metodu. Proto chceme tento soubor zpracovat do podoby přehledové tabulky průměrných hodnot:

MNE	ga	ia	ha
5	77,6	63,9	58,1
6	71,3	60,3	55,8
7	69,4	55,6	56,1
8	70,8	60,8	56,7
9	75,2	59,4	58,5
10	71,2	62,8	60,1
11	70,1	64,0	59,4
12	71,8	61,1	58,6
13	69,2	62,2	60,3
14	73,2	62,9	60,7
15	70,0	62,3	59,9
16	73,3	63,2	62,5
17	75,8	65,3	61,0
18	72,6	62,4	62,3
19	74,4	69,0	64,2
20	73,2	67,4	65,8

Tabulka obsahuje čtyři sloupce oddělené tabulátorem (znak '\t') a na prvním řádku jsou uvedeny jmena sloupců. Tuto tabulku získáme spuštěním výchozího projektu, který ji při načtení vstupního souboru výpíše na standardní výstup. Program můžeme spustit z příkazové řádky voláním:

java -jar dist/lab07.jar

které vytvoří datový soubor jh-l3r5-avg.dat v aktuálním pracovním adresáři. Ten můžeme dále použít pro vykreslení grafu hodnot použitím dodaného skriptu voláním:

./scripts/graph_3methods_distance_avg_only.sh jh-l3r5-avg.dat jh-l3r5-avg.pdf

který vygeneruje PDF soubor zobrazující průběh vzdálenosti pro jednolivé plánovací metody v závislosti na parametru MNE, který je v grafu označen jak s_max.

Graf je vytvořen programem R http://www.r-project.org/ a dodaný skript vytváří předpis (program) pro vytvoření grafu, který je přesměrován ze standardního výstupu skriptu do programu R.
V závislosti na lokálním nastavení může vytvářený datový souboru obsahovat znak '.' nebo ',' jako oddělovač desetinné části. Skript pro vytvoření grafu obsahuje jednoduchou detekci nastavení národního prostředí a předává oddělovač program R. Proto například v případě souboru s desetinnou tečkou a spuštěním skriptu v českém prostředí skončí volání skriptu chybou.

Úkoly na cvičení

  1. Procvičte si předávání parametrů a návratových hodnot diskutované výše. K implementaci využijte třídu Demo.java
  2. Seznamte se s poskytnutým programem, způsoby inicializace, voláním metod a předávání návratových hodnot.
  3. Vytvořte datový soubor jh-l3r5-avg.dat s tabulkou přesměrováním standarního výstupu programu.
  4. Vyzkoušejte si vygenerovat pdf soubor.
  5. Nahraďte výpis na standardní výstup použitím metod printToFile a appendToFile třídy TextIO a seznamte se s jejich implementací.
  6. Rozšiřte program o zpracování průměrných hodnot také pro metodu 'ma' a vytvořte tabulku o pěti sloupcích.
  7. Vykreslete graf pro tuto rozšířenou tabulku skriptem graph_4methods_distance_avg_only.sh a dále již uvažujte pouze zpracování hodnot všech čtyř metod.
  8. Modifikujte program pro vytvoření tabulky s průměrnou hodnotou CPUTIME a vykreslení grafu skriptem graph_4methods_cpu_avg_only.sh
  9. Modifikujte program tak, aby činnost programu bylo možné určit argumenty programu a zároveň byly zachovány všechny funkcionality (inspirujte se příklady z předchozích cvičení).

Až budete mít hotové všechny úkoly ze cvičení (zpracování argumentů programu) i domácí úkol, můžete vyzkoušet zbylé skripty, které zpracovávají i tabulku směrodatných odchylek:

--- Příklad volání programu pro vytvoření souborů s tabulkami
ant jar 
java -jar dist/lab07.jar dat/results-jh-l3r5.log DISTANCE jh-l3r5-avg.dat jh-l3r5-sd.dat
java -jar dist/lab07.jar dat/results-jh-l3r5.log CPUTIME jh-l3r5-ctime-avg.dat jh-l3r5-ctime-sd.dat

--- Příklad vytvoření PDF souborů s grafy a jejich zobrazaní programem xpdf
./scripts/graph_4methods_distance.sh jh-l3r5-avg.dat jh-l3r5-sd.dat jh-l3r5-distance.pdf
./scripts/graph_4methods_cpu.sh jh-l3r5-ctime-avg.dat jh-l3r5-ctime-sd.dat jh-l3r5-ctime.pdf
xpdf jh-l3r5-distance.pdf &
xpdf jh-l3r5-ctime.pdf &

Domácí úkol

courses/a0b36pr1/labs/lab07.txt · Last modified: 2015/11/24 07:42 by schaemar