Portál AbcLinuxu, 16. dubna 2024 15:37

Programování v jazyce D (3): Typy, proměnné, práce s čísly, literály a funkce

10. 1. 2012 | Daniel Kolesa
Články - Programování v jazyce D (3): Typy, proměnné, práce s čísly, literály a funkce  

Dnes se podíváme zase trochu dál. Nejprve se mrkneme na datové typy v D, pak nějaké ty proměnné, základní matematiku a práci s funkcemi.

Obsah

Lekce 3 - typy a proměnné

link

Abychom mohli pokračovat dále, je třeba se naučit něco o typech. Na znalosti typů závisí pak proměnné, které jsou jedním z nejdůležitějších prvků jazyka. Typový systém D je o něco jednodušší než ten z jazyka C nebo C++, ale neztrácí nic na síle. Tak, jdeme na to :-)

Typy

link

Každá hodnota musí mít nějaký typ, který reprezentuje, jak se taková hodnota bude chovat a jak je velká. K tomu se využívají datové typy. Ty si můžeme roztřídit na několik kategorií.

Nejprve se podíváme na základní (atomické) typy, tzn. ty na úplně nejnižší úrovni. Mezi ty patří celá čísla, reálná čísla, boolovské hodnoty, komplexní čísla a znaky. Kromě toho by se dal do této kategorie zařadit i typ void, jakožto "prázdno" - v jazyce se také často využívá a nemá žádnou hodnotu.

D je staticky typovaný jazyk. To znamená, že kontrola typů probíhá při kompilaci, ne za běhu. To má za následek, že se různé hodnoty budou mít stále stejný typ, s jakým byly definovány. Takový jazyk je potom obvykle striktnější, ale zase se programátor vyvaruje typovým chybám za běhu.

Základní typy

link

Základní typy vždy mají nějakou velikost (kromě void). Ta se značí v bitech (popř. bytech). Vyjadřuje, kolik místa v paměti hodnota určitého typu zabere. Teď si můžete říct "noj o, ale není to plýtvání, mít typ, který pobere např. velké číselné hodnoty a místo nevyužít?". Ano, samozřejmě. Ale jazyk nemá jen jeden číselný typ. Místo toho jich existuje celá řada, s různými rozsahy hodnot.

Celá čísla

Puristé budou protestovat, že "to přece nejsou vůbec celá čísla", ale počítejme s tím, že jsou. D definuje několik různě velkých celočíselných (integrálních) typů, které jsou často analogické s těmi v jazyce C. Základní, se kterým budeme pracovat, je int. Int je zkratka pro integer. V jazyce C není jeho velikost definována standardem. V D ano - int je vždy 32 bitů velký a zahrnuje jak záporná, tak kladná čísla.

Pokud jsou třeba jen čísla kladná (nula a větší), tak pro tyto účely je přítomen typ uint (v C by se zapsal jako unsigned int, tzn. "bezznaménkový int"). Ten může logicky jít více do kladných hodnot, než standardní int, ale je useklý v záporných hodnotách.

Vždy není vhodné pracovat s tak velkými čísly. Proto D poskytuje ještě dva menší typy (oba mají i své unsigned protějšky, jen s předponou u). Těmi jsou short a byte. Typ short má vždy 16 bitů. Analogicky typ byte má velikost přesně jeden byte. Stejně tak jsou občas potřeba i větší hodnoty než jen 32 bitů. Ty reprezentují typy long (vždy 64 bitů, unsigned verze ulong) a cent (vždy 128 bitů, ucent pro unsigned, ale tento typ není ještě implementován a je jen rezervován pro pozdější užití).

Cčkaři vědí, že hodnoty různých typů se automaticky neinicializují a je třeba explicitně specifikovat hodnotu. Toto neplatí v případě D. Všechny integrální typy jsou implicitně 0.

Desetinná čísla

Občas celá čísla nestačí. Stejně jako pro celá čísla, D poskytuje několik typů s desetinnou čárkou.

Základním typem, se kterým se pracuje, je float (od floating point). Float má, analogicky k typu int velikost 32 bitů (v Cčku nedefinovaná). Menší neexistuje. 64bitový float se jmenuje double (od "double precision" - dvojitá přesnost; velikost v Cčku logicky není definována). Poslední typ real nemá definovanou velikost. Představuje největší floating point číslo reprezentovatelné hardwarem (80 bitů pro x86).

Výchozí hodnota je u těchto typů vždy TYP.nan.

Boolovské hodnoty

D má separátní typ bool, který může reprezentovat vždy jen 2 hodnoty (je tudíž v podstatě jedním bitem). Tyto hodnoty se jmenují false a true ("pravda" a "nepravda"). Výchozí hodnotou je vždy false. Bool hodnoty jsou implicitně konvertovatelné na jakýkoliv integrální typ (konverze - viz další lekce). Při konverzi se false stane 0 a true 1. Klasické aritmetické operátory se na bool hodnotách nepoužívají. Bitové operátory, AND/OR a ternární operátor se s bool hodnotami použít dají.

Komplexní čísla

D má zabudovaná komplexní čísla, která jsou analogická k floating point typům. Jedninou změnou je, že mají předponu c (cfloat, cdouble, creal). Protože komplexní čísla se skládají ze dvou složek, je třeba nějak reprezentovat tu imaginární složku. K tomu slouží typy ifloat, idouble a ireal. Výchozí hodnoty komplexních typů se také liší. Jsou vždy TYP.nan + TYP.nan * 1.0i (např. float.nan + float.nan * 1.0i).

Znaky

Tyto typy jsou zajímavé tím, že jejich velikosti v podstatě kopírují ty celých čísel. Znakové typy jsou tři. Jmenují se char, wchar a dchar. Všechny jsou integrální a unsigned. Typ char má velikost 8 bitů (tudíž kopíruje ubyte). Typ wchar má 16 bitů (jako ushort). A dchar má 32 bitů (jako uint). Rozdíl je v jejich výchozích hodnotách.

Znakové typy reprezentují jednotky unicode. Typ char je jedna UTF-8 jednotka. Analogicky, wchar reprezentuje UTF-16 a dchar UTF-32. Jejich výchozí hodnoty nejsou 0 ale 0xFF (pro char), 0xFFFF (pro wchar) a 0x0000FFFF (pro dchar).

Odvozené typy

link

Je jasné, že jen se základními atomickými typy si programátor nevystačí. D poskytuje několik odvozených typů. Základním odvozeným typem je pointer (ukazatel). Pointerů se hojně využívá v jazycích C a C++. V D ani nejsou pro vysokoúrovňové programování moc potřeba, ale D je samozřejmě univerzálním jazykem.

Pointer není nic jiného, než číselná hodnota. Jeho velikost je definována platformou. Na většině *nix systémů je pod x86 velký 32 bitů a pod x86_64 64 bitů. Stejně velký jako pointer je typ size_t, což je alias k číselnému typu stejně velkému jako pointer. Číselná hodnota pointeru reprezentuje adresu v paměti. Vzhledem k tomu, že pointer je číslem, tak se k němu dá přičítat a odečítat, a tak měnit, kam v paměti pointer ukazuje. Rozdíl dvou pointerů je typ ptrdiff_t, který je stejně velký jako size_t a je také aliasem, ale narozdíl od něj je znaménkový.

O pointerech se dál zmíním v pozdějších lekcích, teď je ještě brzy.

Dalším odvozeným typem v D je pole. Pole mohou být dvou typů. Statická pole se alokují na zásobníku (stack) a mají předem určenou délku. Je to v podstatě posloupnost hodnot určitého typu v paměti (jdou hned po sobě). Spousta lidí si plete v jazyce C pointery a pole. Jsou to ale dosti odlišné věci. Pointer se ale může chovat jako pole a může na něj ukazovat. V takovém případě pointer ukazuje na první hodnotu v poli. Vzhledem k tomu, že takové hodnoty jdou po sobě, tak přičítáním k pointeru je možné získat hodnotu na určitém indexu v poli (ale opět, o tom později, tohle už je pokročilejší látka).

Dynamická pole v D jsou alokována na haldě (heap). Také je to posloupnost hodnot, ale vzhledem k tomu, že není na fixním stacku, tak je možné měnit délku takového pole. I na dynamické pole může ukazovat pointer. Pro uživatele C++, dynamické pole v D má podobné použití jako vector v C++, ale je lépe zabudované do jazyka.

Každá hodnota v poli má svůj index (číslo, první hodnota má index 0, poslední DÉLKA-1). Velikosti polí v paměti (neplést s délkou pole!) je DÉLKA*VELIKOST_HODNOTY.

Kromě toho má D i asociativní pole. Asociativní pole nemají indexy. Místo toho má každá hodnota klíč, který může být různých typů (např. řetězec). Správně bych je neměl do této kategorie řadit, ale sedí tu. Řadit bych je neměl sem proto, že jsou uvnitř implementované jako hash tabulky, a tudíž jsou objektem, ne odvozeným typem od atomického typu.

Existují ještě dva další odvozené typy. Těmi jsou funkce a delegáty. Funkce reprezentují blok kódu - kontext funkce (scope). Funkce mohou manipulovat s hodnotami ve vlastním kontextu a s globálními hodnotami. Lokální hodnoty jiných funkcí (např. main) musí být poslány přes argumenty (o těch v další lekci). Funkce mají vlastní speciální typ pointerů (tzv. function pointery). Konverze mezi normálními pointery a FP znamená nedefinované chování (resp. definované platformou) ale většinou funguje (občas se toho i dost ošklivě využívá). Delegáty se jinak jmenují "fat pointer" ("tlustý pointer"). Jsou složeninou dvou pointerů. Jeden ukazuje na kontext funkce, ve které byl delegát definován (využívají se většinou jako lambda funkce, o této problematice později) a druhý na funkci samotnou (function pointer). Delegátům bude později věnována vlastní lekce.

Uživatelské typy

link

To ale stále nestačí. Kromě předchozích kategorií, které se dají společně pojmenovat jako POD D má ještě uživatelské typy.

Nejjednodušším je alias. Alias je jen jiný název pro některý předtím definovaný typ. Při kompilaci se takový alias expanduje na plný název typu.

Dalším uživatelským typem je enumerace (enum). Je to v podstatě kolekce konstant. Později s nimi budeme pracovat.

Struktury (struct) a třídy (class), další dva uživatelské typy, nejsou v jazyce C++ rozlišeny. V D ano, struktury jsou hodnotové typy, třídy referenční typy (o referencích opět později). Posledním uživatelským typem je union. To je typ, který definuje několik hodnot různých typů, ale naráz může obsahovat pouze jeden (všechny typy v union sdílí jednu část paměti).

Všemi uživatelskými typy se budeme zabývat v pozdějších kapitolách podrobně.

Zde bych skončil. Přejdeme na proměnné a už i napíšeme nějaký ten kód.

Proměnnné

link

V předchozích lekcích jsme psali nějaké ty úvodní aplikace. Programování ale není pouze o vypisování "hello world" do konzole. Hodnoty si musíme i nějak ukládat a s nimi dále pracovat. K tomuto účelu musíme zavést tzv. proměnné.

Řekněme si, že máme číslo 150. Toto číslo chceme vypsat třikrát po sobě. Přitom po změně tohoto čísla na jednom místě se změní i výstup programu. K tomuto účelu si zavedeme proměnnou. Proměnné jsou jakési "pojmenované instance typů". Zapíšeme je takto:

TYP název;

Např. pro typ int zapíšeme:

int cislo;

Tomuto se říká deklarace proměnné. Proměnné se nikdy nesmí jmenovat jako nějaké rezervované klíčové slovo jazyka. Název proměnné (identifikátor) musí být vždy unikátní vzhledem k aktuálnímu kontextu, jinak dojde ke kolizi a program se nezkompiluje.

No jo, ale my chceme, aby proměnná měla i hodnotu. Deklarované proměnné ji tedy přiřadíme pomocí operátoru =. To se jmenuje definice proměnné.

cislo = 150;

Deklarace i definice se dají kombinovat.

int x = 5;

Proměnné, která už má hodnotu, se dá přiřadit jiná hodnota.

int x = 5;
x = 10;

Hodnota jiné proměnné se dá přiřadit také:

int x = 5;
int y = x;

Obě proměnné budou poté mít nezávislou hodnotu (kopii) (kromě non-POD typů).

Globální, lokální proměnné, kontext

link

Jak už jsem předtím zmínil, názvy proměnných musí být unikátní. Pro pochopení je nutné zavést pojmy globální proměnná a lokální proměnná. Globální proměnná je jakákoliv proměnná deklarovaná mimo kontext určité funkce (popř. třídy, struktury). Vezměme takový program:

import std.stdio;

/* dvě globální proměnné, přístupné odkudkoliv */
int foo     = 150;
string bar = "baz";


void main()
{
    /* lokální proměnná - lokální proměnné nikdy
nekolidují s globálními */
    string bar = "bah";
    short baz = 65536;


    /* vypíše "150bah65536" */
    writeln(foo, bar, baz);
}

Dobrá, máme lokální proměnnou, která se jmenuje stejně jako globální. Ale co když je třeba ve funkci s lokální proměnnou přistoupit ke globální, která má stejný název? To je jednoduché:

import std.stdio;


int foo = 150;
void main()
{
    int foo = 250;
    writeln(foo, .foo);
}

Přidáním tečky před název proměnné se zdůrazní, že se přistupuje ke globální proměnné, nikoliv k lokální.

Typové konverze

link

V jazyce D se rozlišují dva typy konverzí mezi typy, tzv. implicitní konverze a explicitní konverze. Implicitní konverze znamená, že hodnota jednoho typu se dá hned přiřadit proměnné jiného typu. Implicitní konverze je možné provádět hlavně u číselných typů, z typů s menší velikostí na typy s větší velikostí. Např. tato konverze je povolena:

int foo = 5;
long bar = foo;

Obráceně už to ale nejde:

long foo = 5;
int bar = foo;

Explicitní konverze, tzv cast, samozřejmě funguje správně, pokud se hodnota větší proměnné vejde do menší:

int foo = 5;
/* "bar" bude 5 */
short bar = cast(short) foo;

Ale ne, když se nevejde:

int foo = 65537;
/* "bar" bude "foo - největší_hodnota_ushort - 1", tzn. 1 */
short bar = cast(short) foo;

Explicitní konverze podnulových hodnot na unsigned hodnoty není možná. Kromě toho není možné převádět implicitně:

Tímto bych skončil tuto lekci. Látku budeme dále rozšiřovat s rozšiřujícími se znalostmi.

Domácí úkol

link

1) Napište program, který vytvoří proměnnou typu short s hodnotou 16 a z ní udělá int, long, ushort, uint, ulong proměnné a vypíše je.

2) Vytvořte proměnnou typu long s hodnotou 10 a převeďte ji explicitně na int, short, ulong, uint, ushort a přesvědčte se, že hodnota zůstává zachována.

Lekce 4 - čísla a literály

link

Čísla v D samozřejmě můžeme přičítat, odečítat, násobit, dělit, apod., stejně jako v ostatních programovacích jazycích. Ale nebudeme vždy pracovat jen s čísly. Abychom mohli začít dělat i něco s jinými typy, musíme definovat literály. Pak napíšeme i nějaký další kód.

Aritmetické operace

link

Na hodnotách a proměnných můžeme provádět klasické aritmetické operace. Např. součet dvou čísel:

int foo = 5 + 10;

Samozřejmě, toto není moc užitečné. Stejně můžeme ale manipulovat s proměnnými:

int foo = 5;
int bar = foo + 10;

Přičtení hodnoty k proměnné je tedy logicky:

int foo = 5;
foo = foo + 10;

To je ale trochu zdlouhavé, takže se to dá skloubit s přiřazením:

int foo = 5;
foo += 10;

Stejně tak můžeme odečítat, násobit a dělit. Pro přičtení, resp. odečtení hodnoty 1 se dají použít i operátory ++ a --. Ty se dají zapsat buď před proměnnou nebo za proměnnou.

int foo = 5;
foo++;
++foo;
foo--;
--foo;

Proměnná foo bude mít na konci opět hodnotu 5. Jaký je v tom ale potom rozdíl? Při zapsání foo++, tzv. post-inkrementaci, se přičte 1 a vrátí se hodnota před přičtením. Tudíž zápis:

int foo = 5;
int bar = foo++;

vyjádří, že foo na konci bude 6, ale bar bude 5. U ++foo, tzv. pre-inkrementace, je to ale jinak:

/* foo i bar budou 6 */
int foo = 5;
int bar = ++foo;

Stejná pravidla platí i pro --. Zbytek po celočíselném dělení získáme operátorem %. Např. zápis:

int foo = 11;
writeln(foo % 3);

vypíše 2 (protože 10/3 se celočíselně dělit nedá, 9 ano, zbytek do 9 je 1). Ostatní operace jsou pak definovány knihovnou.

Konverzní pravidla při aritmetických operacích

link

Při počítání platí určitá pravidla o výsledném typu. Popíšu je, jak jdou po sobě.

  1. Pokud kterýkoliv operand je typ real, tak se druhý zkonvertuje také na real.
  2. Pokud kterýkoliv operand je typ double, tak se druhý zkonvertuje také na double.
  3. Pokud kterýkoliv operand je typ float, tak se druhý zkonvertuje také na float.
  4. Provede se konverze obou operandů podle pravidla:
    - bool => int
    - byte => int
    - ubyte => int
    - short => int
    - ushort => int
    - char => int
    - wchar => int
    - dchar => uint
    

Pro enum typy platí další pravidla. Těmi se budeme zabývat později.

Literály

link

Literály lze definovat jako prvky s hodnotou. Mohou být různých typů. My už použili číselné literály a řetězcové literály. Typů literálů je samozřejmě více.

Číselné literály

link

D má několik typů číselných literálů. Základním je samozřejmě klasické celé číslo. Patří k němu typ int. Literály lze dle velikosti přiřazovat k jakémukoliv celočíselnému typu. Zapíšeme jej takto:

int x = 5; /* 5 je samotný literál */

Lze zapsat i hexadecimální literály:

int x = 0xDEADBEEF;

Oktalové literály už nejsou podporovány. Místo nich se dají využít funkce standardní knihovny.

Pro unsigned čísla pouze přidáme koncovku u popř U.

uint x = 5u;

Pro literály typu long přidáme l nebo L. Unsigned long se pak zapíše s ul, popř. UL. Float literály končí s f, popř. F:

float a = 3.14f;

Bez koncovky je výchozím typem float literálů typ double. Implicitní konverze je nicméně možná, takže zápis

float a = 3.14;

je možné stejně jako s double. Literály typu real mají koncovku L nebo l:

real a = 3.14L;
Je lepší v takových případech používat raději velké L, protože malé se snadno splete s jedničkou.

Řetězcové literály

link

Základním řetězcovým literálem je prostě řetězec uzavřený v dvojitých uvozovkách. Zapisuje se takto:

string a = "hello world";

Dva literály hned po sobě se chovají jako jeden:

string a = "hello " "world";

Takovéto literály mohou obsahovat escape sekvence, které se pak převedou na jejich pravé reprezentace. Tomu se dá zamezit použitím WYSIWYG literálů, které se zapisují stejně, jen s předponou r:

string a = r"všechno\se\bude\zobrazovat\njak\píšu\r\t";

Tyto literály mají i alternativní syntaxi:

string a = `toto ta\ky fun\guje\`;

Dalším typem jsou hexadecimální řetězcové literály:

string a = x"0A"; /* jako "\x0A" */

Mezery a nové řádky jsou ignorovány. Počet hex čísel musí být násobkem dvou. Všechny tři typy literálů mohou mít postfix znak, který definuje jejich typ. Normálně je to string, což je alias k immutable(char)[] (pole imutabilních znaků). Takové pole se skládá z UTF-8 jednotek. Pokud chceme UTF-16, zapíšeme:

wstring a = "utf-16 řetězec"w;

To samé pro UTF-32:

dstring a = "utf-32 řetězec"d;

Dá se specifikovat i c, což vyústí v string (je tudíž implicitně výchozím nastavením).

Poslední dva typy literálů jsou tzv. delimited literály a token literály. Delimited literály jsou podobné WYSIWYG, ale umožňují zvolit dva znaky, mezi kterými může být napsáno cokoliv a nebude to mít žádný efekt. Tyto znaky musí být napsány hned před/po " a před "..." je třeba napsat q:

string a = q"{hello world\blah/"bah"`na rozdíl od wysiwyg fungují i tyto znaky`````}";

Delimitery mohou být páry [], (), <> nebo {}. Delimited stringy se dají taky použít pro jednoduché zapsání více řádků:

writeln(q"EOS
První řádek
Druhý řádek
Třetí řádek
EOS"
);

Token literály musí obsahovat syntakticky validní D kód (nemusí být ale sémanticky validní). Mají využití např. pro string mixiny (později se jimi budeme zabývat). Textové editory je považují za kus D kódu, takže mohou mít zvýrazněnou syntaxi. Zapisují se mezi q{}:

string x = q{
    writeln("hello world");
}

Ostatní literály

link

Znakové literály se zapisují mezi ', např:

char x = 'F';

Dalšími dvěma typy jsou funkční literály (o těch v další lekci) a delegátové literály (někdy příště). Kromě toho existují i literály polí a asociativních polí. Polím bude věnována samostatná lekce. Tímto bych asi tuto ukončil.

Domácí úkol

link
  1. Napište si pár Unicode řetězců, použijte některých escape sekvencí v nich. Zkuste jak UTF-8, tak UTF-16 a UTF-32.
  2. Znovu si zkuste napsat každý typ literálu a vyzkoušejte si jejich funkci.

Lekce 5 - funkce

link

Toto všechno je sice hezké, ale v reálných programech nemáme vše v jedné funkci main. Tudíž se naučíme s funkcemi pracovat. V této kapitole nestihneme o funkcích všechno. Pokračovat budeme příští týden, kdy se podíváme na přetěžování (overloading), funkce s variabilním počtem argumentů, vnořené funkce a delegáty.

Funkce

link

Pomocí funkcí můžeme program rozdělovat do přehledných struktur. Struktura funkcí vypadá nějak takto:

NÁVRATOVÝ_TYP název (parametr1, parametr2, parametr3, ...)
{
    tělo_funkce
}

Pak funkci můžeme zavolat:

název();

Příklad:

import std.stdio;
void foo()
{
    writeln("hello world");
}


void main() { foo(); }

Pokud z funkce nevracíme hodnotu, pak návratový typ je vždy void. Pro návrat hodnoty z funkce využíváme return:

int foo() { return 5; }

Pokud potřebujeme vyskočit z funkce, která nic nevrací, zapíšeme prostě return; (to se hodí při praci s podmínkami).

Argumenty funkce můžeme číst stejně, jako proměnné:

int foo(int a, int b) { return a + b; }
writeln(foo(5, 10));

Argumenty funkcí mohou mít různé atributy (in, out, ref, lazy, const, immutable, scope), např:

void foo(ref int x, in int y, out int z, int q);

in je ekvivalentní s const scope.

scope znamená, že reference v argumentu nemohou "utéct" (tzn. zůstanou v aktuálním kontextu). out nastaví hodnotu parametru na výchozí hodnotu typu. Např. zápis

void foo(out int x) {}
...
int a = 5;
foo(a);

způsobí, že po zavolání foo bude a rovno 0. Pokud by funkce přiřadila x parametru nějakou hodnotu, tak se změní i venku:

void foo(out int x) { x = 150; }
...
int a = 5;
foo(a);
/* a je teď 150 */

ref funguje podobně, ale nedojde ke změně hodnoty venku, pokud se explicitně nezmění vevnitř. ref je zkratkou k "reference". Reference je v podstatě "alias" k typu. Chová se jako standardní proměnná, ale nemá žádnou vlastní hodnotu a místo toho ukazuje na cizí hodnotu (reference jsou většinou implementované pomocí pointerů, ale jako pointery se nechovají, protože není možné změnit hodnotu, na kterou ukazují a chovají se jako "pravé" hodnoty, referencemi se budeme zabývat později).

lazy argumenty nejsou vyčísleny při zavolání funkce, ale až ve funkci. Tím pádem lze takový argument zavolat nula nebo vícekrát. Např:

void delej_neco_nekolikrat(int n, lazy void f)
{
    while (n--) f();
}
...
int x = 0;
delej_neco_nekolikrat(5, writeln(x++));

vypíše do konzole:

0

1

2

3

4

const znamená, že hodnota bude konstantní podle aktuální reference (ale jiná reference na stejnou paměť může). immutable se nemůže měnit, je pouze pro čtení. Problematikou const / immutable se budeme zabývat později.

Pokud argumenty nemají žádné atributy, pak se stanou mutabilními, lokálními kopiemi. Problematika atributů souvisí s ostatními funkcemi jazyka, takže v průběhu seriálu by se měly veškeré pochybnosti, popř. nepochopení vyjasnit.

Funkce samotné mohou mít různé atributy také.

Pure funkce

link

Pure funkce je taková funkce, která nemá žádné vedlejší efekty. Tyto funkce proto nemohou zapisovat globální proměnné, volat funkce, které nejsou pure, popř. provádět jakýkoliv vstup/výstup. Mohou ale alokovat paměť, ukončit program a pokud se jedná o debug kód, tak mít i vedlejší efekty. O těch více v kapitole o funkcionálním programování v D.

Nothrow funkce

link

Nothrow funkce nevrací žádné výjimky odvozené z třídy Exception. Výjimkami se budeme zabývat později.

Ref funkce

link

Analogicky k ref parametrům funkce se může reference i vracet. Příklad:

int a = 5; ref int foo() { return a; } .... foo() = 10;

Hodnota globální proměnné pak bude 10 (reference mohou být tzv. lvalue, tzn. hodnotami "vlevo", ke kterým přiřazujeme).

Auto funkce

link

Tyto funkce využívají tzv. typové inference ke zjištění typu návratové hodnoty. Příklad:

auto foo(int i) { return i * 2; } /* typ návratové hodnoty se
automaticky nastaví na int */

Auto ref funkce

link

Chovají se podobně jako kombinace ref funkcí a auto funkcí, s jedním rozdílem - automaticky si zjistí, jestli návratová hodnota je lvalue, a podle toho vrací buď referenci, nebo hodnotu.

auto ref foo(int a) { return a; } /* není lvalue, vrací hodnotu */
auto ref foo(ref int a) { return a; } /* vrací referenci */
auto ref foo(out int a) { return a; } /* vrací referenci */
auto ref foo() { return 150; } /* vrací hodnotu */

První return výraz ve funkci rozhoduje, jestli se vrací reference, nebo hodnota.

Inout funkce

link

Těmi se budeme zabývat v kapitole o const a imutabilitě. Vyjadřují jednotný zápis tří variant jedné funkce, kde první vrací mutabilní hodnotu, druhá konstantní hodnotu a třetí imutabilní hodnotu.

Výchozí hodnoty funkčních argumentů

link

U některých funkcí chceme, abychom nemuseli specifikovat všechny argumenty, ale aby měla funkce nějaké výchozí hodnoty. To uděláme snadno:

int foo(int a, int b, string c = "blah") { writeln(a, b, c); }

Potom při zavolání

foo(5, 3);

program vypíše "53blah", ale při

foo(5, 3, "foo");

vypíše "53foo". Pokud má některý argument výchozí hodnotu, všechny následující musí mít logicky také výchozí hodnoty.

To by bylo pro dnešek asi vše. Příští týden budeme s funkcemi pokračovat a uděláme i nějaké nové věci (viz začátek lekce).

Domácí úkol

link
  1. Ještě jednou si vyzkoušejte všechny atributy funkčních argumentů a zjistěte, jaký je rozdíl ve výstupu programu.
  2. Zkuste si různé varianty auto ref funkcí.
  3. Zkuste vracet různé hodnoty a výsledky operací s nimi z auto funkce.

Seriál Programování v jazyce D (dílů: 4)

První díl: Programování v jazyce D: Úvod a první kroky (1), poslední díl: Programování v jazyce D (4): Funkce a delegáty – pokračování, podmínky, cykly, pole, pointery.
Předchozí díl: Programování v jazyce D (2): Instalace, nastavení, první programy
Následující díl: Programování v jazyce D (4): Funkce a delegáty – pokračování, podmínky, cykly, pole, pointery

Další články z této rubriky

LLVM a Clang – více než dobrá náhrada za GCC
Ze 4 s na 0,9 s – programovací jazyk Vala v praxi
Reverzujeme ovladače pro USB HID zařízení
Linux: systémové volání splice()
Programování v jazyce Vala - základní prvky jazyka

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.