Obsah
1. Specializované jazyky pro práci s N-dimenzionálními poli: jazyk J
2. Od APL k programovacímu jazyku J
3. Překlad interpretru jazyka J
5. Atomy a N-dimenzionální pole v programovacím jazyku J
7. Pravidla pro pojmenování primitivních funkcí
8. Vyhodnocování aritmetických a logických výrazů
9. Operace s vektory v programovacím jazyku J
10. Základní aritmetické funkce aplikované na vektory
11. Výběr prvků z vektorů (selektory)
12. Konstrukce vektorů založená na generátorech (indexů)
15. Predikáty pracující s atomy, vektory i maticemi
16. Různé způsoby spojení polí
17. Pokročilejší operace nad poli
19. Dyadická forma operátoru /
1. Specializované jazyky pro práci s N-dimenzionálními poli: jazyk J
„It is easier to learn enough Basic or Java to solve trivial problems, but it is easier to learn enough J to solve more interesting and challenging problems.“
Na úvodní článek o programovacích jazycích určených pro provádění operací s poli dnes navážeme. Zatímco minule jsme se zabývali především univerzálními jazyky, dnes se zaměříme na programovací jazyk J, který je přímo určen pro práci s poli (a lze ho považovat za skvělý doménově specifický jazyk). Jedná se o ideového pokračovatele jazyka APL, takže v žádném případě nečekejte „mainstreamový“ přístup. Je tomu naopak, protože prakticky všechny operace a funkce se zapisují odlišně, než je tomu v běžnějších programovacích jazycích. V rámci dnešního článku si taktéž odpovíme na dotazy zmíněné minule:
- Kolik dimenzí může pole mít? Typicky 1 a 2, někdy i více.
- Začínají indexy prvků od 0, 1 či je první index volitelný?
- Jsou podporována obdélníková pole nebo nepravidelná pole?
- Jsou jednotlivé osy na sobě nezávislé? (což vylučuje nepravidelná pole)
- Je možné indexy na jednotlivých osách pojmenovat? (a vytvořit tak vlastně datový rámec)
- Jedná se o homogenní nebo o heterogenní datovou strukturu? Homogenní struktura může uchovávat prvky jediného (typicky předem definovaného) typu, zatímco v heterogenní struktuře mohou být umístěny prvky různých typů.
- Je nějakým způsobem omezen datový typ prvků pole? (například jen na celá čísla a čísla reálná).
- Lze prvky pole měnit (mutable) nebo je pole neměnitelné (immutable).
- Pokud jsou pole heterogenní a měnitelná, může prvek pole obsahovat to samé pole?
- Obsahuje pole přímo hodnoty prvků nebo jen reference na prvky?
- Jsou prvky v poli uloženy v operační paměti za sebou nebo se jedná o strukturu s ukazateli?
- Jsou prvky v 2D poli uloženy po řádcích nebo po sloupcích? (C versus Fortran).
- Lze měnit tvar (shape) pole?
- Podporuje jazyk operace nad celými poli?
- Podporuje jazyk takzvaný broadcasting (aplikaci skaláru na všechny prvky pole atd.)?
- Jsou pole plnohodnotným datovým typem nebo speciální strukturou?
- Je podporován „literál typu pole“?
2. Od APL k programovacímu jazyku J
„Real Programmers don't write in APL, unless the whole program can be written on one line“
Historie programovacího jazyka APL, s nímž jsme se seznámili již v tomto článku, se začala psát již v roce 1957. Právě tehdy Kenneth E. Iverson, který v té době pracoval na Harvardské universitě, navrhl zcela novou notaci určenou pro jednotný zápis matematických funkcí i pro tvorbu funkcí nových. Tato notace byla prakticky ve všech ohledech odlišná od zavedené notace (resp. přesněji řečeno) notací, které se v matematice používaly. V roce 1962 byla tato notace popsána v knize nazvané „A Programming Language“ a (což původně vlastně ani nebylo plánováno) o několik let později se začaly objevovat skutečné interpretry i překladače programovacího jazyka používajícího stejné symboly, jaké popsal Iverson ve své knize. Konkrétně se jednalo o jazyk nazvaný IVSYS, který byl implementovaný v roce 1966 (což ovšem ještě nebyl skutečný APL, který byl následován jazykem APL\360 z roku 1966 a poté jazykem APL\1130 z roku 1968, jenž pracoval na počítači IBM 1130.
Kenneth Iverson se rozvoji a propagaci jazyka APL aktivně věnoval více než 20 let, za což mu poprávu byla v roce 1979 udělena Turingova cena (Turing Award). Později Iverson navrhl programovací jazyk J, v němž se místo speciálních symbolů používají pouze znaky obsažené v ASCII tabulce (viz další kapitoly s podrobnějšími informacemi). Vraťme se však k APL. Specifikace tohoto programovacího jazyka se od poloviny šedesátých let minulého století několikrát změnila (rozšířila). Jedním z nejvíce důležitých rozšíření je APL2 od J. Browna z IBM, které do jazyka zavedlo podporu pro rekurzivní datové struktury (tedy, jak uvidíme dále, pro pole jako prvky jiných polí, což je mimochodem dále zobecněno v jazyku BQN a vlastně i v jazyku J). Většina dnešních implementací tohoto programovacího jazyka se snaží dodržovat specifikaci APL2, i když je jazyk dále rozšiřován (například v APLX verze 4 byla přidána podpora pro objektově orientované programování, i když v poněkud jiné podobě, než ji známe z mainstreamových programovacích jazyků). Z moderních implementací APL se pak musíme zmínit o komerčním projektu Dyalog APL.
Nesmíme ovšem ignorovat fakt, že i přes všechny zajímavé rysy má programovací jazyk APL i několik nectností, ostatně jako každý programovací jazyk, který vznikl na samotném začátku vývoje interpretrů a překladačů, tj. v době, kdy ještě nebyla teorie překladačů tak rozvinutá jako v pozdějších letech (nejvíce byla neexistence teorie překladačů patrná u prvního překladače Fortranu vytvořeného firmou IBM, jehož vývoj by byl při uplatnění dnešních znalostí mnohem rychlejší, levnější, samozřejmě i samotný jazyk by vypadal poněkud jinak). Už při letmém pohledu na algoritmy, resp. celé programy napsané v programovacím jazyce APL, je zřejmé, že se v něm používá velké množství symbolů, které se nenachází ve znakové sadě ASCII, což je sice velmi unikátní vlastnost (právě proto mnozí v APL programují čistě pro radost z vizuální podoby programů), ale způsobuje poměrně velké problémy, jak při zápisu programů (rozložení znaků na klávesnici), tak i při jejich úpravách, prezentaci na Internetu (zdaleka ne všechny fonty obsahují všechny požadované symboly) atd.
Některé nedostatky programovacího jazyka APL jsou obsaženy i v jeho samotné sémantice, což je mnohem závažnější, než „pouhý“ vzhled zápisu nebo problémy při tisku nebo prezentacích. Jmenujme například zápis řídicích struktur s využitím čísel řádků (podobně jako v BASICu, i když čísla řádků jsou v APL pro každou funkci lokální, což například znemožňuje provádět skoky dovnitř funkcí o odstraňuje vzájemnou závislost jednotlivých funkcí). A taktéž se ukázalo, že by bylo vhodné, aby byl do mnohem větší míry podporován tacit programming, jímž jsme se zabývali v samostatném článku. Z výše uvedených důvodů otec jazyka APL (již zmiňovaný Kenneth Iverson) na počátku devadesátých let minulého století navrhl nový programovací jazyk nazvaný jednoduše J, který některé výše zmíněné nedostatky jazyka APL odstranil a navíc tento nový programovací jazyk rozšířil o některé důležité nové rysy, primitivní funkce i operátory. V dalších kapitolách si stručně popíšeme zejména ty vlastnosti jazyka J, které souvisí se zpracováním polí a vůbec k přístupu k této velmi důležité datové struktuře.
3. Překlad interpretru jazyka J
Na stránce http://www.jsoftware.com jsou nabízeny balíčky s interpretrem jazyka J určené pro všechny tři nejrozšířenější desktopové operační systémy. Po instalaci (která v případě Linuxu spočívá v prostém spuštění staženého skriptu s parametrem -install) se nainstaluje jak interpret programovacího jazyka J (představovaného knihovnou libj.so popř. j.dll), tak i konzolové (řádkové) rozhraní k interpretru (jconsole) a vývojové prostředí naprogramované původně v Javě (jwd), dnes dostupné přes webové rozhraní. Pro odzkoušení demonstračních programů postačuje spustit řádkové rozhraní interpretru jazyka J z terminálu resp. z konzole – jedná se o program nazvaný jconsole.
Protože se však nacházíme na Rootu, ukážeme si, jakým způsobem je možné konzoli s interpretrem programovacího jazyka J přeložit přímo ze zdrojových kódů. Projekt s jazykem J totiž po velmi dlouhé době, kdy se jednalo o projekt dosti izolovaný od ostatního světa a s relativně uzavřenou a malou komunitou, nabízí zdrojové kódy dostupné přes GitHub a přeložitelné jednoduše z příkazové řádky.
Celý postup překladu je jednoduchý. Nejdříve je nutné naklonovat repositář se zdrojovými kódy (kombinace C a J):
$ git clone git@github.com:jsoftware/jsource.git
Dále se v podadresáři make2 spustí skript nazvaný build_all:
$ cd make2 $ ./build_all
Překlad je ve výchozím nastavení prováděn v jediném vláknu, ovšem ani tak netrvá dlouho. Po dokončení činnosti skriptu build_all by se měl v podadresáři bin/linux/j64avx objevit jak interpret (představovaný knihovnou), tak i aplikace jconsole, která interpret jazyka J spustí:
$ cd ..
O výsledku překladu se snadno přesvědčíme:
$ ls -la bin/linux/j64avx/ total 4192 drwxrwxr-x 2 ptisnovs ptisnovs 100 Dec 6 19:54 . drwxrwxr-x 3 ptisnovs ptisnovs 60 Dec 6 19:46 .. -rwxrwxr-x 1 ptisnovs ptisnovs 57184 Dec 6 19:52 jconsole -rwxrwxr-x 1 ptisnovs ptisnovs 4201104 Dec 6 19:54 libj.so -rwxrwxr-x 1 ptisnovs ptisnovs 31984 Dec 6 19:54 libtsdll.so
Samotný interpret je možné spustit takto:
$ bin/linux/j64avx/jconsole
Obrázek 1: Původní vývojové prostředí (vytvořené v Javě) dodávané spolu s interpretrem programovacího jazyka J.
4. Zápis programů v jazyku J
Nyní, když již máme k dispozici interpret jazyka J, si můžeme prakticky vyzkoušet některé jeho vlastnosti. Nejprve si řekneme, jak vypadá struktura zapsaných programů. Způsob zápisu programů v programovacím jazyku J se v mnoha ohledech podobá zápisu používaném v programovacím jazyku APL. Na každém textovém řádku je (tedy většinou – pokud je autor při smyslech) zapsán jeden výraz, který je interpretrem ihned po svém zápisu zpracován a vyhodnocen. V případě, že je výraz zapsaný v interaktivním režimu (například z aplikace jconsole), je výsledek výrazu ihned po jeho vyhodnocení vytištěn na terminál – právě tento způsob práce, který je ostatně společný většině interpretovaných programovacích jazyků, budeme používat ve všech následujících demonstračních příkladech (příkazy zadávané uživatelem jsou pro odlišení zpráv interpretru odsazeny od levého okraje, což je implicitní chování nástroje jconsole; taktéž jsou příkazy zadávané uživatelem zvýrazněny pomocí tučného písma).
Zajímavý a nutno říci, že i dosti neobvyklý je i zápis komentářů. V mnoha programovacích jazycích se pro označení začátku jednořádkových komentářů používá speciální znak či znaky. Například v C++, Javě či JavaScriptu se jedná o dvojici znaků „//“ (nebo o „komentářové závorky“ /* a */ pro víceřádkové komentáře) a v shellu i Pythonu o jeden znak „#“. V programovacím jazyku J je ovšem vše jinak. Zde se totiž začátek komentáře označuje trojicí znaků „NB.“. Veškerý text na řádku zapsaný za těmito znaky je interpretrem ignorován. To tedy znamená, že můžeme psát:
NB. komentar umisteny na zacatku programoveho radku 1+2*3 NB. komentar umisteny za vyrazem, samotny vyraz se vykona a vypise se jeho vysledek 7 NB. tento vyraz se nevykona, nebot je soucasti komentare: 1+2*3
5. Atomy a N-dimenzionální pole v programovacím jazyku J
V programovacím jazyku J se primárně rozlišují dvě skupiny typů hodnot. První skupinu tvoří atomy (atom), druhou skupinu pak pole (array). Atomy neboli skaláry je skupina tvořená třemi datovými typy:
- numerický typ
- znakový typ
Nejzajímavějším atomárním neboli skalárním datovým typem je box. Tento název je velmi přiléhavý, protože se skutečně jedná o jakýsi obal, který může obsahovat libovolnou jinou hodnotu – ať již skalární, tak i pole. Díky existenci boxů je sice typový systém programovacího jazyka J poměrně flexibilní, ovšem ukazuje se, že komplikuje práci s poli. Proto je například v jazyku BQN, který je ideovým pokračovatelem APL i J, použit odlišný typový systém.
Ústředním prvkem jazyka J jsou ovšem – jak lze snadno uhodnout – pole. Na pole se můžeme dívat jako na skupinu atomů organizovaných podél os (axes), přičemž počet os určuje dimenzi pole. Os přitom může být (prakticky) libovolný počet – J tedy nemá žádná omezení, která jsme mohli vidět například v BBC BASICU či v Dartmouth BASICu. Další důležitou vlastností polí je, že jsou homogenní, tj. obsahují prvky stejného typu (nebo boxy). A navíc jsou pole měnitelná, neboli mutable – prvky polí je tedy možné modifikovat. Od jazyka APL se J odlišuje v tom, že se prvky podél os indexují od nuly a nikoli od jedničky, což si ukážeme na demonstračních příkladech.
Pole s počtem dimenzí (resp. os) 1 až 3 mají v jazyku J specifické názvy:
Počet dimenzí/os | Název pole |
---|---|
1 | list |
2 | table |
3 | brick |
Pole s jednou, dvěma a třemi osami lze vytvořit funkcí i., která zhruba odpovídá generátoru range z Pythonu, ovšem s tím, rozdílem, že počet os a tvar pole je volitelný:
i. 3 0 1 2 i. 2 3 0 1 2 3 4 5 i. 2 3 4 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Přečtení tvaru pole – seznamu obsahujícího počet atomů podél každé osy funkcí $ (shape):
$ 42 $ 1 2 3 3 $ i. 2 3 2 3 $ i. 2 3 4 2 3 4 $ (<'abc'),(<1;2;3),(<1;2) 3
Získání počtu os monadickou funkcí # (rank) zkombinovanou s #:
# $ 42 0 # $ 1 2 3 1 # $ 1 2 3 4 1 # $ i. 2 3 2 # $ i. 2 3 4 3 # $ (<'abc'),(<1;2;3),(<1;2) 1
Víme již, že pole musí být homogenní, což je podmínka, kterou lze „obejít“ použitím boxů. Termínem boxing se označuje vytvoření atomu (boxu), jehož obsah je prakticky libovolný, tedy může se vytvořit homogenní pole boxů. Samozřejmě jsou ovšem nutná nějaká omezení, protože box není numerickým typem a tudíž ho nelze použít v aritmetických operacích. Box je ovšem možné otevřít (open) a získat tak jeho obsah.
Vytvoření boxu – zabalení hodnoty:
(<'abc') +---+ |abc| +---+
Spojení dvou boxů do vektoru:
(<'abc'),(<1 2 3) +---+-----+ |abc|1 2 3| +---+-----+
Box jako prvek pole s boxy:
(<'abc'),(<1 2 3),(<1;2) +---+-----+-----+ |abc|1 2 3|+-+-+| | | ||1|2|| | | |+-+-+| +---+-----+-----+ (<'abc'),(<1;2;3),(<1;2) +---+-------+-----+ |abc|+-+-+-+|+-+-+| | ||1|2|3|||1|2|| | |+-+-+-+|+-+-+| +---+-------+-----+
A konečně si ukažme funkci amend, která dokáže změnit obsah vybraného prvku pole:
100 (< 2) } i. 10 0 1 100 3 4 5 6 7 8 9
6. Primitivní funkce jazyka J
Podobně jako v programovacím jazyku APL, nalezneme i v dnes popisovaném jazyku J poměrně velké množství primitivních (neboli základních) funkcí. Ty však nejsou reprezentovány speciálními symboly („hieroglyfy“), ale jedním nebo větším množstvím nealfanumerických ASCII znaků, takže jejich zápis i editace je jednodušší, než tomu bylo v případě APL (na druhou stranu nevypadají zapsané algoritmy tak efektně a možná i elegantně :-). Primitivní funkce mohou akceptovat buď jeden parametr (to jsou takzvané monadické funkce) nebo parametry dva (funkce dyadické). Název monadických funkcí je uveden v prefixové podobě, tj. vždy před svým parametrem, zatímco dyadické funkce jsou zapisovány v podobě infixové – mezi oběma parametry, čímž se efektivně v mnoha případech eliminuje nutnost použití závorek (ty jsou ovšem taktéž podporovány). Znaky, kterými jsou primitivní funkce pojmenovány, jsou většinou použity minimálně dvakrát – jednou pro zápis monadické funkce, podruhé pro zápis funkce dyadické, jejíž význam bývá v menší či větší míře podobný příslušné monadické funkci.
Obrázek 2: Interpret jazyka J je poměrně bohatě vybaven i možností práce s různými typy grafů, což je (spolu s podporou snadné práce s vektory a maticemi) jeden z důvodů poměrně velké oblíbenosti tohoto jazyka mezi inženýry.
7. Pravidla pro pojmenování primitivních funkcí
Kromě použití stejného znaku pro pojmenování nějaké primitivní monadické a současně i dyadické funkce může být navíc za znakem představujícím jméno primitivní funkce uveden symbol „:“ (dvojtečka) nebo „.“ (tečka), což v praxi znamená, že tentýž znak může být ve skutečnosti použit pro pojmenování až šesti různých primitivních funkcí. Například pro znak „<“ existuje celkem pět různých forem, které byly Iversonem a jeho kolegy při návrhu jazyka J zvoleny takovým způsobem, aby buď přímo odpovídaly významu tohoto znaku v matematickém zápisu, nebo měly alespoň logicky podobný význam. Zmíněný znak je (nejenom) v matematice spojen se slovem „menší“, takže je použit i pro implementaci funkce vyhledání minima či dekrementaci/zmenšení operandu o jedničku, jak je to ostatně patrné z následujících výrazů a jejich výsledku po vyhodnocení interpretrem programovacího jazyka J (v příkladech jsou jako parametry použity i vektory):
NB. dyadicka funkce < NB. porovnani dvou hodnot na relaci "mensi nez" 1 < 2 1 2 < 2 0 NB. porovnavat lze i slozky vektoru, vysledkem bude bitovy vektor 1 2 3 < 3 2 1 1 0 0 NB. Dyadicka funkce ukoncena dvojteckou NB. porovnani dvou hodnot na relaci "mensi nebo rovno" 1 <: 2 1 2 <: 2 1 NB. opet lze porovnavat i slozky vektoru 1 2 3 <: 3 2 1 1 1 0 NB. dyadicka funkce ukoncena teckou NB. vrati mensi operand (parametr) = odpovida funkci min 1 <. 2 1 2 <. 1 1 NB. porovnani prislusnych slozek vektoru a vraceni mensi z nich 1 2 1 <: 3 2 1 1 2 1 NB. monadicka funkce ukoncena teckou NB. zaokrouhleni smerem dolu (obdoba ceckove funkce floor) <. 2.5 2 <. 2.9 2 NB. tuto funkci lze samozrejme volat i na vektory <. 1.9 2.0 2.1 2.9 1 2 2 2 NB. monadicka funkce ukoncena dvojteckou NB. operace dekrementace (obdoba ceckoveho operatoru --) <: 1 0 <: 10 9 <: 1 2 3 0 1 2 <: 0 1 2 _1 0 1
8. Vyhodnocování aritmetických a logických výrazů
Aritmetické a logické výrazy, které většinou tvoří nejdůležitější součást všech programů zapisovaných v jazyku J, se vyhodnocují stejným způsobem, jako v již popsaném programovacím jazyku APL, tj. zprava doleva bez toho, aby některé funkce měly vyšší prioritu než funkce jiné. Funkce se zapisují stejným způsobem jako v jiných jazycích prefixové a infixové operátory, tj. buď mezi oba argumenty (operandy) při volání dyadických funkcí nebo před jediný argument v případě, že se volá funkce monadická. V případě, že je zapotřebí změnit pořadí volání funkcí, lze k tomuto účelu použít obligátní kulaté závorky. V jazyku J je k dispozici pět základních aritmetických funkcí, které jsou vypsány v tabulce pod odstavcem (povšimněte si především odlišného způsobu zápisu funkce podílu dvou hodnot). Do tabulky jsou pro úplnost dopsány i tři další funkce pro výpočet mocniny, odmocniny a faktoriálu. Způsob použití těchto funkcí i způsob úpravy priority (pořadí volání) je patrný z demonstračních příkladů uvedených pod tabulkou.
Znak funkce | Monadická funkce | Dyadická funkce |
---|---|---|
+ | negace imaginární složky komplexního čísla | součet (skalárů, vektorů, matic…) |
– | negace | rozdíl |
* | vrací znaménko | součin |
% | převrácená hodnota | podíl |
| | absolutní hodnota | zbytek |
^ | umocnění xy | |
*: | druhá mocnina x2 | |
%: | druhá odmocnina x1/2 | |
! | faktoriál |
Příklad použití operací:
NB. priorita je vždy zprava doleva 1+2*3 7 NB. specifikace priority zavorkami 1+(2*3) 7 NB. specifikace priority zavorkami (1+2)*3 9 NB. funkce jsou vyhodnocovany zprava doleva, NB. tj. zde nejdrive soucet a teprve pote soucin 3*2+1 9 NB. specifikace priority zavorkami 3*(2+1) 9 NB. zmena poradi vyhodnoceni funkci pomoci zavorek (3*2)+1 7 NB. zaporna cisla jsou zapisovana se symbolem _ 10-20 _10 NB. podil se zapisuje symbolem % 42%3 14 NB. druha mocnina je zapisovana pomoci monadicke funkce *: *: 10 100 NB. vypocet prevracene (reciproke) hodnoty % 3 0.33333 NB. vypocet desate mocniny 2^10 1024 NB. zaporny exponent 2^(_10) 0.000976562 NB. aritmeticke funkce lze pouzit i pro soucet vektoru 1 2 3 + 4 5 6 5 7 9 NB. vydeleni skalarni hodnoty postupne vsemi slozkami vektoru 42 % 2 3 4 21 14 10.5 NB. kazda slozka vektoru je umocnena na desatou 2 3 4 ^ 10 1024 59049 1.04858e6 NB. vypocet druhych mocnin vsech slozek vektoru *: 1 2 3 4 1 4 9 16 NB. vypocet faktorialu (samozrejme je pouzit prefixovy zapis) !6 720 !170 7.25742e306 NB. tento zapis si podrobneji vysvetlime pozdeji !i.10 1 1 2 6 24 120 720 5040 40320 362880
9. Operace s vektory v programovacím jazyku J
Jak jsme se již dozvěděli z úvodního textu, je programovací jazyk J, ostatně podobně jako jeho ideový předchůdce APL, určen především pro tvorbu aplikací, v nichž se zpracovávají data uložená ve vektorech, maticích či polích s větším počtem dimenzí (může se jednat například o hierarchické mřížky atd., a to mj. i díky možnosti použít boxing). Z tohoto důvodu je jazyk J vybaven jak jednoduchou syntaxí určenou pro zápis vektorů a matic, tak i sadou primitivních (základních) funkcí, pomocí nichž lze nad vektory i maticemi provádět různé operace. Nejprve si popíšeme primitivní funkce určené pro vytváření i další práci s vektory, které jsou vypsány v následující tabulce.
U všech popisovaných funkcí bude uvedena i informace o tom, zda se jedná o funkci monadickou (tj. s jedním parametrem zapisovaným za symbol funkce) či dyadickou (s dvojicí parametrů, z nichž první se zapisuje před a druhý za symbol funkce). Tato informace je v případě jazyků J i APL velmi důležitá, protože již víme, že stejný symbol může být použit pro pojmenování dvou primitivních funkcí lišících se „pouze“ počtem svých parametrů:
Symbol funkce | Forma funkce | Popis funkce (význam) |
---|---|---|
+ – * % | dyadická | základní aritmetické operace prováděné nad dvojicí vektorů na korespondujících prvcích (též prováděné nad skalárem a vektorem) |
< <: > >: = ~: | dyadická | porovnání korespondujících prvků dvou vektorů |
# | monadická | vrací délku vektoru |
# | dyadická | kopie prvků vektoru představovaného druhým parametrem |
{ | dyadická | výběr prvku či více prvků z vektoru na základě indexů vybíraných prvků |
{. | dyadická | výběr prvních n prvků z vektoru |
}. | dyadická | výběr posledních délka-n prvků vektoru (= odstranění prvních n prvků) |
, | dyadická | spojení dvou vektorů či vektoru se skalárem |
/: | monadická | setřídění prvků vektoru sestupně (funkce vrací indexy prvků, ne jejich hodnoty) |
\: | monadická | setřídění prvků vektoru vzestupně (funkce též vrací indexy prvků, ne jejich hodnoty) |
i. | monadická | vytváří seznam (vektor) obsahující řadu čísel začínající nulou, popř. prázdný vektor |
i: | monadická | vytváří seznam (vektor) obsahující čísla on -n do n, kde n je parametr funkce |
p. | monadická | výpočet kořenů polynomu reprezentovaného vektorem obsahujícím koeficienty ai |
10. Základní aritmetické funkce aplikované na vektory
Základní aritmetické operace, se kterými jsme se již seznámili v předchozích kapitolách (aplikovaných na skaláry), je možné využít i při práci s vektory. V tomto případě se operace provádí vždy nad dvojicí odpovídajících prvků (dyadické funkce) popř. postupně pro všechny prvky vektoru (funkce monadické). Pokud nejsou délky vektorů shodné, nahlásí interpretr jazyka J chybu typu „length error“ (jen u několika specifických operací se délka kratšího vektoru „dorovná“ s vektorem delším):
NB. Nejdříve vytvoříme tři proměnné NB. představující trojici vektorů (různé délky) x =: 1 2 3 4 y =: 9 8 7 6 z =: 11 12 NB. Čtyři základní aritmetické operace NB. (součet, rozdíl, součin, podíl) NB. prováděné nad prvky vektorů x+y 10 10 10 10 y-x 8 6 4 2 x*y 9 16 21 24 x%y 0.111111 0.25 0.428571 0.666667 NB. Následující příkaz skončí s chybou NB. neboť délky vektorů (=počty jejich NB. prvků) nejsou shodné x+z |length error | x +z
Při použití aritmetických funkcí může být jedním z parametrů i skalární hodnota, která je aplikována na všechny prvky vektoru. Viz též následující demonstrační příklady:
NB. Při volání aritmetických funkcí NB. je možné zkombinovat skalární hodnotu NB. s vektorem 2 * x 2 4 6 8 x * 2 2 4 6 8 NB. Předchozí dva výrazy 2 * x a 2 * y NB. sice vrátily shodný výsledek, ale některé NB. další aritmetické operace nejsou NB. komutativní, například dělení: 10 % x 10 5 3.33333 2.5 x % 10 0.1 0.2 0.3 0.4
Následují dva příklady použití dalších funkcí, konkrétně dyadické exponenciální funkce a monadické funkce pro výpočet faktoriálu:
NB. Při práci s vektory lze samozřejmě NB. použít i další primitivní funkce, NB. zde například funkci exponenciální x^y 1 256 2187 4096 NB. Monadická funkce (výpočet faktoriálu) ! x 1 2 6 24
11. Výběr prvků z vektorů (selektory)
Další důležitou a současně i velmi často používanou skupinou základních (primitivních) funkcí určených pro práci s vektory jsou funkce, které umožňují z vektoru vybrat hodnoty některých prvků. Jedná se o rozšíření funkcí „take“ a „drop“, se kterými jsme se již seznámili při popisu programovacího jazyka APL, ovšem s tím rozdílem, že prvky vektorů jsou v případě jazyka J číslovány od nuly, tj. první prvek má index roven 0 a index posledního prvku je roven délce vektoru-1 (což je mimochodem poměrně zvláštní ústupek mainstreamu – v jiných ohledech je J tak vzdálen od mainstreamu tak, jak je to jen možné). Dále se v této skupině nachází funkce umožňující provést spojení dvou vektorů, která se zapisuje pomocí ASCII znaku „,“ – čárka. V aplikacích se poměrně často vyskytuje i funkce vracející délku vektoru (ASCII znak „#“). V případě matic či polí tato funkce vrací rozměr všech jejich dimenzí, což si ukážeme v navazujících kapitolách.
Základní způsoby použití selektorů si pochopitelně opět ukážeme na demonstračních příkladech:
NB. Nejdříve opět vytvoříme tři proměnné NB. představující trojici vektorů x =: 1 2 3 4 y =: 9 8 7 6 z =: 11 12 NB. Zjistíme délky (počet prvků) všech vektorů #x 4 #y 4 #z 2 NB. Spojení vektorů pomocí primitivní NB. funkce , (čárka) w =: x,y,z w 1 2 3 4 9 8 7 6 11 12 NB. Jak je výsledný vektor w dlouhý? #w 10 NB. Výběr pátého prvku (s indexem 4) 4 { w 9 NB. Výběr pátého, šestého a sedmého NB. prvku vektoru w (obdoba funkce take) 4 5 6 { w 9 8 7 NB. Výběr prvních čtyř prvků vektoru 4 {. w 1 2 3 4 NB. Vrácení vektoru BEZ jeho prvních NB. čtyř prvků (obdoba funkce drop) 4 }. w 9 8 7 6 11 12
Funkce pro výběr prvků mají svoje názvy:
Funkce | Forma | Jméno |
---|---|---|
{. | dyadická | take |
}. | dyadická | drop |
{. | monadická | head |
}. | monadická | behead |
{: | monadická | tail |
}: | monadická | curtail |
Funkce take vrátí prvních n prvků vektoru. V případě potřeby je vektor zprava doplněn nulami. Lze použít i záporný počet prvků:
i. 10 0 1 2 3 4 5 6 7 8 9 3 {. i. 10 0 1 2 30 {. i. 10 0 1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 _5 {. i. 10 5 6 7 8 9
Funkce drop vrátí vektor bez prvních n prvků. Lze použít i záporný počet prvků:
3 }. i. 10 3 4 5 6 7 8 9 _3 }. i. 10 0 1 2 3 4 5 6
Monadická funkce head vrátí první prvek vektoru:
{. i. 10 0
Funkce behead vrátí vektor bez prvního prvku:
}. i. 10 1 2 3 4 5 6 7 8 9
Monadická funkce tail vrátí poslední prvek vektoru:
{: i. 10 9
A konečně funkce curtail vrátí vektor bez posledního prvku:
}: i. 10 0 1 2 3 4 5 6 7 8
12. Konstrukce vektorů založená na generátorech (indexů)
Nyní si ve stručnosti ukážeme způsob použití takzvaných generátorů indexů představovaných primitivními funkcemi zapisovanými pomocí symbolů i. a i:. Jedná se o velmi užitečné a v praxi často využívané funkce, které odpovídají primitivní funkci ι, s níž jsme se již seznámili při popisu programovacího jazyka APL, ovšem s tím podstatným rozdílem, že první číslo generované funkcí i. má hodnotu nula, nikoli jedna, a poslední číslo má hodnotu n-1 a nikoli n (to souvisí s tím, že v programovacím jazyce J jsou položky vektorů číslovány–indexovány od nuly, podobně jako například v programovacích jazycích C, C++ či Java – viz též předchozí kapitolu věnovanou indexování). Povšimněte si taktéž, že se pomocí funkce i. dá vytvořit velké množství různých (nejenom) aritmetických řad, takže pro tyto účely není nutné používat programové smyčky. Navíc mohou být primitivní funkce i. a i: součástí nějakého složitějšího výrazu, čehož se při psaní aplikací poměrně často využívá, například při programové tvorbě matic (jednotková matice, trojúhelníková matice atd.).
NB. Základní použití generátoru indexů i. 10 0 1 2 3 4 5 6 7 8 9 NB. Poměrně snadno můžeme dosáhnout toho, NB. aby se počáteční hodnota generované NB. číselné řady "posunula" 20 + i. 10 20 21 22 23 24 25 26 27 28 29 NB. Řada začínající hodnotou -10 _10 + i. 10 _10 _9 _8 _7 _6 _5 _4 _3 _2 _1 NB. Posun a současně i změna "kroku" NB. při generování číselné řady 3 + 0.5 * i. 15 3 3.5 4 4.5 5 5.5 6 6.5 7 7.5 8 8.5 9 9.5 10 NB. Mocninná řada čísla 2 vytvořená NB. bez použití programové smyčky 2 ^ i. 10 1 2 4 8 16 32 64 128 256 512 NB. Výpočet prvních faktoriálu prvních deseti NB. přirozených čísel a nuly. NB. (připomeňme si, že funkce se vyhodnocují NB. zprava doleva, tj. nejdříve se vytvoří NB. vektor čísel, na jehož prvky je aplikována NB. primitivní funkce představující faktoriál) ! i. 10 1 1 2 6 24 120 720 5040 40320 362880 NB. Pomocí primitivní funkce i: lze vygenerovat NB. číselnou řadu symetrickou okolo nuly i: 10 _10 _9 _8 _7 _6 _5 _4 _3 _2 _1 0 1 2 3 4 5 6 7 8 9 10 NB. Změna kroku číselné řady 2* i: 10 _20 _18 _16 _14 _12 _10 _8 _6 _4 _2 0 2 4 6 8 10 12 14 16 18 20 NB. Opět výpočet mocnin čísla 2, tentokrát však NB. včetně záporných exponentů 2^ i: 5 0.03125 0.0625 0.125 0.25 0.5 1 2 4 8 16 32 NB. Vytvoření prázdného vektoru (jedna z nejčastěji NB. používaných možností tvorby prázdného vektoru) i. 0 NB. Ovšem pozor: v následujícím případě NB. se vytvoří vektor obsahující jeden prvek i: 0 0
Vektor s nulami se tvoří podobnou funkcí I.:
I.1 0 I.10 0 0 0 0 0 0 0 0 0 0
13. Matice
Práce s maticemi je v programovacím jazyku J stejně snadná jako práce s vektory. Základní funkci při práci s maticemi představuje funkce reshape zapisovaná pomocí symbolu $. Tato funkce má stejné vlastnosti jako funkce ρ zmiňovaná při popisu programovacího jazyka APL – v prvním parametru (zapisovaného nalevo od symbolu $) jsou uloženy rozměry matice, v parametru druhém (uváděném napravo od symbolu $) pak její jednotlivé prvky, typicky uložené ve vektoru nebo v jiné matici. Naopak monadická forma funkce $ vrací tvar pole (zde je symetrie dokonalá).
NB. Vytvoření nulové matice NB. o rozměrech 3x3 prvky 3 3 $ 0 0 0 0 0 0 0 0 0 0 NB. Vytvoření matice a současně NB. i nastavení hodnot jejích prvků 3 2 $ 1 2 3 4 5 6 1 2 3 4 5 6 NB. Použití generátoru indexů NB. při vytváření matice 3 4 $ i. 12 0 1 2 3 4 5 6 7 8 9 10 11 2 2 $ 0 1 2 3 0 1 2 3 2 3 $ 0 1 2 3 4 5 0 1 2 3 4 5 NB. Odlišný tvar, ovšem stejný obsah prvků 3 2 $ 0 1 2 3 4 5 0 1 2 3 4 5
Dvourozměrná nečtvercová matice:
2 3 $ i. 6 0 1 2 3 4 5
Trojrozměrná matice (brick):
2 3 4 $ i. 24 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Odlišný tvar čtyřrozměrné matice:
4 3 2 $ i. 24 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Zpětné získání tvaru pole pomocí monadické funkce $:
$ 4 3 2 $ i. 24 4 3 2
Konstrukce prázdného pole:
0 $ 10
Velmi důležitou „maticovou“ funkcí je funkce pro výpočet inverzní matice zapisovaná symbolem %.. S využitím této funkce lze například vypočítat soustavu n rovnic o n neznámých:
x + 2y -3z = 15 x + y + z = 12 2x - y - z = 0
Nejprve vytvoříme příslušnou matici a zapíšeme do ní koeficienty (multiplikativní konstanty) uvedené před neznámými:
NB. Prvky ležící na jednotlivých řádcích jsou NB. od sebe pro větší přehlednost odděleny NB. trojicí mezer. m =: 3 3 $ 1 2 _3 1 1 1 2 _1 _1 m 1 2 _3 1 1 1 2 _1 _1
S využitím funkce %.m lze snadno vypočítat inverzní matici:
%.m 0 0.333333 0.333333 0.2 0.333333 _0.266667 _0.2 0.333333 _0.0666667
Po vynásobení inverzní matice vektorem obsahujícím pravé strany rovnic dostaneme kýžený výsledek – hodnoty neznámých x, y a z:
15 12 0 %.m 4 7 1
14. Výběr prvků z matic
Pro výběr prvků z matic se používají stejné funkce, jako pro výběr prvků z vektorů. Pouze nesmíme zapomenout na to, že výběrem jedním indexem získáme z matice vektor.
Nejprve si zopakujeme způsob výběru prvků z vektoru:
Výběr prvků z vektoru s využitím indexu:
0 { 5 6 7 8 5
Index prvního prvku je roven nule:
1 { 5 6 7 8 6
Použití záporného indexu:
_1 { 5 6 7 8 8
Použití záporného indexu:
_2 { 5 6 7 8 7
Pozor na to, že funkce – není stejná jako znaménko _:
-1 { 5 6 7 8 _6
Výběr více prvků z vektoru:
0 2 3 { 5 6 7 8 5 7 8
Výběr více prvků z vektoru, kombinace se záporným indexem:
0 2 _1 { 5 6 7 8 5 7 8
Výběr více prvků z vektoru, kombinace se záporným indexem:
0 2 _2 { 5 6 7 8 5 7 7
A konečně výběry prvků z matice:
Konstrukce matice:
3 3 $ i. 9 0 1 2 3 4 5 6 7 8
Výběr celého řádku z matice:
1 { 3 3 $ i. 9 3 4 5
Záporný index při výběru:
_1 { 3 3 $ i. 9 6 7 8
Výběr řádků v opačném pořadí z matice:
2 1 0 { 3 3 $ i. 9 6 7 8 3 4 5 0 1 2
Zopakování vybraných řádků:
2 1 2 1 2 1 { 3 3 $ i. 9 6 7 8 3 4 5 6 7 8 3 4 5 6 7 8 3 4 5
Výběr konkrétního prvku z matice:
(< 1 ; 1) { 3 3 $ i. 9 4
Výběr podmatice:
(< 1 2 ; 1 2) { 3 3 $ i. 9 4 5 7 8
Výběr podmatice:
(< 0 1 ; 0 1) { 3 3 $ i. 9 0 1 3 4
Výběr sloupce z matice:
(< 0 1 2; 1) { 3 3 $ i. 9 1 4 7
15. Predikáty pracující s atomy, vektory i maticemi
Pro vzájemné porovnávání skalárních hodnot i hodnot prvků uložených ve vektorech, maticích i polích s vyššími dimenzemi se používají predikátové funkce nahrazující relační operátory známé z jiných programovacích jazyků (slovo operátor má v jazyce J odlišný význam). Návratovou hodnotou těchto funkcí je buď skalární hodnota 0 nebo 1, popř. vektor či matice obsahující pouze nuly a jedničky (nula samozřejmě značí nepravdu, jednička pravdu). Tyto funkce jsou popsány v následující tabulce. Povšimněte si především způsobu zápisu podmínky „menší nebo rovno“, „větší nebo rovno“ a „nerovnost“, protože se v nich používají znaky, které jsou oproti jiným programovacím jazykům odlišné (jenže co není v jazyce J odlišné, že?):
Symbol funkce | Forma funkce | Popis (význam) |
---|---|---|
< | dyadická | predikát „menší než“ |
<: | dyadická | predikát „menší nebo rovno“ |
> | dyadická | predikát „větší než“ |
>: | dyadická | predikát „větší nebo rovno“ |
= | dyadická | predikát rovnosti |
~: | dyadická | predikát nerovnosti |
e. | dyadická | predikát „obsahuje“ |
Použití výše uvedených funkcí při porovnávání skalárních hodnot je velmi snadné, o čemž se můžeme velmi snadno přesvědčit:
1 <: 2 1 1 >: 2 0 1 <: 1 1 1 ~: 2 1 1 ~: 1 0
Kromě vzájemného porovnání skalárních hodnot je možné porovnat všechny prvky vektoru (matice) se skalární hodnotou, popř. prvky dvou vektorů (matic) navzájem. V tomto případě je výsledkem porovnání též vektor či matice obsahující hodnoty 0 a 1 (tedy vlastně bitová maska):
NB. Porovnání všech prvků vektoru se skalární hodnotou 1 2 3 4 > 2 0 0 1 1 1 2 3 4 < 2 1 0 0 0 1 2 3 4 = 2 0 1 0 0 NB. Porovnání dvou vektorů, které v tomto NB. případě musí mít shodnou délku. 1 2 3 4 < 4 3 2 1 1 1 0 0
Poslední funkcí, kterou si v této kapitole popíšeme, je funkce testující existenci prvku (prvků) ve vektoru či matici. Jedná se o funkci označovanou symbolem e., jejímž prvním parametrem je skalární hodnota či vektor hodnot, které se hledají v datové struktuře předané ve druhém parametru této funkce. Návratovou hodnotou funkce e. je pravdivostní hodnota 0 nebo 1 udávající, zda se příslušná hledaná hodnota (první parametr) skutečně ve druhém parametru nachází. Pokud se současně vyhledává více hodnot, je návratovou hodnotou vektor nul a jedniček (tedy opět bitové pole), jak je to ostatně ukázáno v následujících demonstračních příkladech:
NB. Vytvoření vektoru čísel x=:1 2 3 4 5 0.3 _1 42 NB. Zjištění, zda vektor obsahuje hodnotu 10 10 e. x 0 NB. Zjištění, zda vektor obsahuje hodnotu 1 1 e. x 1 NB. Dtto pro hodnotu 2 2 e. x 1 NB. Vytvoření vektoru hledaných čísel y=:1 3 5 NB. Zjištění, zda vektor x obsahuje čísla 1, 3 a 5 y e. x 1 1 1 NB. Vyhledání více prvků bez použití NB. pomocného vektoru 1 10 100 1000 42 e. x 1 0 0 0 1 NB. Vektor, ve kterém je prováděno vyhledávání, NB. může být sestrojen funkcí i. 1 5 10 e. i. 10 1 1 0 NB. Vyhledání všech násobků dvou v řadě NB. obsahující násobky čísla 3. (2* i. 20) e. (3* i. 20) 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0
I.2=+/0=|/~ i.100 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
16. Různé způsoby spojení polí
V jazyku J existuje hned několik různých operací pro spojení dvou polí:
Symbol funkce | Forma funkce | Jméno |
---|---|---|
, | dyadická | append |
,. | dyadická | stitch |
,: | dyadická | laminate |
; | dyadická | link |
Tyto funkce budou ukázány na několika příkladech:
Spojení vektorů funkcí append:
1 2 3 , 4 5 6 1 2 3 4 5 6
Spojení matic funkcí append („pod sebe“):
(2 3 $ i. 6) , 2 3 $ i. 6 0 1 2 3 4 5 0 1 2 3 4 5
Matice nemusí mít stejný počet řádků:
(2 3 $ i. 6) , 3 3 $ i. 9 0 1 2 3 4 5 0 1 2 3 4 5 6 7 8
Připojení skaláru k vektoru s možným opakováním skalární hodnoty:
(i. 6) , 100 0 1 2 3 4 5 100
Připojení skaláru k matici s možným opakováním skalární hodnoty:
(2 3 $ i. 6) , 100 0 1 2 3 4 5 100 100 100
Odlišný tvar původní matice:
(3 2 $ i. 6) , 100 0 1 2 3 4 5 100 100
Zopakujme si použití funkce append pro dvě matice:
(2 3 $ i. 6) , 2 3 $ i. 6 0 1 2 3 4 5 0 1 2 3 4 5
Rozdílné chování funkce stitching pro dvě matice (spojení „za sebou“):
(2 3 $ i. 6) ,. 2 3 $ i. 6 0 1 2 0 1 2 3 4 5 3 4 5
Funkce stitching a kombinace matice a skalární hodnoty:
(2 3 $ i. 6) ,. 100 0 1 2 100 3 4 5 100
Funkce stitching a kombinace matice a skalární hodnoty:
(3 2 $ i. 6) ,. 100 0 1 100 2 3 100 4 5 100
Funkce laminating spojuje dvě matice ještě jiným způsobem – jako dva boxy:
(2 3 $ i. 6) ,: 2 3 $ i. 6 0 1 2 3 4 5 0 1 2 3 4 5
Výsledkem je vždy pole obsahující „kompatibilní“ prvky
(2 3 $ i. 6) ,: 3 2 $ i. 6 0 1 2 3 4 5 0 0 0 0 1 0 2 3 0 4 5 0
Další příklad kombinující matici a skalár – výsledkem je matice s dvojicí „kompatibilních“ prvků:
(2 3 $ i. 6) ,: 100 0 1 2 3 4 5 100 100 100 100 100 100
17. Pokročilejší operace nad poli
Popišme si ještě další operace, které lze s poli provádět:
Symbol funkce | Forma funkce | Jméno | |
---|---|---|---|
; | dyadická | linking | |
; | monadická | razing | |
, | monadická | ravelling | |
,. | monadická | itemizing |
Funkce linking slouží ke konstrukci seznamu boxů obsahujících jakékoli prvky:
(2 3 $ i. 6) ; 100 +-----+---+ |0 1 2|100| |3 4 5| | +-----+---+ (2 3 $ i. 6) ; 100 ; 'abcd' +-----+---+----+ |0 1 2|100|abcd| |3 4 5| | | +-----+---+----+ (2 3 $ i. 6) ; 100 ; 'abcd' ; (4 2 $ i. 8) +-----+---+----+---+ |0 1 2|100|abcd|0 1| |3 4 5| | |2 3| | | | |4 5| | | | |6 7| +-----+---+----+---+
Monadická funkce razing naopak rozdělí pole boxů na seznam prvků:
2 2 $ 1;2;3;4 +-+-+ |1|2| +-+-+ |3|4| +-+-+ ; 2 2 $ 1;2;3;4 1 2 3 4
Naproti tomu monadická funkce ravelling rozdělí pole boxů na seznam boxů:
, 2 2 $ 1;2;3;4 +-+-+-+-+ |1|2|3|4| +-+-+-+-+
A konečně funkce itemizing zvyšuje dimenzi pole o jedničku – zabalí prvky původního pole:
2 2 $ 1;2;3;4 +-+-+ |1|2| +-+-+ |3|4| +-+-+ ,. 2 2 $ 1;2;3;4 +-+-+ |1|2| +-+-+ |3|4| +-+-+ $ 2 2 $ 1;2;3;4 2 2 $ ,: 2 2 $ 1;2;3;4 1 2 2
18. Operátor / (reduce)
Programovací jazyk J však kromě poměrně rozsáhlé řady primitivních funkcí obsahuje, ostatně podobně jako již popsaný jazyk APL, několik operátorů, pomocí nichž jsou funkce aplikovány nikoli na jeden či dva parametry, ale postupně na celé vektory nebo matice. Díky operátorům (význam tohoto slova je odlišný od významu, který toto slovo má v jiných programovacích jazycích!) je možné eliminovat velké množství programových smyček a mnohdy tak například několikařádkovou proceduru zapsat pomocí jediného výrazu. Jedním z nejdůležitějších operátorů jazyka J je operátor /, který jsme si již v jednodušší podobě představili při popisu jazyka APL. Tento operátor, který se zapisuje za identifikátor primitivní či uživatelské funkce, postupně danou funkci aplikuje na první dva prvky argumentu, dále ji aplikuje na průběžný výsledek a třetí prvek atd., do doby, než jsou všechny prvky argumentu zpracovány (jinými slovy – daná dyadická funkce je jakoby zapsána mezi všechny prvky předané datové struktury, počet operací je roven n-1 v případě, že předaný vektor má počet prvků n):
NB. Součet všech čísel v řadě od 1 do 10. + / 1 2 3 4 5 6 7 8 9 10 55 NB. Vektor hodnot můžeme vytvořit NB. taktéž s využitím generátoru NB. indexů (viz předchozí části NB. seriálu). Ovšem pozor - indexy NB. se generují od 0, nikoli od 1. + / i. 10 45 NB. Zde se již vektor skutečně vygeneruje NB. tak, že jeho první prvek má NB. jedničkovou hodnotu. + / 1 + i. 10 55
Operátor / je však samozřejmě možné použít i v kombinaci s libovolnou dyadickou funkcí – může se jednat jak o aritmetické funkce, tak i o funkce logické (booleovské), porovnávací (relační), maticové aj. Následují příklady použití některých těchto funkcí; nejprve funkcí booleovských aplikovaných na vektory binárních hodnot:
NB. Použití booleovské funkce "or" NB. (logický součet) +./ 0 0 0 0 0 +./ 0 0 0 1 1 +./ 1 1 1 1 1 NB. Použití booleovské funkce "and" NB. (logický součin) *./ 0 0 0 0 0 *./ 0 0 0 1 0 *./ 1 1 1 1 1 NB. Použití booleovské funkce ekvivalence =/ 0 0 0 0 1 =/ 0 0 0 1 0 =/ 1 1 1 1 1 NB. Použití booleovské funkce "xor" NB. (nonekvivalence) ~:/ 0 0 0 0 0 ~:/ 0 0 0 1 1 ~:/ 1 1 1 1 0
Mezi další dyadické funkce, které lze zkombinovat s operátorem /, patří například funkce pro výpočet (zjištění) minima, maxima, součinu (zde korespondujících prvků vektorů) a mocniny o zvoleném základu:
NB. Výběr nejmenšího prvku z vektoru. <. / 5 4 67 2 1 42 3 7 6 1 NB. Výběr největšího prvku z vektoru. >. / 5 4 67 2 1 42 3 7 6 67 NB. Jeden ze způsobů výpočtu skalárního NB. součinu dvou vektorů. +/ (1 2 3) * (1 2 3) 14 NB. Skalární součin dvou na sebe kolmých NB. vektorů je vždy nulový. +/ (1 0 0) * (0 1 0) 0 NB. Výpočet prvních deseti mocnin NB. základu 2 (včetně 2^0). 2 ^ i. 10 1 2 4 8 16 32 64 128 256 512 NB. Suma prvních deseti mocnin NB. základu 2. Výsledek by měl NB. být roven: (2^(n+1))-1 + / 2 ^ i. 10 1023 NB. Výpočet známé úlohy se šachovnicí a semínky NB. kladenými na jednotlivá políčka šachovnice. 2 ^ i. 64 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1.04858e6 2.09715e6 4.1943e6 8.38861e6 1.67772e7 3.35544e7 6.71089e7 1.34218e8 2.68435e8 5.36871e8 1.07374e9 2.14748e9 4.29497e9 8.58993e9 1.71799e10 3.43597e10 6.87195... NB. Celkový počet všech semínek na šachovnici. + / 2 ^ i. 64 1.84467e19
Nyní si ukážeme příklad převzatý z elektronické knihy „J for C Programmers“, ve kterém je předvedeno, jakým způsobem je možné pomocí operátoru / nalézt největší prvek v matici (zmíněná elektronická kniha je obsažena přímo v instalaci vývojového prostředí programovacího jazyka J). Nejprve si uveďme odpovídající céčkový program, který nalezne prvek s maximální hodnotou v (dvourozměrné) matici x, jejíž rozměry jsou uloženy v konstantách xsize0 a xsize1:
int i, j; float maxval = x[0][0]; for (i = 0; i&jt;=xsize0; ++i) { for (j = 0; j<=xsize1; ++j) { if (x[i][j] > maxval) { maxval = x[i][j]; } } }
V programovacím jazyku J lze maximální prvek v matici nalézt dvojí aplikací operátoru / zkombinovaného s funkcí >. (ta provádí výběr větší hodnoty z dvojice předaných parametrů, tj. jak skalárních hodnot, tak i vektorů popř. matic). První aplikace operátoru / slouží k výběru těch prvků na jednotlivých řádcích matice, které mají největší hodnotu, druhá aplikace pak již z tohoto mezivýsledku (tj. vektoru) vybere přímo maximální hodnotu, takže se celá sekvence zanořených smyček a podmíněného příkazu v programovacím jazyku J zmenší na jediný výraz. Ve skutečnosti je však možné podmíněný příkaz eliminovat i v céčkovém programu, stačí ho nahradit například voláním makra MAX, což však již není možné v příkladu uvedeném níže. Navíc si nikde nemusíme pamatovat rozměry pole (matice), neboť ty lze kdykoli za běhu programu zjistit:
NB. Nejprve si vytvoříme matici NB. s testovacími daty. matice =: 3 3 $ _4 + i.9 matice _4 _3 _2 _1 0 1 2 3 4 NB. Nalezneme největší prvek v matici NB. dvojím použitím operátoru / (<./) <./ matice 4
Druhý demonstrační příklad je již poněkud složitější – nalezení sloupce, ve kterém se nachází prvek s maximální hodnotou –, ale při použití programovacího jazyka J se opět obejdeme bez nutnosti použití programových smyček a podmínek. Sloupec, ve kterém se prvek s největší hodnotou v matici nachází, lze zjistit s využitím dyadické funkce i. (index of), která vrací index prvního prvku obsahujícího danou hodnotu (prvním parametrem této funkce je většinou vektor, druhým parametrem hledaná hodnota). Povšimněte si především použití závorek, které eliminují nutnost dvojího použití mezivýsledku >./ matice v zapisovaném výrazu:
(i. >./) >./ matice
Céčkovský ekvivalent je stále mnohonásobně delší kvůli nutnosti použití programových smyček a podmínek:
int i, j, maxcol = 0; float maxval = x[0][0]; for (i = 0; i&jt;=xsize0; ++i) { for (j = 0; j<=xsize1; ++j) { if (x[i][j] > maxval) { maxval = x[i][j]; maxcol = j } } }
19. Dyadická forma operátoru /
Při popisu programovacího jazyka APL jsme se mj. zmínili i o operátoru „outer product“ zapisovaného dvojicí symbolů ◦. (kolečko+tečka) za nimiž následovalo jméno či symbol nějaké primitivní funkce popř. identifikátor uživatelské dyadické funkce. Tento operátor je založen na principu aplikace zvolené dyadické funkce na dvojici vektorů x a y, přičemž vybraná funkce je aplikována na všechny možné kombinace složek prvního a druhého vektoru. Výsledkem této operace je matice Z obsahující v prvku zij návratovou hodnotu funkce aplikované na prvky xi a yj. Význam tohoto operátoru jsme si vysvětlili na příkladu, ve kterém se vytvořila a následně vypsala část tabulky malé násobilky (pozor – následující úryvek kódu je zapsaný v syntaxi odpovídající programovacímu jazyku APL, nikoli J):
1 2 3 4 5 ◦.× 1 2 3 4 5 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 5 10 15 20 25
Podobně jsme v APL postupovali při výpočtu prvočísel:
(~R∊R∘.×R)/R←1↓⍳x
Programovací jazyk J sice neobsahuje specializovaný operátor typu ◦., ale ani to vlastně není zapotřebí, protože rozšiřuje možnosti operátoru / popsaného v předchozí kapitole. Zatímco v APL byl operátor / (reduce) pouze monadický (což znamená, že ho bylo možné použít s parametrem zapisovaným napravo od identifikátoru funkce), je možné v programovacím jazyce J využít i jeho dyadickou formu, při níž se první parametr (vektor) zapisuje nalevo od operátoru+funkce a druhý parametr (většinou taktéž vektor) napravo. Výsledek aplikace „dyadického“ operátoru / (nazývaného poněkud neobvykle table) je v tomto případě stejný, jako při použití operátoru ◦. v programovacím jazyce APL (u některých funkcí použitých spolu s tímto operátorem dochází k několika malým změnám chování, které však nejsou v naprosté většině případů podstatné).
Výše uvedený příklad by se tedy dal přepsat do programovacího jazyka J dvěma způsoby: přímým zápisem dvojic vektorů, na něž by se aplikoval operátor „/“ v kombinaci s funkcí násobení, nebo využitím generátoru indexů pro zkonstruování obou vektorů. Ve druhém případě opět nesmíme zapomenout na to, že se indexy generují od nuly a nikoli od jedničky:
NB. Tabulka malé násobilky vzniklá NB. aplikací operátoru "outer product" NB. na dvojici explicitně zapsaných vektorů 1 2 3 4 5 */ 1 2 3 4 5 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 5 10 15 20 25 NB. Použití generátoru indexů pro výpočet NB. tabulky malé násobilky. (1+i.5) */ (1+i.5) 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 5 10 15 20 25
Použít lze – i když nepříliš idiomaticky – funkci drop:
(}. i.6) */ (}. i.6) 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 5 10 15 20 25
Popř. bez nadbytečných mezer a závorek:
(}.i.6)*/}.i.6 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 5 10 15 20 25
S využitím operátoru outer product je možné zkonstruovat velké množství matic se speciálními vlastnostmi; tyto matice mohou mít samozřejmě téměř libovolnou velikost. Jedná se například o jednotkové matice (obsahují jedničky v diagonále, ostatní prvky jsou nulové), trojúhelníkové matice různého typu (jedničky se nachází pod či nad diagonálou, ostatní prvky jsou nulové) atd. Tvorba těchto matic je ilustrována na následujících příkladech, ve kterých se často používají porovnávací (relační) funkce:
NB. Pomocný vektor obsahující číselnou NB. řadu 0 1 2 3 4 5 vect =. i. 5 NB. Matice obsahující prvky se shodnými NB. hodnotami na vedlejších diagonálách. vect +/ vect 0 1 2 3 4 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 NB. Jednotková matice. vect =/ vect 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 NB. Inverze všech prvků jednotkové NB. matice. vect ~:/ vect 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 0 NB. Rotace jednotkové matice |. vect =/ vect 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 NB. Matice obsahující NAD hlavní NB. diagonálou jednotkové prvky. vect </ vect 0 1 1 1 1 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 NB. Matice obsahující NAD hlavní NB. diagonálou (včetně diagonály) NB. jednotkové prvky. vect </ vect 1 1 1 1 1 0 1 1 1 1 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 NB. Matice obsahující POD hlavní NB. diagonálou (včetně diagonály) NB. jednotkové prvky. vect >:/ vect 1 0 0 0 0 1 1 0 0 0 1 1 1 0 0 1 1 1 1 0 1 1 1 1 1 NB. Na hlavní diagonále je NB. číselná posloupnost začínající NB. nulou, ostatní prvky jsou nulové vect * vect =/ vect 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 0 3 0 0 0 0 0 4 NB. Na hlavní diagonále je NB. číselná posloupnost začínající NB. jedničkou, ostatní prvky jsou nulové (vect+1) * vect =/ vect 1 0 0 0 0 0 2 0 0 0 0 0 3 0 0 0 0 0 4 0 0 0 0 0 5 NB. Rotace předchozí matice |. (vect+1) * vect =/ vect 0 0 0 0 5 0 0 0 4 0 0 0 3 0 0 0 2 0 0 0 1 0 0 0 0
Příklad použití operátoru / s nekomutativní funkcí:
(i.5) ^/ (i.5) 1 0 0 0 0 1 1 1 1 1 1 2 4 8 16 1 3 9 27 81 1 4 16 64 256
Kdo dává přednost transponované tabulce (mocniny rostou po jednotlivých řádcích, základ po sloupcích), může použít funkci |: aplikovanou na vypočtenou matici. I v tomto případě totiž platí, že jsou funkce vyčíslovány zprava doleva, nejdříve je tedy vypočtena matice 5×5 hodnot, která je až následně transponována:
|: (i.5) ^/ (i.5) 1 1 1 1 1 0 1 2 3 4 0 1 4 9 16 0 1 8 27 64 0 1 16 81 256 NB. Kratší zápis pro ty, co šetří NB. každý stisk klávesy :-) |:(i.5)^/i.5 1 1 1 1 1 0 1 2 3 4 0 1 4 9 16 0 1 8 27 64 0 1 16 81 256
20. Odkazy na Internetu
- Jazyky umožňující operace s poli aneb rozsáhlý svět „array programmingu“
https://www.root.cz/clanky/jazyky-umoznujici-operace-s-poli-aneb-rozsahly-svet-bdquo-array-programmingu-ldquo/ - Programovací technika nazvaná tacit programming
https://www.root.cz/clanky/programovaci-technika-nazvana-tacit-programming/ - Oslava 55 let od vzniku první implementace jazyka APL
https://www.root.cz/clanky/oslava-55-let-od-vzniku-prvni-implementace-programovaciho-jazyka-apl/ - NuVoc
https://code.jsoftware.com/wiki/NuVoc - J (programming language) [Wikipedia]
https://en.wikipedia.org/wiki/J_%28programming_language%29 - J – Absolutely Essential Terms
https://code.jsoftware.com/wiki/Vocabulary/AET - J – Atoms and Arrays
https://code.jsoftware.com/wiki/Vocabulary/Nouns#Atom - Why J
https://www.jsoftware.com/help/primer/why_j.htm - What is an Array?
https://vector.org.uk/what-is-an-array/ - Comments
http://www.gavilan.edu/csis/languages/comments.html - Vector (Wolfram MathWorld)
https://mathworld.wolfram.com/Vector.html - n-Tuple (Wolfram MathWorld)
https://mathworld.wolfram.com/n-Tuple.html - n-Vector (Wolfram MathWorld)
https://mathworld.wolfram.com/n-Vector.html - Matrix (Wolfram MathWorld)
https://mathworld.wolfram.com/Matrix.html - Array (Wolfram MathWorld)
https://mathworld.wolfram.com/Array.html - ND Arrays (Tensors) in different languages
https://www.youtube.com/watch?v=WbpbEilgQBc - Extending APL to Infinity\
https://www.jsoftware.com/papers/eem/infinity.htm - Vector Library (R7RS-compatible)
https://srfi.schemers.org/srfi-133/srfi-133.html - Vectors (pro Gauche)
https://practical-scheme.net/gauche/man/gauche-refe/Vectors.html - Kawa: Compiling Scheme to Java
https://www.mit.edu/afs.new/sipb/project/kawa/doc/kawa-tour.html - Kawa in Languages shootout
http://per.bothner.com/blog/2010/Kawa-in-shootout/ - Kawa 2.0 Supports Scheme R7RS
https://developers.slashdot.org/story/14/12/13/2259225/kawa-20-supports-scheme-r7rs/ - Kawa — fast scripting on the Java platform
https://lwn.net/Articles/623349/ - Incanter is a Clojure-based, R-like platform for statistical computing and graphics.
http://incanter.org/ - Evolution of incanter (Gource Visualization)
https://www.youtube.com/watch?v=TVfL5nPELr4 - Questions tagged [incanter] (na Stack Overflow)
https://stackoverflow.com/questions/tagged/incanter?sort=active - Data Sorcery with Clojure
https://data-sorcery.org/contents/ - Back to the Future: Lisp as a Base for a Statistical Computing System
https://rd.springer.com/chapter/10.1007/978–3–7908–2084–3_2 - Incanter Cheat Sheet
http://incanter.org/docs/incanter-cheat-sheet.pdf - Back to the Future: Lisp as a Base for a Statistical Computing System (celá verze článku)
https://www.researchgate.net/publication/227019917_Back_to_the_Future_Lisp_as_a_Base_for_a_Statistical_Computing_System - BQN: finally, an APL for your flying saucer
https://mlochbaum.github.io/BQN/ - Is BQN stable?
https://mlochbaum.github.io/BQN/commentary/stability.html - Specification: BQN system-provided values
https://mlochbaum.github.io/BQN/spec/system.html - Tutorial: BQN expressions
https://mlochbaum.github.io/BQN/tutorial/expression.html - BQN primitives
https://mlochbaum.github.io/BQN/doc/primitive.html - Function trains
https://mlochbaum.github.io/BQN/doc/train.html - BQN community links
https://mlochbaum.github.io/BQN/community/index.html - BQN UV
https://observablehq.com/@lsh/bqn-uv - APL Wiki
https://aplwiki.com/wiki/ - The Array Cast
https://www.arraycast.com/episodes/episode-03-what-is-an-array - EnthusiastiCon 2019 – An Introduction to APL
https://www.youtube.com/watch?v=UltnvW83_CQ - Dyalog
https://www.dyalog.com/ - Try APL!
https://tryapl.org/ - Lisp-Stat Information
http://homepage.cs.uiowa.edu/~luke/xls/xlsinfo/ - Sample Plots in Incanter
https://github.com/incanter/incanter/wiki/Sample-Plots-in-Incanter#line - vectorz-clj
https://github.com/mikera/vectorz-clj - vectorz – Examples
https://github.com/mikera/vectorz-clj/wiki/Examples - Basic Vector and Matrix Operations in Julia: Quick Reference and Examples
https://queirozf.com/entries/basic-vector-and-matrix-operations-in-julia-quick-reference-and-examples - Vectors and matrices in Julia
https://fncbook.github.io/v1.0/linsys/demos/matrices-julia.html - Array vs Matrix in R Programming
https://www.geeksforgeeks.org/array-vs-matrix-in-r-programming/ - Concurrency (computer science)
https://en.wikipedia.org/wiki/Category:Concurrency_%28computer_science%29 - Koprogram
https://cs.wikipedia.org/wiki/Koprogram - Coroutine
https://en.wikipedia.org/wiki/Coroutine - Coroutines in C
http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html - S-expression (Wikipedia)
https://en.wikipedia.org/wiki/S-expression - S-Expressions (Rosetta Code)
http://rosettacode.org/wiki/S-Expressions - Introducing Julia/Metaprogramming
https://en.wikibooks.org/wiki/Introducing_Julia/Metaprogramming - Tutorial for the Common Lisp Loop Macro
http://www.ai.sri.com/pkarp/loop.html - Clojure Macro Tutorial (Part I, Getting the Compiler to Write Your Code For You)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-i-getting.html - Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html - Clojure Macro Tutorial (Part III: Syntax Quote)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html - Clojure Macros and Metaprogramming
http://clojure-doc.org/articles/language/macros.html - Fatvat – Exploring functional programming: Clojure Macros
http://www.fatvat.co.uk/2009/02/clojure-macros.html - CS 2101 Parallel Computing with Julia
https://www.coursehero.com/file/11508091/CS-2101-Parallel-Computing-with-Julia/ - Julia By Example
https://samuelcolvin.github.io/JuliaByExample/ - Array Programming
https://en.wikipedia.org/wiki/Array_programming - Discovering Array Languages
http://archive.vector.org.uk/art10008110 - no stinking loops – Kalothi
http://www.nsl.com/ - Vector (obsahuje odkazy na články, knihy a blogy o programovacích jazycích APL, J a K)
http://www.vector.org.uk/ - APL Interpreters
http://www.vector.org.uk/?area=interpreters - APL_(programming_language
http://en.wikipedia.org/wiki/APL_(programming_language - APL FAQ
http://www.faqs.org/faqs/apl-faq/ - APL FAQ (nejnovější verze)
http://home.earthlink.net/~swsirlin/apl.faq.html - A+
http://www.aplusdev.org/ - APLX
http://www.microapl.co.uk/ - FreeAPL
http://www.pyr.fi/apl/index.htm - Learning J (Roger Stokes)
http://www.jsoftware.com/help/learning/contents.htm - J: a modern, high-level, general-purpose, high-performance programming language
http://www.jsoftware.com/ - K, Kdb: an APL derivative for Solaris, Linux, Windows
http://www.kx.com - openAPL (GPL)
http://sourceforge.net/projects/openapl - Parrot APL (GPL)
http://www.parrotcode.org/ - Learning J (Roger Stokes)
http://www.jsoftware.com/help/learning/contents.htm - Rosetta Code
http://rosettacode.org/wiki/Main_Page - Why APL
http://www.acm.org/sigapl/whyapl.htm - Introducing Julia/Functions
https://en.wikibooks.org/wiki/Introducing_Julia/Functions - Functions (Julia documentation)
https://docs.julialang.org/en/v1/manual/functions/ - Evaluate binomial coefficients
http://rosettacode.org/wiki/Evaluate_binomial_coefficients - Ackermann function
http://rosettacode.org/wiki/Ackermann_function - Julia (front page)
http://julialang.org/ - Julia – dokumentace
http://docs.julialang.org/ - Julia – repositář na GitHubu
https://github.com/JuliaLang/julia - Julia (programming language)
https://en.wikipedia.org/wiki/Julia_%28programming_language%29 - IJulia
https://github.com/JuliaLang/IJulia.jl - Introducing Julia
https://en.wikibooks.org/wiki/Introducing_Julia - Julia: the REPL
https://en.wikibooks.org/wiki/Introducing_Julia/The_REPL - Month of Julia
https://github.com/DataWookie/MonthOfJulia - Learn X in Y minutes (where X=Julia)
https://learnxinyminutes.com/docs/julia/ - New Julia language seeks to be the C for scientists
http://www.infoworld.com/article/2616709/application-development/new-julia-language-seeks-to-be-the-c-for-scientists.html - Julia: A Fast Dynamic Language for Technical Computing
http://karpinski.org/publications/2012/julia-a-fast-dynamic-language - The LLVM Compiler Infrastructure
http://llvm.org/ - Julia: benchmarks
http://julialang.org/benchmarks/ - Type system
https://en.wikipedia.org/wiki/Type_system - Half-precision floating-point format
https://en.wikipedia.org/wiki/Half-precision_floating-point_format - Dartmouth BASIC
https://en.wikipedia.org/wiki/Dartmouth_BASIC - BASIC 4th Edition
http://www.bitsavers.org/pdf/dartmouth/BASIC_4th_Edition_Jan68.pdf - VECTRAN
https://encyclopedia2.thefreedictionary.com/VECTRAN - Comparison of programming languages (array)
https://en.wikipedia.org/wiki/Comparison_of_programming_languages_(array) - BASIC at 50
https://www.dartmouth.edu/basicfifty/commands.html - BBC Basic – arrays
http://www.riscos.com/support/developers/bbcbasic/part2/arrays.html - Datová struktura
https://cs.wikipedia.org/wiki/Datov%C3%A1_struktura - SIMD instrukce využívané v moderních mikroprocesorech řady x86
https://www.root.cz/clanky/simd-instrukce-vyuzivane-v-modernich-mikroprocesorech-rady-x86/ - SIMD instrukce v moderních mikroprocesorech řady x86 (2.část: SSE)
https://www.root.cz/clanky/simd-instrukce-v-modernich-mikroprocesorech-rady-x86–2-cast-sse/ - SIMD instrukce v moderních mikroprocesorech řady x86 (3.část: SSE2)
https://www.root.cz/clanky/simd-instrukce-v-modernich-mikroprocesorech-rady-x86–3-cast-sse2/