Obsah
1. Programovací jazyk C3: podmínky, rozvětvení a programové smyčky
2. Rozvětvení běhu programu založené na konstrukcích if a if-else
3. Porovnání s jazykem C: nekorektní způsoby zápisu konstrukce if-else
4. Složitější rozvětvení založené na konstrukci if-else if-else
5. Základní forma rozvětvení založeného na konstrukci switch-case
6. Větší množství podmínek a konstrukce switch-case
7. Podmínky v konstrukci switch-case vyhodnocované v čase běhu procesu
8. Konstrukce switch-case a příkaz break
9. Klíčové slovo nextcase v programové konstrukci switch-case
10. Základní forma počítané programové smyčky for
11. Průchod polem či jinou sekvencí smyčkou typu foreach
12. Průchod polem smyčkou foreach se získáním indexu i prvku v každé iteraci
13. Modifikace prvků pole při jeho průchodu smyčkou foreach
14. Modifikace prvků pole přes ukazatel získaný v každé iteraci smyčky foreach
15. Průchod prvky sekvence v opačném pořadí s využitím smyčky foreach_r
16. Programová smyčka typu while
18. Vnořené programové smyčky a příkaz break
19. Repositář s demonstračními příklady
1. Programovací jazyk C3: podmínky, rozvětvení a programové smyčky
V předchozích třech článcích o programovacím jazyce C3 jsme si mj. řekli, že tento jazyk má poměrně velké množství rezervovaných klíčových slov. Všechna tato slova jsou vypsána pod tímto odstavcem:
void bool char double float float16 int128 ichar int iptr isz long short uint128 uint ulong uptr ushort usz float128 any fault typeid assert asm bitstruct break case catch const continue alias default defer typedef do else enum extern false for foreach foreach_r fn tlocal if inline import macro module nextcase null return static struct switch true try union var while attrdef
Některá slova již známe. Jedná se o názvy datových typů, o některé konstanty a taktéž o slova používaná v definicích funkcí. Pokud tato slova ze seznam odstraníme, zbude nám dosti zmenšený seznam:
any typeid assert
asm bitstruct break case
catch const continue alias
default defer typedef do
else enum extern
for foreach foreach_r fn
tlocal if inline
macro nextcase
static struct switch
try union
while attrdef
V dnešním článku se seznámíme s konstrukcemi pro řízení běhu programu, které jsou realizovány zvýrazněnými klíčovými slovy:
any typeid assert
asm bitstruct break case
catch const continue alias
default defer typedef do
else enum extern
for foreach foreach_r fn
tlocal if inline
macro nextcase
static struct switch
try union
while attrdef
Pro připomenutí – všechny demonstrační příkladu ukázané v navazujících kapitolách, jsou založeny na následující šabloně:
module program_stub;
fn void main()
{
}
Jedná se o zápis plnohodnotného programu, který je možné přeložit a spustit:
Program linked to executable './program_stub'. Launching ./program_stub Program completed with exit code 0.
2. Rozvětvení běhu programu založené na konstrukcích if a if-else
V prakticky všech moderních programovacích jazycích se pro základní rozvětvení běhu programu používá konstrukce založená na klíčovém slovu if; v případě úplného rozvětvení (do dvou větví) pak konstrukce založená na dvojici klíčových slov if a else. Nejinak je tomu i v programovacím jazyku C3, ve kterém se konstrukce if zapisuje následujícím způsobem (včetně kulatých závorek okolo podmínky, tedy výrazu vyhodnocovaného na hodnotu typu bool, a včetně zápisu příslušné větve do bloku ohraničeného složenými závorkami):
module if_branch;
import std::io;
fn void which_number(int x) {
if (x % 2 == 0) {
io::printfn("%d is even number", x);
}
}
fn void main()
{
for (int i=0; i<10; i++) {
which_number(i);
}
}
Tento program po svém překladu a spuštění vypíše pouze informace o tom, že funkci which_number byl předán parametr obsahující sudé číslo:
0 is even number 2 is even number 4 is even number 6 is even number 8 is even number
Úplné rozvětvení je realizováno s využitím klíčových slov if a else, přičemž obě větve jsou zapsány vlastním blokem (i když tento blok bude obsahovat jen jediný příkaz):
module if_else_branch;
import std::io;
fn void which_number(int x) {
if (x % 2 == 0) {
io::printfn("%d is even number", x);
}
else {
io::printfn("%d is odd number", x);
}
}
fn void main()
{
for (int i=0; i<10; i++) {
which_number(i);
}
}
Takto upravený program dokáže rozpoznat a vypsat jak sudá, tak i lichá čísla, což je ostatně patrné i z vypsaných výsledků:
0 is even number 1 is odd number 2 is even number 3 is odd number 4 is even number 5 is odd number 6 is even number 7 is odd number 8 is even number 9 is odd number
3. Porovnání s jazykem C: nekorektní způsoby zápisu konstrukce if-else
V programovacím jazyku C, a tím pádem i například v C++ nebo v Javě (ale i v dalších programovacích jazycích) je možné ve větvi if popř. ve větvi else zapsat jen jediný příkaz, který nemusí být umístěn do samostatného bloku. Ovšem i kvůli zamezení chybám typu Apple Goto fail takovou konstrukci jazyk C3 (ale například i Go) odmítne a bude vyžadovat, aby se tento (jediný) příkaz zapsal přímo na řádek s podmínkou if.
To si ostatně můžeme snadno otestovat na následující ukázce kódu:
module improper_if_else;
import std::io;
fn void which_number(int x) {
if (x % 2 == 0)
io::printfn("%d is even number", x);
else
io::printfn("%d is odd number", x);
}
fn void main()
{
for (int i=0; i<10; i++) {
which_number(i);
}
}
Překladač tuto konstrukci odmítne:
3:
4: fn void which_number(int x) {
5: if (x % 2 == 0)
6: io::printfn("%d is even number", x);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(/tmp/ramdisk/c3-control-flow/04_improper_if.c3:6:9) Error: The 'then' part of a single line if-statement must start on the same line as the 'if' or use '{ }'
Pokud se jedná o neúplné rozvětvení (s if, ale bez else), je podporován tento zápis:
module improper_if_else;
import std::io;
fn void which_number(int x) {
if (x % 2 == 0) io::printfn("%d is even number", x);
}
fn void main()
{
for (int i=0; i<10; i++) {
which_number(i);
}
}
Ovšem v případě, že se jedná o rozvětvení úplné, vyžaduje překladač jazyka C3 explicitní zápis programových bloků, tedy takový zápis, který byl použit v předchozí kapitole:
module improper_if_else;
import std::io;
fn void which_number(int x) {
if (x % 2 == 0) io::printfn("%d is even number", x);
else io::printfn("%d is odd number", x);
}
fn void main()
{
for (int i=0; i<10; i++) {
which_number(i);
}
}
Chybové hlášení překladače:
2: import std::io;
3:
4: fn void which_number(int x) {
5: if (x % 2 == 0) io::printfn("%d is even number", x);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(/tmp/ramdisk/c3-control-flow/05_improper_if.c3:5:21) Error: if-statements with an 'else' must use '{ }' even around a single statement.
4. Složitější rozvětvení založené na konstrukci if-else if-else
V případě, že má být provedeno rozvětvení do většího množství větví (typicky na základě složitější podmínky), můžeme využít konstrukci if-else if else. Ve skutečnosti se jedná o běžné rozvětvení if-else, ovšem ve větvi else je zapsáno další rozvětvení. Výsledek není příliš čitelný a i z tohoto důvodu lze využít rozšířenou konstrukci switch-case popsanou v navazujících kapitolách. Nicméně se vraťme k rozvětvení realizovanému pomocí if-else if-else. Vypadá následovně:
module if_else_if;
import std::io;
fn void which_number(int x) {
if (x == 0) {
io::printfn("%d is zero", x);
}
else if (x % 2 == 0) {
io::printfn("%d is even number", x);
}
else {
io::printfn("%d is odd number", x);
}
}
fn void main()
{
for (int i=0; i<10; i++) {
which_number(i);
}
}
Takto upravený program rozliší, zda se do funkce which_number předává nulová hodnota, hodnota kladná či naopak hodnota záporná:
0 is zero 1 is odd number 2 is even number 3 is odd number 4 is even number 5 is odd number 6 is even number 7 is odd number 8 is even number 9 is odd number
5. Základní forma rozvětvení založeného na konstrukci switch-case
V předchozí kapitole bylo napsáno, že rozhodovací konstrukce if-else if-else není příliš čitelná a většinou se ani často v jazyku C3 nepoužívá. Je tomu tak z toho důvodu, že programovací jazyk C3 nabízí vývojářům lepší, použitelnější i čitelnější rozhodovací konstrukci switch-case. Ta se v některých ohledech odlišuje od jazyka C a spíše se podobá programovacímu jazyku Go.
Podívejme se nejdříve na základní způsob zápisu této rozhodovací konstrukce. Za klíčovým slovem switch je v kulatých závorkách zapsán výraz, který se vyhodnotí a jeho výsledek je postupně porovnáván s konstantami zapsanými za klíčová slova case. V případě, že dojde ke shodě, tj. například pokud platí, že x==1, jsou vykonány příkazy uvedené v bloku za case. Oproti programovacímu jazyku C je zde jeden významný rozdíl – není zapotřebí jednotlivé bloky explicitně ukončovat klíčovým slovem break:
module switch_statement;
import std::io;
fn void which_number(int x) {
switch (x) {
case 0:
io::printfn("%d is zero", x);
case 1:
io::printfn("%d is odd number", x);
case 2:
io::printfn("%d is even number", x);
default:
io::printfn("I don't know what to say about %d", x);
}
}
fn void main()
{
for (int i=0; i<10; i++) {
which_number(i);
}
}
Výsledek by měl vypadat následovně:
0 is zero 1 is odd number 2 is even number I don't know what to say about 3 I don't know what to say about 4 I don't know what to say about 5 I don't know what to say about 6 I don't know what to say about 7 I don't know what to say about 8 I don't know what to say about 9
Z důvodu (například) přepisu starších programů z C do C3 je ovšem použití klíčového slova break povoleno:
module switch_statement;
import std::io;
fn void which_number(int x) {
switch (x) {
case 0:
io::printfn("%d is zero", x);
break;
case 1:
io::printfn("%d is odd number", x);
break;
case 2:
io::printfn("%d is even number", x);
break;
default:
io::printfn("I don't know what to say about %d", x);
break;
}
}
fn void main()
{
for (int i=0; i<10; i++) {
which_number(i);
}
}
6. Větší množství podmínek a konstrukce switch-case
V případě, že budeme mít za úkol rozlišit, jaká čísla (sudá, lichá či nula) v rozsahu od 0 do 9 jsou předána do funkce which_number, můžeme realizaci tohoto algoritmu, i když bude řešení dosti naivní a nerozšiřitelné, založit na konstrukci switch-case, ovšem s tím, že budeme nějaký příkaz (například výpis informace o tom, že se jedná a sudou hodnotu) provádět pro více hodnot x. Realizace se do značné míry podobá zápisu, který by byl použit v klasickém céčku, akorát není zapotřebí zapisovat klíčové slovo break v každém bloku s výpisem typu hodnoty:
module switch_statement;
import std::io;
fn void which_number(int x) {
switch (x) {
case 0:
io::printfn("%d is zero", x);
case 1:
case 3:
case 5:
case 7:
case 9:
io::printfn("%d is odd number", x);
case 2:
case 4:
case 6:
case 8:
case 10:
io::printfn("%d is even number", x);
default:
io::printfn("I don't know what to say about %d", x);
}
}
fn void main()
{
for (int i=0; i<10; i++) {
which_number(i);
}
}
Nyní bude výsledek vypadat podobně, jako tomu bylo při použití rozhodovací konstrukce if-else if-else:
0 is zero 1 is odd number 2 is even number 3 is odd number 4 is even number 5 is odd number 6 is even number 7 is odd number 8 is even number 9 is odd number
7. Podmínky v konstrukci switch-case vyhodnocované v čase běhu procesu
Ve skutečnosti ovšem programová konstrukce switch-case implementovaná v programovacím jazyku C3 nabízí větší možnosti, než podobně vypadající konstrukce v původním céčku. Například je možné za klíčová slova case zapisovat výrazy vyhodnocované až v čase běhu programu. To tedy znamená, že můžeme realizovat rozhodnutí, zda je předaná celočíselná hodnota nulová, sudá či naopak lichá, například následujícím způsobem:
module switch_statement;
import std::io;
fn void which_number(int x) {
switch (true) {
case x == 0:
io::printfn("%d is zero", x);
case x % 2 == 0:
io::printfn("%d is even number", x);
default:
io::printfn("%d is odd number", x);
}
}
fn void main()
{
for (int i=0; i<10; i++) {
which_number(i);
}
}
Výsledky získané po překladu a spuštění tohoto příkladu:
0 is zero 1 is odd number 2 is even number 3 is odd number 4 is even number 5 is odd number 6 is even number 7 is odd number 8 is even number 9 is odd number
Ve skutečnosti můžeme výraz za klíčovým slovem switch zcela odstranit a provést rozvětvení následujícím způsobem:
module switch_statement;
import std::io;
fn void which_number(int x) {
switch {
case x == 0:
io::printfn("%d is zero", x);
case x % 2 == 0:
io::printfn("%d is even number", x);
default:
io::printfn("%d is odd number", x);
}
}
fn void main()
{
for (int i=0; i<10; i++) {
which_number(i);
}
}
Výsledky budou totožné s předchozím řešením:
0 is zero 1 is odd number 2 is even number 3 is odd number 4 is even number 5 is odd number 6 is even number 7 is odd number 8 is even number 9 is odd number
8. Konstrukce switch-case a příkaz break
V předchozím textu jsme si řekli, že v rozhodovací konstrukci switch-case není nutné jednotlivé větve „uzavírat“ slovem break. Ovšem můžeme se setkat se situací, kdy by větev byla prázdná. V takovém případě se break hodí. Typickým příkladem použití je explicitní zápis větve default, která je prázdná (jazyk C3 její explicitní zápis ovšem nevyžaduje):
module switch_statement;
import std::io;
fn void which_number(int x) {
switch (true) {
case x == 0:
io::printfn("%d is zero", x);
case x % 2 == 0:
io::printfn("%d is even number", x);
case x % 2 != 0:
io::printfn("%d is odd number", x);
default:
break;
}
}
fn void main()
{
for (int i=0; i<10; i++) {
which_number(i);
}
}
Opět se podívejme na to, jaké zprávy budou tímto demonstračním příkladem vypsány:
0 is zero 1 is odd number 2 is even number 3 is odd number 4 is even number 5 is odd number 6 is even number 7 is odd number 8 is even number 9 is odd number
9. Klíčové slovo nextcase v programové konstrukci switch-case
V jazyku C3 je možné ve větvi case zapsat klíčové slovo nextcase, které přenese řízení do další větve. V mnoha ohledech se tedy nextcase podobá klíčovému slovu fallthrough z programovacího jazyka Go. Podívejme se na příklad použití. Ve větvi zavolané pro x==0 se po výpisu zprávy „0 is zero“ konstrukce switch-case neukončí, ale přejde se na další větev, ve které se vypíše „0 is even number“ (což je vlastně taktéž korektní). Takto upravený demonstrační příklad vypadá následovně:
module switch_statement;
import std::io;
fn void which_number(int x) {
switch (true) {
case x == 0:
io::printfn("%d is zero", x);
nextcase;
case x % 2 == 0:
io::printfn("%d is even number", x);
case x % 2 != 0:
io::printfn("%d is odd number", x);
default:
break;
}
}
fn void main()
{
for (int i=0; i<10; i++) {
which_number(i);
}
}
Povšimněte si, že se skutečně pro x==0 vypíše dvojice zpráv a nikoli pouze informace o nulovém prvku:
0 is zero 0 is even number 1 is odd number 2 is even number 3 is odd number 4 is even number 5 is odd number 6 is even number 7 is odd number 8 is even number 9 is odd number
10. Základní forma počítané programové smyčky for
Vzhledem k tomu, že je programovací jazyk C3 odvozen od klasického céčka, nebude velkým překvapením, že i v C3 nalezneme programovou smyčku realizovanou klíčovým slovem for. I v C3 je možné specifikovat lokální proměnnou použitou jako počitadlo smyčky. Příkladem může být program, který jsme si (i když v nepatrně odlišné variantě) ukázali minule. V tomto programu je definováno desetiprvkové pole, přičemž hodnoty prvků tohoto pole jsou naplněny právě v počítané smyčce for:
module loops;
import std::io;
fn void main()
{
int[10] a;
for (int i=0; i<a.len; i++) {
a[i] = (i + 1)*10;
}
io::printf("a=%s\n", a);
}
Ze zobrazených výsledků je patrné, že je pole skutečně naplněno hodnotami vypočtenými uvnitř programové smyčky:
a=[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
Ve smyčce for lze ovšem hodnotu počitadla měnit libovolným způsobem, například:
module loops;
import std::io;
fn void main()
{
for (int i=1; i<1000; i*=2) {
io::printf("i=%s\n", i);
}
}
Výsledky:
i=1 i=2 i=4 i=8 i=16 i=32 i=64 i=128 i=256 i=512
11. Průchod polem či jinou sekvencí smyčkou typu foreach
V předchozím článku jsme se seznámili s takzvanými kontejnery, tj. s datovými typy, které jako své hodnoty obsahují nějakou sekvenci prvků (obecně odlišného typu). Pro realizaci průchodu těmito prvky je v programovacím jazyku C3 určena programová smyčka typu forach, kterou je možné zapsat různými způsoby.
Nejjednodušší forma zápisu smyčky typu foreach je ukázána na dalším demonstračním příkladu. Procházíme v něm polem a, přičemž v každé iteraci bude hodnota n-tého prvku uložena do lokální proměnné item. Povšimněte si, že není nutné specifikovat typ této proměnné. A navíc bude tato proměnná viditelná pouze ve vnitřním bloku smyčky foreach:
module loops;
import std::io;
fn void main()
{
int[10] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
foreach (item : a)
{
io::printfn("item: %s", item);
}
}
Výsledky, které by se měly zobrazit po překladu a spuštění tohoto příkladu:
item: 1 item: 2 item: 3 item: 4 item: 5 item: 6 item: 7 item: 8 item: 9 item: 10
12. Průchod polem smyčkou foreach se získáním indexu i prvku v každé iteraci
Programová smyčka typu forarch dokáže v každé iteraci získat jak hodnotu prvku pole (což jsme ostatně viděli i v předchozí kapitole), tak i index tohoto prvku, což může být poměrně užitečné (modifikace pole, tisk různých tabulek atd.). Zápis takové varianty programové smyčky je ve skutečnosti velmi jednoduchý, jak je to ostatně patrné i z následujícího demonstračního příkladu. Povšimněte si, že není nutné definovat ani typ proměnné index ani item (a pochopitelně jsou tato jména plně volitelná vývojářem):
module loops;
import std::io;
fn void main()
{
int[10] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
foreach (index, item : a)
{
io::printfn("item #%d: %s", index, item);
}
}
Po překladu a spuštění tohoto příkladu by se mělo vypsat deset řádků s indexy i hodnotami prvků pole a:
item #0: 1 item #1: 2 item #2: 3 item #3: 4 item #4: 5 item #5: 6 item #6: 7 item #7: 8 item #8: 9 item #9: 10
13. Modifikace prvků pole při jeho průchodu smyčkou foreach
Při průchodu polem s využitím programové smyčky foreach je možné obsah tohoto pole přímo modifikovat (což ale obecně nemusí platit pro všechny typy sekvencí). Využijeme přitom znalosti indexu každého prvku pole. Pokud například budeme chtít do desetiprvkového pole celých čísel postupně uložit hodnoty 10, 20, 30 … 100, je možné postupovat následujícím způsobem:
module loops;
import std::io;
fn void main()
{
int[10] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
foreach (i, item : a)
{
a[i] = ((int)i + 1)*10;
}
io::printf("a=%s\n", a);
}
Po překladu a spuštění tohoto příkladu si lze snadno ověřit, že je výsledek korektní:
a=[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
module loops;
import std::io;
fn void main()
{
int[10] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
foreach (i, _ : a)
{
a[i] = ((int)i + 1)*10;
}
io::printf("a=%s\n", a);
}
14. Modifikace prvků pole přes ukazatel získaný v každé iteraci smyčky foreach
Při použití programové smyčky foreach ve formě:
foreach (i, item : a)
{
...
...
...
}
je v každé iteraci do proměnné item zkopírována hodnota prvku. To ovšem znamená, že sice můžeme změnit hodnotu této proměnné, ale na původní pole nebude mít tato modifikace žádný vliv.
To si ostatně můžeme velmi snadno ověřit:
module loops;
import std::io;
fn void main()
{
int[10] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
foreach (i, item : a)
{
item = ((int)i + 1)*10;
}
io::printf("a=%s\n", a);
}
Po spuštění tohoto příkladu se vypíšou původní hodnoty prvků pole:
a=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Jazyk C3 ovšem umožňuje, aby se ve smyčce foreach v každé iteraci získal ukazatel na prvek pole, což již umožňuje zápis nové hodnoty do dané prvku (právě přes tento ukazatel). Syntaxe vypadá takto:
foreach (i, &item : a)
{
...
...
...
}
Ověřme si, jak lze takovou variantu programové smyčky využít:
module loops;
import std::io;
fn void main()
{
int[10] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
foreach (i, &item : a)
{
*item = ((int)i + 1)*10;
}
io::printf("a=%s\n", a);
}
Po spuštění tohoto demonstračního příkladu skutečně dojde k zápisu nových hodnot do všech prvků pole:
a=[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
15. Průchod prvky sekvence v opačném pořadí s využitím smyčky foreach_r
Programovací jazyk C3 umožňuje u některých typů sekvencí, mezi které patří mj. i klasická pole, nejenom průchod od prvního prvku směrem k prvku poslednímu, ale i opačný průchod, tj. od posledního prvku směrem k prvku prvnímu. Pokud je zapotřebí v programu tento průchod realizovat, používá se namísto programové smyčky foreach smyčka zapisovaná klíčovým slovem foreach_r. Ta má – pochopitelně kromě opačného směru iterací – naprosto stejné vlastnosti, jako již popsaná smyčka foreach.
module loops;
import std::io;
fn void main()
{
int[10] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
foreach_r (item : a)
{
io::printfn("item: %s", item);
}
}
Výsledkem běhu tohoto programu bude následující sekvence hodnot přečtených z pole:
item: 10 item: 9 item: 8 item: 7 item: 6 item: 5 item: 4 item: 3 item: 2 item: 1
16. Programová smyčka typu while
Druhým typem programové smyčky, se kterou se dnes seznámíme, je smyčka typu while. Jedná se o smyčku, ve které se podmínka, zda se má provádět další iterace, provádí vždy na začátku každé iterace, tj. ještě před vstupem do těla smyčky (smyčka se tedy nemusí provést ani jedenkrát). Základní tvar této smyčky se v jazyku C3 vlastně nijak neodlišuje od jazyka C, ovšem s tím upřesněním, že podmínka pro další iteraci musí být zapsána výrazem typu bool.
Podívejme se na realizaci průchodu polem, tentokrát ovšem namísto smyčky foreach použijeme smyčku while (která je mimochodem v tomto kontextu nevhodná):
module loops;
import std::io;
fn void main()
{
int[10] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int i = 0;
while (i<a.len) {
io::printfn("item: %s", a[i]);
i++;
}
}
Výsledky vypsané tímto demonstračním příkladem by měly být snadno pochopitelné:
item: 1 item: 2 item: 3 item: 4 item: 5 item: 6 item: 7 item: 8 item: 9 item: 10
V této smyčce lze, pochopitelně podobně jako u dalších typů programových smyček, použít příkaz break určený pro ukončení těla smyčky (typicky při splnění nějaké další podmínky):
module loops;
import std::io;
fn void main()
{
int[10] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int i = 0;
while (i<a.len) {
if (a[i] >= 6) {
break;
}
io::printfn("item: %s", a[i]);
i++;
}
}
Nyní se tělo smyčky ukončí při splnění zapsané podmínky (hodnota prvku pole je větší nebo rovna šesti):
item: 1 item: 2 item: 3 item: 4 item: 5
17. Vnořené programové smyčky
V jazyku C3, ostatně podobně jako ve všech mainstreamových programovacích jazycích, je možné programové smyčky vnořovat, s čímž souvisí i syntaxe a zejména sémantika příkazů break a continue. Podívejme se nejdříve na realizaci výpočtu malé násobilky s využitím dvou vnořených smyček typu for:
module loops;
import std::io;
fn void main()
{
for (int y=1; y<=10; y++) {
for (int x=1; x<=10; x++) {
int z = x * y;
io::printf("%3d ", z);
}
io::printn();
}
}
Výsledkem bude následující tabulka vypsaná na standardní výstup:
1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42 48 54 60 7 14 21 28 35 42 49 56 63 70 8 16 24 32 40 48 56 64 72 80 9 18 27 36 45 54 63 72 81 90 10 20 30 40 50 60 70 80 90 100
module loops;
import std::io;
fn void main()
{
int y=1;
while (y<=10) {
int x=1;
while (x<=10) {
int z = x * y;
io::printf("%3d ", z);
x++;
}
io::printn();
y++;
}
}
18. Vnořené programové smyčky a příkaz break
Jak jsme se již řekli v předchozí kapitole, přináší možnost tvorby vnořených programových smyček jeden syntaktický a současně i sémantický problém – jak řešit výskoky (ukončení) z vnitřních smyček? V programovacím jazyku C k tomuto účelu sloužila konstrukce goto, ovšem jazyk C3 se snaží být více strukturovaný. Proto bylo nutné zavést způsob zápisu, který umožní výskok z vnitřních smyček.
Nejprve si připomeňme, že ukončení vnitřní smyčky zajistí příkaz break, a to bez dalších úprav:
module loops;
import std::io;
fn void main()
{
for (int y=1; y<=10; y++) {
for (int x=1; x<=10; x++) {
int z = x * y;
io::printf("%3d ", z);
if (z == 42) {
break;
}
}
io::printn();
}
}
Po spuštění tohoto programu se vnitřní smyčka ukončí po dosažení hodnoty 42, ovšem vnější smyčka stále pokračuje ve své činnosti. Výsledkem bude tabulka se zkráceným šestým a sedmým řádkem:
1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42 7 14 21 28 35 42 8 16 24 32 40 48 56 64 72 80 9 18 27 36 45 54 63 72 81 90 10 20 30 40 50 60 70 80 90 100
Programovací jazyk C3 zavádí novou syntaxi zápisu návěští (label), které umožňuje ukončení nejenom vnitřní smyčky, ale i libovolně zvolené smyčky vnější. Návěští se zapisuje přímo za klíčové slovo, kterým smyčka začíná:
module loops;
import std::io;
fn void main()
{
for END: (int y=1; y<=10; y++) {
for (int x=1; x<=10; x++) {
int z = x * y;
io::printf("%3d ", z);
if (z == 42) {
break END;
}
}
io::printn();
}
}
V tomto demonstračním příkladu bude tisk tabulky zcela ukončen po dosažení hodnoty 42:
1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42
Naprosto stejným způsobem lze téhož dosáhnout i u programové smyčky typu while:
module loops;
import std::io;
fn void main()
{
int y = 1;
while END: (y<=10) {
int x = 1;
while (x<=10) {
int z = x * y;
io::printf("%3d ", z);
if (z == 42) {
break END;
}
x++;
}
io::printn();
y++;
}
}
Výsledek:
1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 4 8 12 16 20 24 28 32 36 40 5 10 15 20 25 30 35 40 45 50 6 12 18 24 30 36 42
Ještě si ukažme nepatrně komplikovanější příklad, ve kterém se příkazem break vyskakuje ze dvou smyček, i když jsou v programu realizovány celkem tři vnořené programové smyčky:
module loops;
import std::io;
fn void main()
{
for (int zaklad=1; zaklad<=100; zaklad*=10) {
for END: (int y=1; y<=10; y++) {
for (int x=1; x<=10; x++) {
int z = x * y;
io::printf("%5d ", z*zaklad);
if (z == 42) {
break END;
}
}
io::printn();
}
io::printn();
io::printn("-----------------------------------------------------------");
}
}
Výsledky ukazují ukončení „prostřední“ smyčky, nikoli smyčky vnější:
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50
6 12 18 24 30 36 42
-----------------------------------------------------------
10 20 30 40 50 60 70 80 90 100
20 40 60 80 100 120 140 160 180 200
30 60 90 120 150 180 210 240 270 300
40 80 120 160 200 240 280 320 360 400
50 100 150 200 250 300 350 400 450 500
60 120 180 240 300 360 420
-----------------------------------------------------------
100 200 300 400 500 600 700 800 900 1000
200 400 600 800 1000 1200 1400 1600 1800 2000
300 600 900 1200 1500 1800 2100 2400 2700 3000
400 800 1200 1600 2000 2400 2800 3200 3600 4000
500 1000 1500 2000 2500 3000 3500 4000 4500 5000
600 1200 1800 2400 3000 3600 4200
-----------------------------------------------------------
19. Repositář s demonstračními příklady
Demonstrační příklady vytvořené pro nejnovější verzi programovacího jazyka C3 byly uloženy do repositáře dostupného na adrese https://github.com/tisnik/c3-examples. Následují odkazy na jednotlivé příklady (či jejich nedokončené části).
Demonstrační příklady z prvního článku o jazyku C3:
| # | Příklad | Stručný popis | Adresa |
|---|---|---|---|
| 1 | factorial.c3 | realizace výpočtu faktoriálu | https://github.com/tisnik/c3-examples/blob/master/introduction/factorial.c3 |
| 2 | factorial_macro.c3 | výpočet faktoriálu konkrétní hodnoty implementovaný formou makra | https://github.com/tisnik/c3-examples/blob/master/introduction/factorial_macro.c3 |
| 3 | swap_macro.c3 | makro realizující prohození dvou hodnot | https://github.com/tisnik/c3-examples/blob/master/introduction/swap_macro.c3 |
| 4 | renderer.c | výpočet a vykreslení Juliovy množiny implementovaný v jazyku C | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer.c |
| 5 | renderer_v1.c3 | definice datové struktury s rozměry rastrového obrázku a skeleton všech funkcí | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v1.c3 |
| 6 | renderer_v2.c3 | anotace parametrů funkcí typu ukazatel (pointer) | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v2.c3 |
| 7 | renderer_v3.c3 | statická kontrola, zda se nepředávají neinicializované ukazatele | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v3.c3 |
| 8 | renderer_v4.c3 | runtime kontrola, zda se nepředávají neinicializované ukazatele | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v4.c3 |
| 9 | renderer_v5.c3 | první (nekorektní) varianta funkce pro inicializaci barvové palety | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v5.c3 |
| 10 | renderer_v6.c3 | druhá (korektní) varianta funkce pro inicializaci barvové palety | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v6.c3 |
| 11 | renderer_v7.c3 | volání knihovní I/O funkce a volání nativní céčkovské funkce | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v7.c3 |
| 12 | renderer_v8.c3 | plně funkční program pro výpočet a vykreslení Juliovy množiny | https://github.com/tisnik/c3-examples/blob/master/introduction/renderer_v8.c3 |
Demonstrační příklady ze druhého článku o jazyku C3:
| # | Příklad | Stručný popis | Adresa |
|---|---|---|---|
| 13 | 01_just_main.c3 | struktura nejjednoduššího programu obsahujícího pouze prázdnou funkci main | https://github.com/tisnik/c3-examples/blob/master/c3-basics/01_just_main.c3 |
| 14 | 02_module_name.c3 | struktura programu s uvedeným plným jménem modulu | https://github.com/tisnik/c3-examples/blob/master/c3-basics/02_module_name.c3 |
| 15 | 03_hello_world.c3 | klasický program typu „Hello, world!“ napsaný v jazyku C3 | https://github.com/tisnik/c3-examples/blob/master/c3-basics/03_hello_world.c3 |
| 16 | 04_exit_value.c3 | ukončení procesu s předáním návratového kódu zpět volajícímu programu | https://github.com/tisnik/c3-examples/blob/master/c3-basics/04_exit_value.c3 |
| 17 | 05_c_function.c3 | zavolání funkce definované v knihovně programovacího jazyka C | https://github.com/tisnik/c3-examples/blob/master/c3-basics/05_c_function.c3 |
| 18 | 06_bool_type.c3 | definice proměnných typu pravdivostní hodnota (bool) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/06_bool_type.c3 |
| 19 | 07_int_to_bool.c3 | implicitní převod hodnoty typu int na pravdivostní hodnotu (nekorektní řešení) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/07_int_to_bool.c3 |
| 20 | 08_int_to_bool.c3 | explicitní převod hodnoty typu int na pravdivostní hodnotu (korektní řešení) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/08_int_to_bool.c3 |
| 21 | 09_int_to_bool.c3 | explicitní převod hodnoty typu int na pravdivostní hodnotu (nekorektní řešení) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/09_int_to_bool.c3 |
| 22 | 10_bool_sizeof.c3 | zjištění velikosti paměti obsazené hodnotou typu bool | https://github.com/tisnik/c3-examples/blob/master/c3-basics/10_bool_sizeof.c3 |
| 23 | 11_int_types.c3 | definice proměnných typu celé číslo se znaménkem s různou bitovou šířkou | https://github.com/tisnik/c3-examples/blob/master/c3-basics/11_int_types.c3 |
| 24 | 12_uint_types.c3 | definice proměnných typu celé číslo bez znaménka s různou bitovou šířkou | https://github.com/tisnik/c3-examples/blob/master/c3-basics/12_uint_types.c3 |
| 25 | 13_no_suffixes.c3 | celočíselné konstanty bez uvedení suffixu (bitové šířky) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/13_no_suffixes.c3 |
| 26 | 14_suffixes.c3 | celočíselné konstanty s uvedením sufficu (bitové šířky) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/14_suffixes.c3 |
| 27 | 15_int_sizeof.c3 | zjištění velikosti paměti obsazené celočíselnými hodnotami se znaménkem | https://github.com/tisnik/c3-examples/blob/master/c3-basics/15_int_sizeof.c3 |
| 28 | 16_uint_sizeof.c3 | zjištění velikosti paměti obsazené celočíselnými hodnotami bez znaménka | https://github.com/tisnik/c3-examples/blob/master/c3-basics/16_uint_sizeof.c3 |
| 29 | 17_int_conversions.c3 | korektní převody mezi celočíselnými hodnotami s různou bitovou šířkou | https://github.com/tisnik/c3-examples/blob/master/c3-basics/17_int_conversions.c3 |
| 30 | 18_int_conversions.c3 | nekorektní převody mezi celočíselnými hodnotami s různou bitovou šířkou | https://github.com/tisnik/c3-examples/blob/master/c3-basics/18_int_conversions.c3 |
| 31 | 19_int_conversions.c3 | explicitní převody a přetečení hodnot | https://github.com/tisnik/c3-examples/blob/master/c3-basics/19_int_conversions.c3 |
| 32 | 20_float_types.c3 | definice proměnných typu numerická hodnota s plovoucí řádovou čárkou (tečkou) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/20_float_types.c3 |
| 33 | 21_vector_type.c3 | definice vektoru obsahujícího celočíselné hodnoty | https://github.com/tisnik/c3-examples/blob/master/c3-basics/21_vector_type.c3 |
| 34 | 22_vector_operations.c3 | základní operace s celými vektory | https://github.com/tisnik/c3-examples/blob/master/c3-basics/22_vector_operations.c3 |
| 35 | 23_vector_sizes.c3 | zjištění a tisk velikosti vektorů (různé datové typy prvků vektorů, shodná délka) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/23_vector_sizes.c3 |
| 36 | 24_vector_sizes.c3 | zjištění a tisk velikosti vektorů (stejné datové typy prvků vektorů, odlišná délka) | https://github.com/tisnik/c3-examples/blob/master/c3-basics/24_vector_sizes.c3 |
Demonstrační příklady z předchozího článku o jazyku C3:
Demonstrační příklady z dnešního článku o jazyku C3:
| # | Příklad | Stručný popis | Adresa |
|---|---|---|---|
| 57 | 01_program_stub.c3 | struktura programu s uvedeným plným jménem modulu | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/01_program_stub.c3 |
| 58 | 02_if.c3 | nejjednodušší forma rozvětvení založené na konstrukci if | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/02_if.c3 |
| 59 | 03_if_else.c3 | plné rozvětvení realizované konstrukcí if-else | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/03_if_else.c3 |
| 60 | 04_improper_if.c3 | nekorektní způsob zápisu programové konstrukce if-else (porovnání s jazykem C) | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/04_improper_if.c3 |
| 61 | 05_improper_if.c3 | nekorektní způsob zápisu programové konstrukce if-else (porovnání s jazykem C) | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/05_improper_if.c3 |
| 62 | 06_if_else_if.c3 | složitější rozvětvení založené na programové konstrukci if-else if-else | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/06_if_else_if.c3 |
| 63 | 07_switch_basic.c3 | základní forma vícenásobného rozvětvení založeného na konstrukci switch-case | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/07_switch_basic.c3 |
| 64 | 08_switch_basic.c3 | větší množství podmínek a programová konstrukce switch-case | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/08_switch_basic.c3 |
| 65 | 09_switch_condition.c3 | podmínky zapsané ve větvích programové konstrukci switch-case vyhodnocované v čase běhu procesu | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/09_switch_condition.c3 |
| 66 | 10_switch_true.c3 | konstrukce switch-case bez uvedeného výrazu za klíčovým slovem switch | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/10_switch_true.c3 |
| 67 | 11_switch_break.c3 | zápis prázdné větve default v programové konstrukci switch-case | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/11_switch_break.c3 |
| 68 | 12_switch_nextcase.c3 | pokračování ve vykonávání konstrukce switch-case vynucené klíčovým slovem nextcase | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/12_switch_nextcase.c3 |
| 69 | 13_for_loop.c3 | základní forma programové smyčky realizované klíčovým slovem for | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/13_for_loop.c3 |
| 70 | 14_foreach_loop.c3 | základní forma programové smyčky typu for-each | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/14_foreach_loop.c3 |
| 71 | 15_foreach_loop.c3 | programová smyčka for-each vracející index prvku i hodnotu prvku | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/15_foreach_loop.c3 |
| 72 | 16_foreach_loop.c3 | modifikace obsahu pole v programové smyčce for-each | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/16_foreach_loop.c3 |
| 73 | 17_foreach_loop.c3 | pokus o modifikaci obsahu procházeného pole | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/17_foreach_loop.c3 |
| 74 | 18_foreach_loop.c3 | modifikace procházeného pole přes ukazatel na prvek | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/18_foreach_loop.c3 |
| 75 | 19_foreach_r_loop.c3 | programová smyčka for-each, ve které se sekvencí prochází v opačném pořadí | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/19_foreach_r_loop.c3 |
| 76 | 20_while_loop.c3 | základní forma programové smyčky typu while | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/20_while_loop.c3 |
| 77 | 21_while_loop2.c3 | programová smyčka typu while s konstrukcí break | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/21_while_loop2.c3 |
| 78 | 22_nested_loops.c3 | realizace vnořených programových smyček | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/22_nested_loops.c3 |
| 79 | 23_break.c3 | vnořené programové smyčky a příkaz break: ukončení vnitřní smyčky | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/23_break.c3 |
| 80 | 24_break.c3 | vnořené programové smyčky a příkaz break: ukončení vnější smyčky | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/24_break.c3 |
| 81 | 25_break.c3 | vnořené programové smyčky a příkaz break, varianta se smyčkami typu while | https://github.com/tisnik/c3-examples/blob/master/c3-control-flow/25_break.c3 |
20. Odkazy na Internetu
- Programovací jazyk C3: evoluce, nikoli revoluce
https://www.root.cz/clanky/programovaci-jazyk-c3-evoluce-nikoli-revoluce/ - Programovací jazyk C3: datové typy pro moderní architektury
https://www.root.cz/clanky/programovaci-jazyk-c3-datove-typy-pro-moderni-architektury/ - Programovací jazyk C3: složené datové typy a kontejnery
https://www.root.cz/clanky/programovaci-jazyk-c3-slozene-datove-typy-a-kontejnery/ - The C3 Programming Language
https://c3-lang.org/ - C3 For C Programmers
https://c3-lang.org/language-overview/primer/ - C3 is a C-like language trying to be an incremental improvement over C rather than a whole new language
https://www.reddit.com/r/ProgrammingLanguages/comments/oohij6/c3_is_a_clike_language_trying_to_be_an/ - Tiobe index
https://www.tiobe.com/tiobe-index/ - PYPL PopularitY of Programming Language
https://pypl.github.io/PYPL.html - C3 Tutorial
https://learn-c3.org/ - History of programming languages
https://devskiller.com/history-of-programming-languages/ - History of programming languages (Wikipedia)
https://en.wikipedia.org/wiki/History_of_programming_languages - D language
https://dlang.org/ - Zig programming language
https://ziglang.org/ - V language
https://vlang.io/ - D programming language
https://en.wikipedia.org/wiki/D_(programming_language) - Zig programming language (Wikipedia)
https://en.wikipedia.org/wiki/Zig_(programming_language) - V programming language (Wikipedia)
https://en.wikipedia.org/wiki/V_(programming_language) - Syntax highlighting for C3's programming language
https://github.com/Airbus5717/c3.vim - Go factorial
https://gist.github.com/esimov/9622710 - Generational list of programming languages
https://en.wikipedia.org/wiki/Generational_list_of_programming_languages - The Language Tree: Almost Every Programming Language Ever Made
https://github.com/Phileosopher/langmap - List of C-family programming languages
https://en.wikipedia.org/wiki/List_of_C-family_programming_languages - Compatibility of C and C++
https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B - C++23: compatibility with C
https://www.sandordargo.com/blog/2023/08/23/cpp23-c-compatibility - Can C++ Run C Code? Understanding Language Compatibility
https://www.codewithc.com/can-c-run-c-code-understanding-language-compatibility/ - C3: Comparisons With Other Languages
https://c3-lang.org/faq/compare-languages/ - C3 Programming Language Gains Traction as Modern C Alternative
https://biggo.com/news/202504040125_C3_Programming_Language_Alternative_to_C - The case against a C alternative
https://c3.handmade.network/blog/p/8486-the_case_against_a_c_alternative - C (programming language) Alternatives
https://alternativeto.net/software/c-programming-language-/ - Seriál Programovací jazyk Go
https://www.root.cz/serialy/programovaci-jazyk-go/ - Is C3 the Underdog That Will Overtake Zig and Odin?
https://bitshifters.cc/2025/05/22/c3-c-tradition.html - „Hello, World!“ program
https://en.wikipedia.org/wiki/%22Hello%2C_World!%22_program - The C Programming Language
https://en.wikipedia.org/wiki/The_C_Programming_Language - Kontejner (abstraktní datový typ)
https://cs.wikipedia.org/wiki/Kontejner_(abstraktn%C3%AD_datov%C3%BD_typ) - Are arrays not considered containers because they are not based off of a class?
https://stackoverflow.com/questions/37710975/are-arrays-not-considered-containers-because-they-are-not-based-off-of-a-class - Array declaration (C, C++)
https://en.cppreference.com/w/cpp/language/array.html - Understanding the Apple ‘goto fail;’ vulnerability
https://www.blackduck.com/blog/understanding-apple-goto-fail-vulnerability-2.html - Branch (computer science)
https://en.wikipedia.org/wiki/Branch_(computer_science) - Conditional (computer programming)
https://en.wikipedia.org/wiki/Conditional_(computer_programming) - Dangling else
https://en.wikipedia.org/wiki/Dangling_else - Switch statement
https://en.wikipedia.org/wiki/Switch_statement