Obsah
1. Seznamy v calcu
2. Funkce určené pro práci se seznamy
3. Matice
4. Funkce určené pro práci s maticemi
5. Obsah dalšího pokračování tohoto seriálu
1. Seznamy v calcu
V předchozím pokračování tohoto seriálu jsme si řekli, že při programování (skriptování) v calcu není zapotřebí definovat typ proměnných, protože ten je odvozen přímo z obsahu proměnné. To však platí, podobně jako v dalších dynamicky typovaných jazycích, pro takzvané primitivní datové typy, za které jsou v calcu považována celá čísla s neomezeným rozsahem, reálná čísla s libovolnou přesností a rozsahem, komplexní čísla a mimo numerické hodnoty i znaky a řetězce. Při vytváření složitějších skriptů by však bylo výhodné číselné či řetězcové hodnoty sdružovat do větších celků, tj. strukturovaných datových typů. Calc pro tyto účely nabízí možnost práce se seznamy, maticemi a objekty. Při popisu těchto strukturovaných datových typů začneme u seznamů, které mají velký význam jak při zpracování numerických hodnot (pro tyto účely můžeme seznamy ztotožnit s vektory), tak i při načítání, zpracování a výstupu dat textových.
Seznam je v calcu chápán jako dynamická datová struktura, tj. jeho obsah se může za běhu programu měnit, což je rozdílné chování od seznamů známých například z programovacího jazyku Lisp, kde byly seznamy chápány funkcionálně jako konstantní objekty, tj. aplikací nějaké funkce na seznam vznikl nový seznam bez modifikace seznamu původního. Pro vytvoření seznamu není k dispozici žádná speciální syntaktická konstrukce (narozdíl od jazyků, jakými jsou Python či Perl), ale je použit konstruktor seznamů představovaný funkcí list(), která může mít libovolný počet parametrů – tyto parametry se po svém vyhodnocení stanou prvky seznamu a funkce list() vrátí seznam jako celek. Obsah seznamu je možné vypsat dvěma způsoby: buď pouze zadáním jména proměnné, do které je seznam přiřazen, nebo pomocí příkazu či funkce print. V níže uvedeném úryvku kódu je ukázáno vytvoření jednoduchého seznamu o pěti prvcích, přiřazení tohoto seznamu do proměnné a a následné vypsání obsahu seznamu na standardní výstup:
a=list(1,2,3,4);
print a;
list (4 elements, 4 nonzero):
[[0]] = 1
[[1]] = 2
[[2]] = 3
[[3]] = 4
Již jsme se zmínili o tom, že v calcu se seznamy chovají dynamicky, tj. je možné do nich přidávat nebo z nich ubírat prvky. Pro manipulaci s prvky, které se nachází na začátku seznamu, slouží funkce push() a pop(). Pomocí funkce push() se na začátek seznamu vloží další prvek, další prvky jsou přitom odsunuty (fyzické přesouvání prvků v operační paměti se však neprovádí, pouze se změní jejich indexy). Opak tvoří funkce pop(), která jeden prvek ze seznamu vyjme; hodnota vyjmutého prvku přitom tvoří návratovou hodnotu této funkce. Vzhledem k tomu, že se jedná o „normální“ funkce a nikoli metody objektů, musí být jako první parametr předán vlastní seznam, na který má být operace aplikována. Ukázka použití obou zmíněných funkcí, které se ve své podstatě k seznamu chovají jako k zásobníku, je uvedena v dalším příkladu (všimněte si, že poslední volání funkce pop() bylo aplikováno na prázdný seznam, funkce tedy nevrátila žádnou hodnotu):
b=list(1,2,3,4);
print b;
list (4 elements, 4 nonzero):
[[0]] = 1
[[1]] = 2
[[2]] = 3
[[3]] = 4
push(b,0);
print b;
list (5 elements, 4 nonzero):
[[0]] = 0
[[1]] = 1
[[2]] = 2
[[3]] = 3
[[4]] = 4
pop(b);
0
print b;
list (4 elements, 4 nonzero):
[[0]] = 1
[[1]] = 2
[[2]] = 3
[[3]] = 4
pop(b);
1
pop(b);
2
pop(b);
3
pop(b);
4
pop(b);
print(b);
list (0 elements, 0 nonzero)
Kromě funkcí push() a pop(), jež se používají k manipulaci se začátkem seznamu, jsou k dispozici i dvě funkce append(), které naopak manipulují s prvky nacházejícími se na konci seznamu. Pomocí funkce append() je možno k seznamu připojit další prvek, funkce remove() odstraní ze seznamu poslední prvek a současně vrátí jeho hodnotu. Pomocí dvojice funkcí push() a remove() je možné seznam používat jako frontu. Ukázka manipulace s koncem seznamu je ukázána na dalším příkladu:
c=list(1,2,3,4);
print c;
list (4 elements, 4 nonzero):
[[0]] = 1
[[1]] = 2
[[2]] = 3
[[3]] = 4
append(c,5);
print c;
list (5 elements, 5 nonzero):
[[0]] = 1
[[1]] = 2
[[2]] = 3
[[3]] = 4
[[4]] = 5
remove(c);
5
print c;
list (4 elements, 4 nonzero):
[[0]] = 1
[[1]] = 2
[[2]] = 3
[[3]] = 4
remove(c);
4
remove(c);
3
remove(c);
2
remove(c);
1
remove(c);
print c;
list (0 elements, 0 nonzero)
Seznam je v operační paměti uložen po jednotlivých prvcích, které jsou vzájemně svázány dvojicí ukazatelů. Jeden ukazatel ukazuje na předchůdce prvku, druhý ukazatel na svého následovníka. Díky tomu je možné manipulovat i s prvky, které se nacházejí „uvnitř“ seznamu, tj. nikoli nutně na jeho začátku či konci. Vložení prvku kdekoli dovnitř seznamu obstará funkce insert(), která jako svůj první parametr očekává seznam, jako druhý parametr index prvku a třetím parametrem je hodnota vkládaného prvku. Při práci s funkcí insert() si musíme uvědomit, že prvky uložené v seznamu jsou indexovány od nuly. O odstranění libovolného prvku ze seznamu se postará funkce delete(), přičemž index odstraňovaného prvku je předán jako druhý parametr této funkce. Opět následuje jednoduchý příklad použití těchto funkcí:
d=list(1,2,4,5);
insert(d,2,3);
print d;
list (5 elements, 5 nonzero):
[[0]] = 1
[[1]] = 2
[[2]] = 3
[[3]] = 4
[[4]] = 5
delete(d,2);
3
print d;
list (4 elements, 4 nonzero):
[[0]] = 1
[[1]] = 2
[[2]] = 4
[[3]] = 5
V předchozích odstavcích jsem se zmínil o tom, že prvky seznamu jsou indexovány od nuly. To je velmi důležitá informace, protože k prvkům seznamu je možné přistupovat přímo pomocí jejich indexů. Ty se musí zapisovat do dvojitých hranatých závorek, což je malý rozdíl oproti polím a maticím. Na dalším příkladu je ukázána smyčka, která programově vypíše obsah seznamu uloženého v proměnné e. Délka seznamu, tj. počet prvků, je získána pomocí funkce size():
e=list(10,20,30,40);
for (i=0; i<size(e); i++) print e[[i]];
10
20
30
40
Seznam nemusí obsahovat pouze numerické hodnoty, ale také (mimo jiných typů dat) řetězce. Zejména při zpracování textových dat je možné použít funkce search() pro vyhledání prvku v seznamu podle své hodnoty, rsearch() pro zpětné vyhledávání prvku a sort() pro setřídění prvků. Následuje ukázka použití funkcí search() a sort() na seznam obsahující řetězce, tj. textová data:
psi=list("cindy", "bobes", "zofka", "amy");
search(psi, "bobes");
1
search(psi, "vorech");
// nic se nevrátilo - prvek nebyl nalezen
sort(psi);
list (4 elements, 4 nonzero):
[[0]] = "amy"
[[1]] = "bobes"
[[2]] = "cindy"
[[3]] = "zofka"
2. Funkce určené pro práci se seznamy
V následující tabulce je proveden soupis některých funkcí, které je možné v calcu použít při práci se seznamy.
Jméno funkce | Význam funkce |
---|---|
list | konstruktor seznamu, tato funkce vytvoří seznam, do nějž vloží všechny zadané parametry |
obecný příkaz či funkce, která mj. slouží k výpisu obsahu seznamu | |
push | vložení nového prvku na začátek seznamu |
pop | vyjmutí prvku, který se nachází na začátku seznamu |
append | připojení prvku ke konci seznamu |
remove | odstranění posledního prvku ze seznamu |
insert | vložení prvku na libovolné místo v seznamu |
delete | odstranění libovolného prvku ze seznamu |
select | výběr prvků ze seznamu na základě zadané podmínkové funkce |
size | tato funkce vrátí počet prvků uložených v seznamu |
sort | setřídění prvků v seznamu |
search | vyhledání prvku v seznamu podle své hodnoty; vrací se index prvku nebo prázdná hodnota |
rsearch | odpovídá funkci search, ale prohledávání začíná od konce seznamu |
reverse | otočení prvků v seznamu (provádí se změnou ukazatelů, nikoli fyzickým přesunem dat) |
copy | kopie celého seznamu do seznamu dalšího |
count | vrací počet prvků ze seznamu, které odpovídají zadané podmínce |
sum | vrací součet hodnot všech prvků, které se nachází v seznamu |
3. Matice
Dalším strukturovaným datovým typem, který je možné v calcu použít, jsou matice. Význam matic je v matematice a na ni navazujících teoretických i aplikovaných disciplínách tak velký, že mnohé matematicky zaměřené aplikace (například známý Matlab či Octave) matice považují za hlavní datový typ a většina operací v nich s maticemi přímo pracuje, a/nebo jako svůj výsledek vrací matice. Podobně jako seznamy, i matice jsou v calcu implementovány jako dynamický datový typ, který může za běhu skriptu měnit svou hodnotu (resp. hodnoty) svých prvků. Matice mohou mít několik dimenzí, v současné verzi je však možné použít maximálně čtyřrozměrné matice, což by však pro většinu použití aplikačních skriptů mělo dostačovat. Jednorozměrná matice se považuje za vektor, který má, narozdíl od vektoru vytvořeného pomocí seznamu, pevnou velikost (délku).
Matice je možné vytvářet několika různými způsoby, nejpoužívanější je však způsob ukázaný na dalším demonstračním příkladu, ve kterém je uvedena ukázka vytvoření dvojdimenzionální matice nazvaná matice1. Jak je z tohoto příkladu patrné, je při vytváření matic použito slovo mat, za nímž následuje jméno matice a počet prvků v každé dimenzi. Inicializaci jednotlivých prvků je možné provést podobným způsobem, jaký je použit v programovacím jazyku C a jeho následovnících – ve složených závorkách jsou zapsány hodnoty prvků, přičemž celkový počet zadaných prvků by měl odpovídat násobku počtu prvků v každé dimenzi (na rozdíl od céčka se však nemusí hodnoty v jednotlivých dimenzích uzavírat do vlastní dvojice složených závorek, protože tato jazyková konstrukce má v calcu poněkud jiný význam). Příkaz print, který jsme používali při práci se seznamy, je možné použít i při práci s maticemi, podobný je i způsob výpisu prvků.
mat matice1[3,3]={1,2,3,4,5,6,7,8,9}
print matice1
mat [3,3] (9 elements, 9 nonzero):
[0,0] = 1
[0,1] = 2
[0,2] = 3
[1,0] = 4
[1,1] = 5
[1,2] = 6
[2,0] = 7
[2,1] = 8
[2,2] = 9
Jednorozměrný vektor se vytváří následujícím způsobem:
mat vektor[3]={1,2,3}
print vektor
mat [3] (3 elements, 3 nonzero):
[0] = 1
[1] = 2
[2] = 3
K prvkům matice je možné přistupovat pomocí indexů zapsaných ve dvojitých hranatých závorkách, jednotlivé indexy jsou přitom odděleny čárkou, což přístup k prvkům matice odlišuje od způsobu přístupu k prvkům polí. Vzhledem k důležitosti matic v matematice jsou v calcu dostupné operátory, které pracují přímo s maticemi. Matice se tedy při použití operátorů dostávají na úroveň primitivních datových typů. Mezi operátory, které je možné při práci s maticemi použít, patří:
Operátor | Význam operátoru |
---|---|
== | vrací pravdivostní hodnotu „true“ v případě, že jsou matice ekvivalentní, tj. mají stejnou velikost (resp. tvar) i hodnoty korespondujících prvků |
+ | součet dvojice matic stejné velikosti (tato operace se provádí prvek po prvku) |
– | rozdíl dvojice matic stejné velikosti (tato operace se provádí prvek po prvku) |
* | (první operand je matice, druhý operand je numerická hodnota) vynásobení prvků matice zadanou numerickou hodnotou |
* | (první operand je numerická hodnota, druhý operand matice) vynásobení prvků matice zadanou numerickou hodnotou |
/ | (první operand je matice, druhý operand je numerická hodnota) vydělení prvků matice zadanou numerickou hodnotou |
* | (oba operandy jsou matice) maticové násobení, při kterém musí být velikosti matic stejné, ale tvar druhé matice je transponovaný (matice je "otočená) |
Velmi často používanými operacemi u matic je výpočet determinantu a inversní matice. Determinant matice (ovšem pouze matice čtvercové) je možné získat funkcí det(), inversní čtvercovou matici pomocí funkce inverse(). V následujícím příkladu je ukázka použití těchto funkcí, včetně případu, kdy inversní matici není možné spočítat, protože zdrojová matice má determinant rovný nule (je singulární):
mat matice2[3,3]={3,0,0,0,1,2,4,5,6}
det(matice2)
-12
inverse(matice2)
mat [3,3] (9 elements, 7 nonzero):
[0,0] = ~0.33333333333333333333
[0,1] = 0
[0,2] = 0
[1,0] = ~-0.66666666666666666667
[1,1] = -1.5
[1,2] = 0.5
[2,0] = ~0.33333333333333333333
[2,1] = 1.25
[2,2] = -0.25
mat matice3[3,3]={1,2,3,4,5,6,7,8,9}
print matice2
det(matice3)
0
inverse(matice3)
Matrix is not invertible
Pokud jsou výpočty prováděny s vektory, tj. jednodimenzionálními maticemi, jsou k dispozici dvě užitečné „vektorové“ funkce. První funkcí je dp() (skalární součin, výsledkem je jedna numerická hodnota), druhou funkcí je cp() (vektorový součin, výsledkem je vektor kolmý na oba zadané vektory). Názvy těchto funkcí pochází z anglických výrazů „dot product“ a „cross product“. Následuje ukázka použití těchto vektorových funkcí, včetně obligátního sčítání a odčítání vektorů (plus operace pro násobení prvků uložených ve vektorech):
mat vector1[3]={1,2,3}
mat vector2[3]={7,8,9}
vector1+vector2
mat [3] (3 elements, 3 nonzero):
[0] = 8
[1] = 10
[2] = 12
vector1-vector2
mat [3] (3 elements, 3 nonzero):
[0] = -6
[1] = -6
[2] = -6
vector1*vector2
mat [3] (3 elements, 3 nonzero):
[0] = 7
[1] = 16
[2] = 27
dp(vector1, vector2)
50
cp(vector1, vector2)
mat [3] (3 elements, 3 nonzero):
[0] = -6
[1] = 12
[2] = -6
4. Funkce určené pro práci s maticemi
Jméno funkce | Význam funkce |
---|---|
size | tato funkce vrací počet prvků v nejvyšší dimenzi zadané matice |
det | výpočet determinantu matice |
inverse | výpočet inversní matice |
dp | skalární součin dvojice matic, které musí být jednodimenzionální |
cp | vektorový součin dvojice matic, které musí být jednodimenzionální |
sort | setřídění prvku v matici |
mattrace | součet prvků vektoru (jednodimenzionální matice) nebo součet prvků na diagonále 2D matice |
mattrans | transpozice dvojrozměrné matice |
reverse | změna pořadí všech prvků v matici |
search | nalezení prvku o zadané hodnotě |
5. Obsah dalšího pokračování tohoto seriálu
Popis aplikace calc se nám dnešní částí rozrostl o jeden díl, ale v příštím pokračování tohoto seriálu se již touto aplikací budeme zabývat naposled. Popíšeme se práci s uživatelsky definovanými objekty, formátování výstupu a také vstupně/výstupní operace, včetně manipulace se soubory.