Cykly

Pro posun na další strany použijte kurzorové šipky

Ⓒ Tomáš Bořil
borilt@gmail.com, 23. 3. 2015

Zadání

Vypište postupně čísla 1 až 10 s krokem 1.

Jedno z možných řešení je deset řádků s příkazem printline. Pojďme se ale naučit efektivnější způsob pro zápis opakovaných příkazů. Až budeme chtít např. ten samý sled příkazů opakovat pro všechny intervaly dlouhého textgridu, oceníme navíc to, že takový zápis je univerzální a nezávislý na skutečném počtu intervalů.

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'
endfor

Použijeme tzv. for cyklus. Za for si vybereme libovolný název proměnné (např. i jako index), dále specifikujeme celočíselný rozsah from první to poslední.

For cyklus postupně nastavuje naši proměnnou na jednotlivá čísla v tomto rozsahu (krok je vždy +1) a pro každou konkrétní hodnotu opakuje tzv. vnitřek cyklu, neboli příkazy mezi řádky for a endfor.

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'
endfor

Zápis čteme: "Pro i od 1 do 10 s krokem +1 opakuj příkazy..."

Estetická poznámka: Ač to není nutné, každý slušný programátor vnitřní příkazy cyklu odsazuje. Např. o čtyři mezery či o celý tabulátor. Na první pohled je tak vidět, co je vnitřek cyklu a které příkazy patří ke kterým. U větších skriptů s vnořenými cykly a mnoha podmínkami oceníte přehlednost, kterou odsazování přináší.

Čísla 1 až 10 s krokem 1

clearinfo                                          <==

for i from 1 to 10
    printline 'i'
endfor

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10                                 <==
    printline 'i'
endfor

i: 1

Nastavení první hodnoty i = 1. Protože není větší než maximální hodnota 10, další krok začne vykonávat příkazy uvnitř cyklu.

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'                                  <==
endfor

i: 1

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'
endfor                                             <==

Skok na začátek for cyklu.

i: 1

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10                                 <==
    printline 'i'
endfor

i: 1 2

Nová hodnota i = 2 není větší než 10, takže budeme pokračovat vnitřkem cyklu.

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'                                  <==
endfor

i: 1 2

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'
endfor                                             <==

i: 1 2

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10                                 <==
    printline 'i'
endfor

i: 1 2 3

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'                                  <==
endfor

i: 1 2 3

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'
endfor                                             <==

i: 1 2 3

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10                                 <==
    printline 'i'
endfor

i: 1 2 3 4

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'                                  <==
endfor

i: 1 2 3 4

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'
endfor                                             <==

i: 1 2 3 4

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10                                 <==
    printline 'i'
endfor

i: 1 2 3 4 5

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'                                  <==
endfor

i: 1 2 3 4 5

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'
endfor                                             <==

i: 1 2 3 4 5

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10                                 <==
    printline 'i'
endfor

i: 1 2 3 4 5 6

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'                                  <==
endfor

i: 1 2 3 4 5 6

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'
endfor                                             <==

i: 1 2 3 4 5 6

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10                                 <==
    printline 'i'
endfor

i: 1 2 3 4 5 6 7

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'                                  <==
endfor

i: 1 2 3 4 5 6 7

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'
endfor                                             <==

i: 1 2 3 4 5 6 7

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10                                 <==
    printline 'i'
endfor

i: 1 2 3 4 5 6 7 8

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'                                  <==
endfor

i: 1 2 3 4 5 6 7 8

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'
endfor                                             <==

i: 1 2 3 4 5 6 7 8

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10                                 <==
    printline 'i'
endfor

i: 1 2 3 4 5 6 7 8 9

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'                                  <==
endfor

i: 1 2 3 4 5 6 7 8 9

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'
endfor                                             <==

i: 1 2 3 4 5 6 7 8 9

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10                                 <==
    printline 'i'
endfor

i: 1 2 3 4 5 6 7 8 9 10

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'                                  <==
endfor

i: 1 2 3 4 5 6 7 8 9 10

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'
endfor                                             <==

i: 1 2 3 4 5 6 7 8 9 10

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10                                 <==
    printline 'i'
endfor

i: 1 2 3 4 5 6 7 8 9 10 11

Hodnota i = 11 je větší než maximální hodnota 10, a proto cyklus již nevstoupí dovnitř, ale skočí na konec za endfor, kde by skript případně pokračoval dalšími příkazy.

Čísla 1 až 10 s krokem 1

clearinfo

for i from 1 to 10
    printline 'i'
endfor

i: 1 2 3 4 5 6 7 8 9 10 11

Důležité je uvědomit si, že cyklus skutečně skončil s hodnotou i = 11, a proto již nezačal vykonávat svůj vnitřek. Kdybychom na dalších řádcích za endfor ve skriptu zkusili vypsat hodnotu i pomocí printline 'i', skutečně by se vypsalo číslo 11, zkuste si to.

Závěrem k prvnímu cyklu

clearinfo

for i from 1 to 10
    printline 'i'
endfor

Všimněte si, že proměnná i se neustále přepisovala novými hodnotami. Není to tedy paměť, která by si pamatovala celou historii. Vždy si pamatuje pouze aktuální hodnotu.

Protože jsme ji postupně vypisovali do Praat Info, tak tuto historii vidíme.

i: 1 2 3 4 5 6 7 8 9 10 11

Zadání

Vypište postupně čísla 3 až 8 s krokem 1.

Krok zůstává +1, takže stačí jednoduše modifikovat meze for cyklu z předchozího příkladu. Zkuste si skript vyrobit sami, než se přepnete na následující stránku s řešením.

Čísla 3 až 8 s krokem 1

clearinfo

for i from 3 to 8
    printline 'i'
endfor

Zadání

Vypište postupně čísla 10 až 1 s krokem -1 (tedy 10, 9, 8, ..., 1).

Zde nastává problém, protože na rozdíl od většiny klasických programovacích jazyků Praat neumožňuje ve for cyklu specifikovat krok. Musíme tedy zavést pomocnou proměnnou a vymyslet přepočet. To bývá velice komplikované. Dobrou zprávou je, že takováto zadání budeme muset v praxi pomocí Praatu řešit asi jen málokdy, spíše nikdy. Pro cvičení to ale zkusme vymyslet.

Čísla 10 až 1 s krokem -1

  1. Celkem to bude 10 čísel, vytvoříme opět for cyklus s proměnnou i, ale v rozsahu 0 až 9 s krokem +1.

  2. Čísla i potřebujeme šikovně zvoleným vzorcem přepočítat na naše čísla (tedy 0 → 10, 1 → 9, 2 → 8 atd.)

  3. Všimněme si, že čísla jdou přesně s opačným krokem, i tedy vynásobíme -1.

  4. Nyní ale potřebujeme přičíst konstantní číslo, aby první číslo nebylo 0, ale 10.

  5. Přičteme tedy 10.

  6. Celý vzorec bude vypadat cislo = -1*i + 10 (Pozn. * značí násobení, / by znamenalo dělení).

Čísla 10 až 1 s krokem -1

clearinfo

for i from 0 to 9
    cislo = -1*i + 10
    printline 'cislo'
endfor

Ukázka přepočítaných hodnot

i 0 1 2 3 4 5 6 7 8 9
cislo 10 9 8 7 6 5 4 3 2 1

Zadání

Vypište postupně čísla 2 až 3 s krokem 0.25.

  1. Kolik to bude čísel?

  2. Celkem 5, vytvoříme tedy for cyklus s proměnnou i od 0 do 4.

  3. Krok je 0.25, kolika budeme násobit ve vzorci čísla i?

  4. Budeme násobit 0.25, tedy cislo = 0.25*i.

  5. První číslo zatím vychází 0. Co musíme ještě přičíst, aby to bylo 2?

  6. Přičteme 2. Vzorec tedy vypadá cislo = 0.25*i + 2.

Čísla 2 až 3 s krokem 0.25

clearinfo

for i from 0 to 4
    cislo = 0.25*i + 2
    printline 'cislo'
endfor

Ukázka přepočítaných hodnot

i 0 1 2 3 4
cislo 2 2.25 2.5 2.75 3

Zadání

Vypište postupně čísla 100 až 10 s krokem -10.

Zkuste to nyní vymyslet celé sami, použijte pořád tu samou logiku.

  1. Kolik to bude čísel?

  2. Vytvořte for cyklus s proměnnou i od 0 do pocetCisel-1.

  3. Vytvořte vzorec pro novou proměnnou, i se násobí krokem.

  4. Ve vzorci připočtěte takovou hodnotu, aby počáteční číslo bylo správně.

  5. Na následující stránce je již celé řešení.

Čísla 100 až 10 s krokem -10

clearinfo

for i from 0 to 9
    cislo = -10*i + 100
    printline 'cislo'
endfor

Ukázka přepočítaných hodnot

i 0 1 2 3 4 5 6 7 8 9
cislo 100 90 80 70 60 50 40 30 20 10

Zadání

Vypište hodnoty odpovídající času v sekundách v rozsahu 20 ms až 50 ms s krokem 10 ms.

Výstupem tedy mají být na samostatných řádcích čísla 0.02, 0.03, 0.04 a 0.05.

Zkuste to opět vyrobit sami, na následující stránce je jedno z možných řešení.

Tip: Přehlednější bude provést výpočet v ms, a teprve pak výsledné číslo vydělit 1000.

Časy 20 ms až 50 ms s krokem 10 ms (v sekundách)

clearinfo

for i from 0 to 3
    cas = (10*i + 20) / 1000
    printline 'cas'
endfor

Ukázka přepočítaných hodnot

i 0 1 2 3
cislo 0.02 0.03 0.04 0.05

Cyklus while

Také vám vadí, že je nutno vymýšlet přesný počet hodnot a ne příliš přehledný převodní vztah?

Existuje výrazně jednodušší řešení, a to pomocí cyklu while.

  1. Před začátkem cyklu while si nastavíme počáteční hodnoty proměnných podle potřeby.

  2. V cyklu while specifikujeme podmínku, do kdy se má vnitřek cyklu opakovat.

  3. Uvnitř cyklu se opakují příkazy. Zde musíme zajistit to, aby jednou došlo k nesplnění podmínky a cyklus se ukončil. Jinak dojde k nekonečnému zacyklení a skript nikdy neskončí.

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10
endwhile

printline Konec.

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo                                          <==

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10
endwhile

printline Konec.

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20                                         <==

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10
endwhile

printline Konec.

casMS: 20

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50                                  <==
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10
endwhile

printline Konec.

Je casMS menší nebo rovno 50? Ano, je. Proto vstoupíme dovnitř cyklu.

casMS: 20

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000                             <==
    printline 'cas'
    casMS = casMS + 10
endwhile

printline Konec.

casMS: 20
cas: 0.02

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'                                <==
    casMS = casMS + 10
endwhile

printline Konec.

casMS: 20
cas: 0.02

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10                             <==
endwhile

printline Konec.

casMS: 20 30
cas: 0.02

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10
endwhile                                           <==

printline Konec.

Skok na začátek cyklu while.

casMS: 20 30
cas: 0.02

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50                                  <==
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10
endwhile

printline Konec.

Je casMS menší nebo rovno 50? Ano, je. Proto vstoupíme dovnitř cyklu.

casMS: 20 30
cas: 0.02

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000                             <==
    printline 'cas'
    casMS = casMS + 10
endwhile

printline Konec.

casMS: 20 30
cas: 0.02 0.03

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'                                <==
    casMS = casMS + 10
endwhile

printline Konec.

casMS: 20 30
cas: 0.02 0.03

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10                             <==
endwhile

printline Konec.

casMS: 20 30 40
cas: 0.02 0.03

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10
endwhile                                           <==

printline Konec.

Skok na začátek cyklu while.

casMS: 20 30 40
cas: 0.02 0.03

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50                                  <==
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10
endwhile

printline Konec.

Je casMS menší nebo rovno 50? Ano, je. Proto vstoupíme dovnitř cyklu.

casMS: 20 30 40
cas: 0.02 0.03

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000                             <==
    printline 'cas'
    casMS = casMS + 10
endwhile

printline Konec.

casMS: 20 30 40
cas: 0.02 0.03 0.04

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'                                <==
    casMS = casMS + 10
endwhile

printline Konec.

casMS: 20 30 40
cas: 0.02 0.03 0.04

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10                             <==
endwhile

printline Konec.

casMS: 20 30 40 50
cas: 0.02 0.03 0.04

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10
endwhile                                           <==

printline Konec.

Skok na začátek cyklu while.

casMS: 20 30 40 50
cas: 0.02 0.03 0.04

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50                                  <==
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10
endwhile

printline Konec.

Je casMS menší nebo rovno 50? Ano, je. Proto vstoupíme dovnitř cyklu.

casMS: 20 30 40 50
cas: 0.02 0.03 0.04

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000                             <==
    printline 'cas'
    casMS = casMS + 10
endwhile

printline Konec.

casMS: 20 30 40 50
cas: 0.02 0.03 0.04 0.05

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'                                <==
    casMS = casMS + 10
endwhile

printline Konec.

casMS: 20 30 40 50
cas: 0.02 0.03 0.04 0.05

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10                             <==
endwhile

printline Konec.

casMS: 20 30 40 50 60
cas: 0.02 0.03 0.04 0.05

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10
endwhile                                           <==

printline Konec.

Skok na začátek cyklu while.

casMS: 20 30 40 50 60
cas: 0.02 0.03 0.04 0.05

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50                                  <==
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10
endwhile

printline Konec.

Je casMS menší nebo rovno 50? Ne, není. Skočíme za konec cyklu a pokračujeme dále.

casMS: 20 30 40 50 60
cas: 0.02 0.03 0.04 0.05

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10
endwhile

printline Konec.                                   <==

casMS: 20 30 40 50 60
cas: 0.02 0.03 0.04 0.05

Časy 20 ms až 50 ms s krokem 10 ms pomocí while

clearinfo

casMS = 20

while casMS <= 50
    cas = casMS / 1000
    printline 'cas'
    casMS = casMS + 10
endwhile

printline Konec.

A to je konec skriptu. Aktuální hodnota v proměnné casMS je sice 60, ale ta již nebyla vypsána, protože pro ní se vnitřek cyklu neprovedl.

casMS: 20 30 40 50 60
cas: 0.02 0.03 0.04 0.05

Varování: desetinná čísla v počítači

Kde to jde, vyhýbejte se v počítači porovnávání desetinných čísel.

V předchozím příkladu jsme sice chtěli vypisovat desetinná čísla, ale v cyklu jsme počítali s celými čísly (postupné přičítání), teprve pak jsme dělili tisícem.

Takový přístup je chytrý a bezpečný. Vnitřní aritmetika mikroprocesoru v počítači počítá ve skutečnosti v tzv. dvojkové (binární) soustavě a výpočty, které nám v naší desítkové soustavě připadají naprosto bezpečné a přesné, mohou v binární soustavě vést např. na nekonečně periodická čísla, a může tudíž docházet k nepřesnostem vlivem zaokrouhlování.

Na takové problémy narazíme např. při postupném přičítání desetinných čísel, a zvláště pak při porovnávání dvou desetinných čísel s dotazem, zda se rovnají. Je to překvapivé, ale ve většině programovacích jazyků skončí např. dotaz na rovnost čísel 3.14*5 a 15.7 negativní odpovědí, podle procesoru počítače se obě čísla nerovnají!

Varování: desetinná čísla v počítači

clearinfo
cislo = 0

while cislo <= 15.7
    printline 'cislo'
    cislo = cislo + 3.14
endwhile

Číslo 15.7 se již nevypíše, přestože by v naší desítkové soustavě mělo bez problémů podmínku splnit. Výpočty ale interně probíhají ve dvojkové (binární) soustavě a díky postupnému přičítáná to najednou není přesně 15.7, ale např. číslo o nepatrný kousek větší (a nic na tom nemění to, že printline svůj výpis zaoukrouhlí na menší počet míst, takže si nepřesnosti běžně nevšimneme!).

Tip pro zvídavé: zkuste uvnitř cyklu vypočítat ještě proměnnou rozdil = cislo - 15.7 a vypisujte ji. Nepřesnosti budou patrné.

Konec