====== 6 - Cykly ====== Cílem cvičení je seznámit se se způsoby zápisu cyklů a procvičit si zpracování posloupnosti hodnot. Dále pak rozvinout dovednosti programování a kompilace programu z příkazové řádky, zadávání vstupních argumentů programu a přesměrováním standardního vstupu a výstupu programu z/do souboru. ===== Výchozí program v Javě ===== Výchozí projekt obsahuje rozšířenou implementaci třídy TextIO z minulého cvičení, která nově zapouzdřuje volání metody třídy ''Scanner'' pro načítání celého řádku ze standardního vstupu. Při vytvoření nové instance třídy ''TextIO'' dojde také k vytvoření instance třídy ''Scanner'', která je rovnou napojena na standardní vstup ''System.in''. Výchozí projekt si stáhněte z archívu: {{:courses:a0b36pr1:labs:pr1-lab06.zip|}}. Struktura zdrojových kódu je podobná projektům předchozích cvičení i zde je hlavní třída ''Start'', ze které je volána metoda ''start'' třídy ''Lab06''. V metodě ''start'' je však nově implementováno zpracování prvního vstupního argumentu, který určí jaká čast programu bude po spuštění vykonána. Všiměte si, že pokud není zadán žádný argument volá se příslušná část programu odpovídající hodnotě konstantní proměnné ''DEFAULT_PART''. Pro tento účel je využito ternárního operátoru: final int DEFAULT_PART = 2; int part = (args.length > 0 && TextIO.isInteger(args[0]) ? Integer.parseInt(args[0]) : DEFAULT_PART); ===== Informace k procvičovanému tématu ===== ==== Cykly ==== === for cyklus === for (inicializace; podmínka; změna) { tělo cyklu } * //Inicializace// u ''for'' cyklu se provede pouze před prvním provedením těla cyklu. * //Podmínka// je ověřována před každým provedením těla cyklu. * //Tělo cyklu// se provede pouze, pokud je podmínka splněna. * //Změna// se provede vždy až po provedení těla cyklu. Použití preinkrementu či postinkrementu tuto vlastnost neovlivní. * U for cyklu lze vynechat libovolnou část příkazu for. Cyklus for(;;) bude nekonečný. === for cyklus - příklad === cyklus vypíše prvních deset násobků čísla ''i''. int i = 3; for (int k=1; k<=10; k++) { int j = i * k; System.out.print(j + ", "); } === while cyklus === while(podmínka) { tělo cyklu } Nejprve se otestuje platnost podmínky. Je-li splněna, provede se tělo cyklu a poté se opět otestuje platnost podmínky. V případě nesplnění podmínky při prvním testování, neproběhne tělo cyklu ani jednou. === while cyklus - příklad === Cyklus vypíše násobky čísla i až do hodnoty menší než 100. int i = 5, j = 0; while (j < 100) { System.out.print(j + ", "); j += i; } === do - while cyklus === do { tělo cyklu } while (podmínka); * Nejprve se provede //tělo cyklu// a teprve po jeho prvním provedení se otestuje podmínka. Znovu se tělo cyklu provede pouze v případě platnosti podmínky. * Narozdíl od while cyklu se tělo do - while cyklu provede alespoň jednou. === do - while cyklus - příklad === Co dělá následující kód? java.util.Scanner in = new java.util.Scanner(System.in); int a; do { a = in.nextInt(); suma += a; if (a!=0) { soucet++; } } while (a!=0); === Příkaz break === * Příkaz ''break'' ukončí provádění cyklu a pokračuje zpracováním kódu za tělem cyklu. * Výstupem tohoto kódu for (int i=1; i<4; i++) { for (int j=1; j<4; j++) { if (i==2 && j==2) { break; } System.out.println(j + " "); } } bude posloupnost 1 2 3 1 1 2 3 === Příkaz continue === * Příkaz continue ukončí provádění těla cyklu a v případě splnění podmínky znovu zahájí zpracování těla cyklu. * Výstupem tohoto kódu for (int i=1; i<4; i++) { for (int j=1; j<4; j++) { if (i==2 && j==2) { continue; } System.out.print(j + " "); } } bude posloupnost 1 2 3 1 3 1 2 3 ==== Kompilace programu z příkazové řádky ==== Kromě kompilace a spuštění programu ve vývojovém prostředí je součástí standarního vývojářského balíku Java SDK také kompilátor a vlastní virtuální stroj JVM (Java Virtual Machine). Přestože při vývoji je zpravidla pohodlnější využít jak pro kompilaci tak pro spouštění programu vývojového prostředí (např. Netbeans) jsou situace, kdy je výhodnější sestavovat program přímo příkazem. Takové případy jsou například servery automatické kompilace a ověřování zdrojového kódu, u kterého při vývoji ve více členém týmu probíhají modifikace na různých částech a jedním z hlavním problémů je integrace a správné provázání jednotlivých částí. Předklad zdrojového kódu pak probíha kontinuálně tak jak jsou přidávány do projektu nové funkcionality a je vhodné celý proces automatizovat. Právě v takových situací jsou využívány nástroje pro řízení překladu a jedním z nich je systém Apache Ant ( [[http://ant.apache.org/]] ). Ten vyniká jednoduchou syntax zápisu pravidel překladu [[http://www.root.cz/clanky/ant-nebojte-se-mravence/]] a lze jej považovat za de facto standard pro překlad Java projektů. Rovněž vývojové prostředí Netbeans využívá tento systém pro překlad projektů. Postup sestavení projektu je zapisovám v XML souboru ''build.xml'' a ukázku takového souboru můžete najít v poskytovaných projektech na jednotlivá cvičení. Pro naše účely vystačíme se základním použitím a vyjdeme právě z dodaného souboru. Předtím si však připomene sestavení programu kompilátorem javac. Nechť aktuální pracovní adresář obsahuje zdrojové kódy poskytnutého projektu ''pr1-lab06''. Překlad jednolivých souborů lze provést příkazy --- Výpis obsahu adresáře zdrojových souborů v balíčku cz.cvut.fel.pr1 ls src/cz/cvut/fel/pr1 Lab06.java Start.java TextIO.java --- Překlad souborů javac src/cz/cvut/fel/pr1/TextIO.java javac -classpath src src/cz/cvut/fel/pr1/Start.java javac -classpath src src/cz/cvut/fel/pr1/Lab06.java --- Výpis obsahu adresáře, nově se zkompilovanými soubory .class ls src/cz/cvut/fel/pr1 Lab06.class Lab06.java Start.class Start.java TextIO.class TextIO.java Všiměte si, že pro překlad tříd ''Start'' a ''Lab06'' je nutné specifikovat dodatečnou cestu, kde má kompilátor hledat závislosti, neboť tyto třídy využívají naši novou třídu ''TextIO'', která není standardní součástí Java SDK. Proto specifikujme cestu k balíku přepínačem -''classpath'' (zkráceně -''cp'') s jedním argumentem. Spuštění programu (přeloženého bytekódu) provedeme příkazem ''java'', kde je nutné uvést plné jméno třídy, tj. včetně jména balíku, ve kterém se třída nalézá. Jméno balíku je uvedeno ve zdrojového souboru za klíčovým slovem ''package''. V našem případě spouštíme třídu ''Start'' z balíku ''cz.cvut.fel.pr1'': --- Spuštění třídy Start a výstup programu pro část part1() java -classpath src cz.cvut.fel.pr1.Start i: 00 - value 0.00 i: 01 - value 0.10 i: 02 - value 0.20 i: 03 - value 0.30 i: 04 - value 0.40 i: 05 - value 0.50 i: 06 - value 0.60 i: 07 - value 0.70 i: 08 - value 0.80 i: 09 - value 0.90 V tomto případě specifikujeme cestu, kde má JVM hledat třídy přepínačem -''classpath'' (-''cp'') s uvedením cesty k adresářové struktuře, tj. kde jsou uloženy zkombilované třídy (.class). Tento způsob překladu však není příliš komfortní a použitelný je jen pro velmi malé projekty. Nicméně sofistikvanější nástroje nakonec nedělají nic jiného, než že generují takováto individuální volání překladače ''javac''. V naše případě využijeme pro sestavení projektu příkaz ''ant'', který v základní konfiguraci hledá soubor ''buidl.xml'' v aktuálním pracovním adresáři. Po načtení souboru s definicí překladu spustí překlad pro definovaný cíl, který je možné specifikovat prvním argumentem. Pro jednoduchost využijeme cíl ''jar'', který nejen že přeloží příslušné zdrojové soubory projektu, ale také vytvoří archív (zip) přeložených souboru spolu s označením hlavní spustitelné třídy v balíku (v takzvaném manifestu1). --- Příklad výpisu při sestavení projektu příkazem ant ant jar Buildfile: build.xml build: [echo] Build Example [mkdir] Created dir: build [javac] Compiling 3 source files to build jar: [jar] Updating jar: dist/lab06.jar BUILD SUCCESSFUL Total time: 2 seconds Všiměte si, že přeložené třídy .class jsou uloženy v adresáři ''build'' a výsledný jar balík je pak umístěn v adresáři ''dist''. Nyní můžeme program spustit přímo z jar: --- Výpis přeložení souborů ls build/cz/cvut/fel/pr1 Lab06.class Start.class TextIO.class --- Výpis vytvořeného distribučního balíku ls dist lab06.jar --- Spuštění balíku java -jar dist/lab06.jar i: 00 - value 0.00 i: 01 - value 0.10 ... Výhodou výtvořeného balíku je snadné spuštění což v kombinaci s přenositelností Java byte kódu umožňuje jednoduše zkopírovat balík ''lab06.jar'' na jiný počítač a spustit jej, je-li vybaven instalací JVM. Přestože je systém ''ant'' mocným nástrojem a dobře použitelný pro řadu projektů, v současné době je spíše používán jeho následovník ''Maven'' [[http://maven.apache.org/]]. Ten představuje moderní nástroj a jeho syntax je však o něco složitější a vyžaduje hlubší nastudování problematiky. To je však odměněno jeho bohatými možnostmi. ===== Průběh cvičení ===== ==== Část 1 ==== Nejdříve se seznamte s výpisem deseti hodnot v části ''part1''(). Program modifikujte tak, aby vypsal čísla od deseti do jedné. --- Příklad očekávaného výstupu programu i: 10 - value 1.00 i: 09 - value 0.90 i: 08 - value 0.80 i: 07 - value 0.70 i: 06 - value 0.60 i: 05 - value 0.50 i: 04 - value 0.40 i: 03 - value 0.30 i: 02 - value 0.20 i: 01 - value 0.10 ==== Část 2 ==== Do části ''part2''() napište program, který - Vypíše všechna sudá čísla v intervalu 1 až 20 - a následně čísla dělitelná sedmi v intervalu 13 až 39. --- Ukázka výpisu volání programu s argumentem 2 pro spuštění druhé části part2() java -jar dist/lab06.jar 2 Even numbers: 2 4 6 8 10 12 14 16 18 20 Numbers divisible by 7: 14 21 28 35 V metodě ''start'' modifikujte větvění tak, aby v případě hodnoty proměnné ''PART == 1'' došlo k volání jak part1() tak part2(). switch(PART) { case 1: part1(); case 2: part2(); break; ... } Očekávaný výpis při spuštění programu s argumentem 1 pro první část je: i: 10 - value 1.00 i: 09 - value 0.90 i: 08 - value 0.80 i: 07 - value 0.70 i: 06 - value 0.60 i: 05 - value 0.50 i: 04 - value 0.40 i: 03 - value 0.30 i: 02 - value 0.20 i: 01 - value 0.10 Even numbers: 2 4 6 8 10 12 14 16 18 20 Numbers divisible by 7: 14 21 28 35 ==== Část 3 ==== Napište program v části ''part3()'', který načte ze standardního vstupu celočíselnou hodnotu a zjistí, zda-li je číslo prvočíslo. Vstupní hodnotu testujte, zda-li je celým číslem metodou ''TextIO.isInteger'' a v případě neočekávaného vstupu vypište chybovou hlášku na standardní chybový výstup. Testujte metodu pro různě veliké vstupy a v případě nepřiměřeně dlouhého výpočtu omezte vstupní hodnotu. Kromě testu dělení čísla až do jeho výše minus jedna, je možné test zrychlit testováním až do výše zaokrouhlené druhé odmocniny testovaného čísla. [[http://en.wikipedia.org/wiki/Primality_test]] --- Ukázka výpisu java -jar dist/lab06.jar 3 Enter an integer number > 2 and < 10000000 11 Given number 11 is prime number --- Ukázka výpisu java -jar dist/lab06.jar 3 Enter an integer number > 2 and < 10000000 6257134 Given number 6257134 is not prime number --- Ukázka chybového výpisu java -jar dist/lab06.jar 3 Enter an integer number > 2 and < 10000000 0 Given number is out of range --- Ukázka chybového výpisu java -jar dist/lab06.jar 3 Enter an integer number > 2 and < 10000000 2 Given number is out of range --- Ukázka chybového výpisu java -jar dist/lab06.jar 3 Enter an integer number > 2 and < 10000000 eleven It is not an integer number ==== Část 4 ==== Seznamte se s metodou ''part4''() poskytnutého projektu, která počítá součet hodnot z posloupnosti zadaných čísel ze standardního vstupu. Všiměte si, jak je testován konec vstupu. Vstupní hodnoty jsou čteny jako textové řetězce po řádcích, přičemž se předpokládá, že na každém řádku je jedno číslo. Pokud řádek neobsahuje číslo, je jeho obsah ignorován. Načítání je ukončeno, pokud je detekován konec vstupu, pro který lze použít znak EOF (End-Of-File). Při vstupu z klávesnice je zpravidla možné tento znak zapsat kombinací CTRL+D. java -jar dist/lab06.jar 4 1 2 3 4 5 End of input detected! Sum of the 5 input numbers is 15.0 Program rozšiřte o výpočet průměrné hodnoty. S očekávaným výstupem ve tvaru java -jar dist/lab06.jar 4 1 2 3 4 5 End of input detected! Sum of the 5 input numbers is 15.0 Avg of the 5 input numbers is 3.0 ==== Část 5 ==== V páté části cvičení si vyzkoušíme překlad programu z příkazové řádky a jeho následné spuštění s předáním vstupního argumentu pro výběr příslušné části programu, která bude vykonána. Také si vyzkoušíme přesměrování standardního vstupu ze souboru. Nejdříve si otevřete terminál a přepněte se do adresáře se staženými projektovými soubory, např. ''cd ~/pr1/pr1-lab06''. V adresáři najdete zdrojové soubory v adresářové struktuře ''src'' a dále pak soubor pro řízení předkladu ''build.xml'' programu ''ant'', případně další projektové soubory vytvořené prostředím Netbeans. --- Překlad zdrojových souborů ant jar V archívu s projektovými soubory je také příklad vstupního souboru ''input.txt'' pro část ''part4''(). Obsah textového souboru můžeme zobrazit příkazem ''cat'': --- Výpis obsahu souboru input.txt na standardní výstup cat input.txt 23 3 1 3 4 Tento soubor použijeme jako vstup našeho programu. Nejdříve tak, že přesměrujeme standardní vstup na tento soubor při spuštění programu: --- Spuštění programu s přesměrovaným vstupem (a argumentem 4 pro spuštění 4. části) java -jar dist/lab06.jar 4 Alternativně můžeme použít standardní výstup jednoho programu jako standardní vstup jiného programu prostřednictvím znaku svislítko | (pipe): --- Spuštění programu s přesměrovaným vstupem jako výstupem programu cat cat input.txt | java -jar dist/lab06.jar 4 End of input detected! Sum of the 5 input numbers is 34.0 Avg of the 5 input numbers is 6.8 Dále můžeme přesměrovat standardní výstup programu --- Spuštění programu s přesměrovaným standardním výstupem do souboru output.txt cat input.txt | java -jar dist/lab06.jar 4 >output.txt End of input detected! a také můžeme přesměrovat standardní chybový výstup, například do souboru err.txt --- Spuštění programu s přesměrovaným standardním výstupem do souboru output.txt a chybovým výstupem do err.txt cat input.txt | java -jar dist/lab06.jar 4 >output.txt 2>err.txt V pracovním adresáři vzniknout dva soubory ''output.txt'' a ''err.txt'', které můžete vypsat na obrazovku příkazem ''cat'' nebo otevřít ve vašem oblíbeném textovém editoru, např. ''gedit''. Více o přesměrování se můžete dozvědět například na * [[https://www.pslib.cz/ke/BASH:_P%C5%99esm%C4%9Brov%C3%A1n%C3%AD]] nebo * [[http://www.poznejlinux.cz/pokrocili/komandlajna/io_roury_filtry]]. V případě používání alternativního operačního systému pak třeba přímo na stránkách jeho tvůrce * [[http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx?mfr=true]]. ===== Domácí úkol ===== [[courses:a0b36pr1:hw:lab06|]]