programovací jazyk From Wikipedia, the free encyclopedia
Wolfram Language či jen Wolfram (dále jen WL; dříve též Mathematica, pro podrobnosti viz oddíl "Pojmenování") je univerzální vyšší[4] programovací jazyk vyvíjený společností Wolfram Research. Ústředním konceptem jazyka jsou výrazy — výrazem jsou reprezentovány i takové části jazyka jako podmíněný příkaz či cykly, jež jsou obvykle v jiných jazycích jako C či Python vyjadřovány pomocí příkazů. Kód tak má přirozenou stromovou strukturu složenou z vnořených výrazů, jež lze přitom upravovat podobně jako data. Dalším ústředním konceptem jazyka jsou vzory (angl. patterns), které umožňují pružné vyhledávání a nahrazování částí kódu či dat pomocí nahrazovacích pravidel. WL je interpretovaný deklarativní jazyk[4], jenž umožňuje symbolické, funkcionální i procedurální programování s velkou mírou abstrakce[4].
Paradigma | multiparadigmatický |
---|---|
Vznik | 1988 |
Autor | Stephen Wolfram |
Vývojář | Wolfram Research |
První vydání | 1988 |
Poslední verze | 13.1.0 (2022) |
Typová kontrola | dynamická |
Hlavní implementace | Mathematica, Mathics, Expreduce |
Ovlivněn jazyky | Lisp[1], APL[1], SMP[1], C |
Ovlivnil jazyky | Jupyter[2][3] |
OS | multiplatformní |
Licence | proprietární licence |
Web | wolfram.com/language, wolframlanguage.org |
Přípona souboru | .nb, .wl, .wls; dříve též .cdf, .m |
WL obsahuje množství funkcí z různých oblastí matematiky, statistiky, zpracování dat či strojového učení. Jazyk umožňuje tvorbu 2D i 3D grafiky, manipulaci se zvukem či videem, tvorbu interaktivních uživatelských rozhraní, tvorbu webových rozhraní apod. Integrální součástí jazyka jsou funkce pro přímý přístup k datům různého druhu uloženým na vzdálených serverech[pozn. 1]. Podporovány jsou symbolické i numerické výpočty, paralelní výpočty i napojení na jiné jazyky jako Java, Python či Julia[5]. Kód ve WL lze pro zvýšení efektivity zkompilovat do jazyka C či do nízkoúrovňové reprezentace LLVM.
V současnosti jedinou plnohodnotnou implementací jazyka WL je systém Mathematica[pozn. 2]. WL samotný je vyvíjený v jazycích C a Java jako multiplatformní jazyk[4] běžící na systémech Windows, macOS a Linux. Rozšíření jazyka WL mezi uživateli je spíše omezené. Podle žebříčku TIOBE[7] se jazyk WL nedostává do první stovky nejpoužívanějších jazyků. Žebříček IEEE Spectrum k roku 2022[8] vypisuje 57 nejoblíbenějších jazyků, přičemž WL mezi nimi nefiguruje. Podobně není WL v seznamu nejoblíbenějších jazyků na projektu RedMonk[9] a v žebříčku PYPL index[10] není WL ani zahrnut do seznamu sledovaných jazyků.
Firma Wolfram Research od roku 1988 vyvíjí program Mathematica, který zpočátku uživateli umožňoval provádět různé matematické operace v závislosti na zadaných příkazech. Samotné jméno "Mathematica" bylo navrženo Stevem Jobsem[11] a tento produkt byl inzerován sloganem "A System for Doing Mathematics by Computer"[12], to jest "systém pro dělání matematiky počítačem". S postupem času se sada příkazů rozšiřovala, čímž vzniknul programovací jazyk s funkčností přesahující rámec matematiky, přičemž se pro tento jazyk též ustálilo pojmenování "Mathematica". Tento název tak označoval jak konkrétní softwarový produkt, tak programovací jazyk, který tento produkt implementuje.
S verzí 10.0.0 došlo k oddělení těchto dvou konceptů[13] spolu s přejmenováním některých produktů společnosti a změnou jejich inzerování. Jméno "Mathematica" tak označuje vývojové prostředí určené pro tvorbu kódu v jazyce, jež nese jméno "Wolfram language", to jest "jazyk Wolfram". Zavedené změny nicméně nejsou zcela dobře vymezeny, což vedlo k rozporuplnému přijetí mezi stávajícími uživateli a zmatení v užívaném názvosloví[14][15][16][17]. Na oficiálních stránkách[1] je rozdíl mezi Wolfram language a Mathematicou popsán slovy: "V prvním přiblížení, Wolfram Language = Mathematica + Wolfram|Alpha + Cloud + víc. Je kompatibilní s Mathematicou, ale je jejím podstatným rozvinutím, včetně znalostí, použití a mnoha nových myšlenek,"[pozn. 3] kde Wolfram|Alpha je služba poskytovaná firmou Wolfram Research založená zčásti na zpracování přirozeného jazyka a slovem Cloud jsou myšleny cloudové služby a s tím související funkce nabízené téže firmou.
1988 | 1.0 (červen) |
---|---|
1989 | 1.2 (srpen) |
1990 | |
1991 | 2.0 (leden) |
1992 | 2.1 (červen) |
1993 | 2.2 (červen) |
1994–1995 | |
1996 | 3.0 (září) |
1997–1998 | |
1999 | 4.0 (květen) |
2000 | 4.1 (listopad) |
2001 | |
2002 | 4.2 (červen) |
2003 | 5.0 (červen) |
2004 | 5.1 (listopad) |
2005 | 5.2 (červenec) |
2006 | |
2007 | 6.0 (květen) |
6.0.1 (červenec) | |
2008 | 6.0.2 (únor) |
6.0.3 (červen) | |
7.0 (listopad) | |
2009 | 7.0.1 (březen) |
2010 | 8.0 (listopad) |
2011 | 8.0.1 (březen) |
8.0.4 (říjen) | |
2012 | 9.0.0 (listopad) |
2013 | 9.0.1 (leden) |
2014 | 10.0.0 (červenec) |
10.0.1 (září) | |
10.0.2 (prosinec) | |
2015 | 10.1 (březen) |
10.2 (červenec) | |
10.3 (říjen) | |
10.3.1 (prosinec) | |
2016 | 10.4 (březen) |
10.4.1 (duben) | |
11 (srpen) | |
11.0.1 (září) | |
2017 | 11.1 (březen) |
11.1.1 (duben) | |
11.2 (září) | |
2018 | 11.3 (březen) |
2019 | 12 (duben) |
2020 | 12.1 (březen) |
12.1.1 (červen) | |
12.2 (prosinec) | |
2021 | 12.3 (květen) |
12.3.1 (červenec) | |
13 (prosinec) | |
2022 | 13.0.1 (únor) |
13.1 (červen) |
WL je symbolický jazyk, kde je s kódem nakládáno stejným způsobem jako s daty — přístup známý jako homoikonicita[4], který se uplatňuje i v jiných jazycích jako třeba Lisp. Kniha "Mathematica programming — an advanced introduction"[19] uvádí tři základní principy, na nichž je WL vybudován. Prvním z nich jsou symbolické výrazy jako stavební blok jazyka. Druhým jsou vzory[4] a jejich použití pro úpravu výrazů s pomocí nahrazovacích pravidel. Třetím principem je pak způsob, jakým jsou výrazy a jejich úpravy vyhodnocovány. Každý princip je podrobněji představen v samostatné podsekci níže. WL je interpretovaný jazyk využívající JIT kompilace[4]. Ze symbolického charakteru jazyka plyne jeho dynamické typování[1] a funkcionální přístup k úpravě kódu[4]. Ačkoli objektově orientované programování není ve WL přímo podporováno, existují různé způsoby jak ho emulovat[pozn. 4].
Výpočetní systém, na kterém běží WL, se skládá ze dvou hlavních částí — výpočetního jádra (angl. kernel) a front endu (angl. front end). Výpočetní jádro se stará o samotné výpočty, zatímco front end slouží k interakci s uživatelem. Vkládání vstupů uživatelem a jejich formátování stejně jako pokročilé funkce vykreslování výstupů včetně matematické sazby, grafiky či přehrávání zvuku má na starosti front end. S tím je svázán i fakt, že mnoho funkcí WL bez front endu nefunguje. Jádro komunikuje s front endem pomocí protokolu WSTP (Wolfram Symbolic Transfer Protocol[25], dříve známý pod jménem MathLink). Tento protokol lze použít i pro komunikaci mezi jádrem a externí aplikací vyvinutou v jiném jazyce, přičemž oficiálně jsou takto podporována mimo jiné propojení s jazyky Java (knihovna J/Link[26]), R (knihovna RLink[27]) či s .NET frameworkem (knihovna .NET/Link[28]).
Při vývoji WL se klade důraz na jeho zpětnou kompatibilitu[4]. V minulosti byly hlavní verze WL vydávány zhruba jednou za tři roky, viz přehled vpravo.
Každý objekt ve WL, to jest kód, data, obrázky, dokumenty, rozhraní, programy atd., je představován symbolickým výrazem (angl. expression)[4][29]. Výrazy mohou být dvojího druhu, a to buď atomární anebo normální. Atomárních výrazů je omezené množství, jedná se (především) o čísla, řetězce a symboly[pozn. 5]. Všechno ostatní jsou normální výrazy, které mají obecnou strukturu následujícího tvaru:
hlava[arg1, arg2, ...]
kde hlava
je hlavička výrazu, jež samotná je buď symbol anebo normální výraz, a kde dále objekty arg1
, arg2
, atd. tvoří tělo výrazu a samy mohou být buď atomárními či normálními výrazy. Tímto přirozeně vzniká stromová struktura výrazů[4][30] a následně i celého kódu, viz obrázek vlevo. S touto strukturou lze nakládat jako s daty — lze například přistupovat k jejím částem pomocí indexů[pozn. 6], podobně jako lze přistupovat k prvkům vícerozměrného pole. Příkladem normálního výrazu je i samotné volání funkce. Například Sin[x]
je nejen volání funkce sinus na proměnnou x
, ale současně i výraz s hlavičkou Sin
a tělem obsahujícím argument x
. Každý konstrukt má ve WL reprezentaci výše uvedeného tvaru, která se nazývá FullForm
, takže například součet dvou čísel 1 + 2
je interně reprezentován jako Plus[1,2]
, to jest výraz s hlavičkou Plus
. Výrazy jsou ve WL imutabilní struktury, kde jen vyhrazené funkce mohou výraz měnit "in place"[4].
Vzory (angl. patterns)[31] jsou svým způsobem šablony, kterým může vyhovovat současně vícero různých výrazů. Tyto šablony lze použít pro vyhledání nějaké části výrazu, či pro zkontrolování, zda je daný výraz správného tvaru. Tak například výraz Sin[x]
je sice odlišný od výrazu Sin[y]
, oba bychom ale mohli popsat slovy "funkce sinus volaná na nějakou proměnnou". Takovýto krátký popis lze formalizovat pomocí vzoru ve tvaru Sin[_]
, kde podtržítko _
je znak představující "cokoliv". Tyto a podobné vzory lze uplatnit při definicích funkcí. Konstantní funkci konst
, která na jakýkoliv vstup vrátí číslo 42, lze definovat způsobem: konst[_] = 42
. Pokud je nutno se na vstup odkazovat jako na proměnnou, lze mu dát jméno. Například x_
označuje "cokoliv, čemuž jsme dali jméno x". Funkci, jež má pro jakoukoliv vstupní hodnotu vrátit její druhou mocninu, tak lze definovat následovně:
f[x_] := x^2
Ve výše uvedeném vzoru přitom není vůbec řečeno, že vstupní hodnotou musí být číslo — lze použít skutečně cokoliv:
{f[2], f["retez"], f[Sin]} (* vrátí: {4, "retez"^2, Sin^2} *)
Ačkoliv to třeba nemusí dávat smysl, funkce f
aplikovala druhou mocninu i na řetězec "retez" a identifikátor funkce Sin
. Tímto způsobem WL implementuje polymorfizmus[4].
Vzory mohou mít velmi složitou strukturu. Způsob, jakým WL určí, zda daný výraz vyhovuje zadanému vzoru, se v angličtině nazývá pattern matching. Úpravy výrazů pak probíhají pomocí nahrazovacích pravidel (angl. (transformation) rules)[32] tvaru vzor->vyraz
, které danou část výrazu vyhovující vzoru vzor
nahradí novým (pod)výrazem tvaru vyraz
. Výše uvedenou funkci f
lze vyjádřit jako nahrazovací pravidlo x_ -> x^2
[pozn. 7]. Aplikace pravidla se zapisuje jako /.
a tak výše uvedený kód lze přepsat do tvaru:
{2 /. x_ -> x^2, "retez" /. x_ -> x^2, Sin /. x_ -> x^2} (* vrátí: {4, "retez"^2, Sin^2} *)
Ve skutečnosti je jakákoliv funkce vyjádřena interně jako sada odpovídajících nahrazovacích pravidel.[33][pozn. 8]
Výpočetní jádro WL si udržuje interní databázi globálních nahrazovacích pravidel, v nichž jsou obsaženy jak definice vestavěných i uživatelem definovaných funkcí, tak přiřazení do proměnných. Jakmile je výraz ve WL odeslán do výpočetního jádra, prochází jádro tuto databázi a zjišťuje, zda levé strany některých pravidel vyhovují zadanému výrazu či některé jeho části. Je-li takové pravidlo nalezeno, je na výraz aplikováno, čímž dojde k jeho změně. Tato změna odstartuje další prohledávání téže databáze, kde jsou nyní hledána pravidla vyhovující novému tvaru výrazu. Tento proces probíhá tak dlouho, dokud už nelze výraz žádným z existujících pravidel upravit. Tento výsledný výraz je následně vrácen uživateli[34][35].
Pořadí, ve kterém jsou výrazy takto vyhodnocovány, lze nicméně upravovat, a to buď pomocí funkcí Evaluate
, Unevaluated
, Hold
apod., anebo pomocí atributů funkcí HoldAll
, HoldFirst
, atd.[36] Máme-li například funkci, jež má vrátit hlavičku výrazu, na který je volána, můžeme obdržet neočekávané výsledky:
f[h_[x_]] := h; (* definice *)
f[hlava[telo]] (* vrátí: hlava *)
f[Sin[x]] (* vrátí: Sin *)
f[Sin[Pi/2]] (* vrátí: f[1] *)
Ačkoli bychom chtěli, aby i v posledním řádku vrátila funkce symbol Sin
, obdržíme výsledek f[1]
. Důvod je ten, že se nejprve vyhodnotil výraz Sin[Pi/2]
na hodnotu 1 a teprve tato hodnota byla vložena do funkce f
. Protože funkce f
ale nemá přiřazenu definici pro případ argumentů, které nemají tvar normálního výrazu, zůstává funkce nevyhodnocena, čímž obdržíme f[1]
. Toto chování lze napravit tak, že argument zabalíme do výrazu Unevaluated
, který zabrání tomu, aby se daný výraz vyhodnotil:
f[Unevaluated[Sin[Pi/2]]] (* vrátí: Sin *)
Téhož chování lze dosáhnout i tak, že funkci f
nastavíme atribut HoldAll
, který zamezí tomu, aby k vyhodnocování argumentů docházelo obecně a Unevaluated
tak není potřeba:
SetAttributes[f, HoldAll]
f[Sin[Pi/2]] (* vrátí: Sin *)
Níže je podán krátký výběr nejzákladnějších prvků jazyka WL. Důraz je ve WL obecně kladen na funkcionální přístup, kdy například místo for cyklů pro úpravu polí prvek po prvku lze použít funkci Map
, která má jako první parametr funkci, jež se má na prvky uplatnit, a jejímž druhým parametrem je upravované pole. Dalším specifikem je používání vzorů, což jsou obdoby regulárních výrazů, které lze použít například pro vyhledávání prvků v poli se zadanými vlastnostmi či v definicích funkcí.
Syntaxe se v mnohém podobá známým jazykům jako je jazyk C. Až na speciální znaky a znaky, jež mají ve WL předdefinovanou funkci, lze pro identifikátory použít ve WL celou sadu Unicode[37] včetně písmen české abecedy, přičemž identifikátory nemohou začínat číslicí. V praxi se přesto většinou používají jen písmena anglické abecedy. Některé předdefinované interní konstanty a proměnné navíc začínají znakem dolaru $
. Na rozdíl od mnoha jiných jazyků nelze však v identifikátoru použít podtržítko, které v WL hraje funkci zástupného symbolu ve vzorech. WL je case sensitive.
V sekci "Ukázky" níže je vypsána řada příkladů ilustrující různou funkcionalitu jazyka WL. V článku "Syntaxe a sémantika programovacího jazyka Wolfram Language" lze pak nalézt další podrobnosti.
Nápověda se programově vyvolá zapsáním otazníku na začátek řádky, po němž následuje dotazovaný symbol, např. ?Sin
. Komentáře jsou vloženy mezi závorky s hvězdičkami způsobem (* toto je komentář *)
. Znak procenta %
je používán pro vyvolání posledního výsledku:
a = 1; (* do proměnné a ulož hodnotu 1 *)
b = a + 2; (* proměnná b obsahuje hodnotu 3 *)
% (* vrátí: 3 *)
Čísla mohou být jednoho z následujících typů: Integer
(celá čísla, nekonečná přesnost), Rational
(racionální čísla, nekonečná přesnost), Real
(reálná čísla s plovoucí řádovou čárkou, libovolně zvolitelná přesnost[4]), Complex
(komplexní čísla, jejichž reálná a imaginární část může být celá, racionální či reálná). Navíc je podporována řada konstant jako číslo pí Pi
či Eulerovo číslo E
. S čísly lze provádět obvyklé aritmetické operace:
3 + 4 (* sčítání; vrátí: 7 *)
3 - 4 (* odčítání; vrátí: -1 *)
3 * 4 (* násobení; vrátí: 12 *)
3 / 4 (* podíl dvou celých čísel; vrátí: 3/4 *)
3.0 / 4.0 (* podíl dvou reálných čísel; vrátí: 0.75 *)
Quotient[3, 4] (* celočíselné dělení; vrátí: 0 *)
Mod[3, 4] (* modulo operace; vrátí: 3 *)
3^4 (* mocnina; vrátí: 81 *)
2.3 + 4 I (* komplexní číslo; I označuje imaginární jednotku *)
Řetězce se zapisují pomocí dvojitých uvozovek a speciální znaky lze escapovat lomítkem. Dva a více řetězců lze spojit operátorem <>
:
"toto je řetězec"
"řekla: \"tak či onak\" \\ následuje\nnový řádek a\ttabulátor"
(* vypíše:
řekla: "tak či onak" \ následuje
nový řádek a tabulátor
*)
"první"<>" a "<>"druhý"
(* vrátí: "první a druhý" *)
Proměnné, konstanty či pojmenované funkce jsou reprezentovány symboly. Obecně jsou konstrukty ve WL reprezentovány jako výrazy (angl. expressions), které mají hlavičku, za níž v hranatých závorkách následuje tělo výrazu. Například volání funkce sinus Sin[0.2]
lze chápat jako výraz s hlavičkou Sin
, v jehož těle je reálné číslo . Podobně například komplexní číslo lze reprezentovat jako výraz Complex[1,2]
, jehož hlavičkou je Complex
a tělo tvoří dvě čísla 1 a 2.
Ve WL existují dva základní druhy přiřazení: přímé =
a odložené :=
[38]. Odložené přiřazení vyhodnocuje pravou stranu až ve chvíli, kdy je symbol na levé straně volán. Rozdíl mezi oběma přiřazeními lze nejsnáze nahlédnout, je-li na pravé straně generováno náhodné číslo. Zatímco do proměnné a
níže je uloženo jedno dané číslo, které je při volání této proměnné vráceno, v případě proměnné b
je funkce RandomReal
vždy znova zavolána při každém volání této proměnné:
a = RandomReal[]; (* přímé přiřazení *)
b := RandomReal[]; (* odložené přiřazení *)
{a,b} (* vrátí: {0.86258,0.480284} *)
{a,b} (* vrátí: {0.86258,0.705826} *)
Podporovány jsou standardní porovnávací operátory, přičemž nerovnost se zapisuje jako vykřičník následovaný rovnítkem !=
:
1 < 2 (* ostrá nerovnost; vrátí: True *)
1 >= 2 (* neostrá nerovnost; vrátí: False *)
1 == 1. (* rovnost; vrátí: True *)
1 != 2 (* nerovnost; vrátí: True *)
Logické operátory implementují zkrácené vyhodnocení, a tak je-li např. v operátoru AND z prvního argumentu zřejmé, že je výsledek False
, další argumenty se již nevyhodnocují. Negace se zapisuje vykřičníkem:
10 < 12 && "ret" != "aha" (* AND; vrátí: True *)
10 < 12 || (Print["druhý argument"];False) (* OR; vrátí: True; nic nevypíše, protože již první argument je True a celý výraz tak musí být True *)
!True (* negace; vrátí: False *)
Podmíněný příkaz je představován funkcí If
, jejímž prvním parametrem je podmínka, druhým parametrem je výraz, který se vyhodnotí, je-li podmínka pravdivá, a třetím parametrem je výraz vyhodnocený, je-li podmínka nepravdivá. Funkce If
má ještě nepovinný čtvrtý parametr, který je vyhodnocen, nelze-li určit pravdivostní hodnotu podmínky:
If[1 < 2, "mensi", "vetsi"] (* vrátí: "mensi" *)
If[x < y, "mensi", "vetsi", "nevim"] (* vrátí: "nevim" *)
Protože proměnné x
a y
nemají v kódu výše přiřazenu žádnou hodnotu, nelze vyhodnotit nerovnost a funkce If
tak vrátí čtvrtý argument.
Ačkoli WL podporuje tradiční cykly jako for cyklus For
, while cyklus While
a repeat-until cyklus Until
, není používání for cyklu kvůli jeho pomalé implementaci doporučeno[1]. Místo toho se doporučuje používání funkcí jako Do
pro opakované provádění kódu, Table
pro tvorbu polí či Nest
pro tvorbu vnořených struktur. Další alternativou je používání funkce Map
, jež aplikuje danou funkci na každý prvek pole:
pole = {0, 0, 0, 0, 0};
For[i = 1, i <= 5, i++, pole[[i]] = i^2];
pole (* vrátí: {1, 4, 9, 16, 25} *)
Table[i^2, {i, 5}] (* vrátí: {1, 4, 9, 16, 25} *)
Map[#^2 &, {1, 2, 3, 4, 5}] (* vrátí: {1, 4, 9, 16, 25} *)
Standardní vyhodnocování kódu lze programově přerušit funkcemi Throw
či Confirm
, jejichž volání musí být po řadě zachyceno funkcemi Catch
či Enclose
. Chybové hlášky a varování lze vypsat voláním funkce Message
. Například:
Catch[
a = 1;
Throw[chyba];
a = 3;
] (* vrátí: chyba; proměnná a má hodnotu 1 *)
Základními dvěma datovými strukturami ve WL jsou pole neboli seznamy (List
)[39] a asociativní pole (Association
)[40]. Pole jsou imutabilní datové struktury, které lze indexovat a lze do nich ukládat jako prvky libovolné další výrazy, které nemusejí být stejného typu. Do jednoho pole tak lze uložit čísla, řetězce, obrázky, zvukové nahrávky apod. Indexování se provádí pomocí dvojitých hranatých závorek, přičemž počáteční index je roven jedné a pro indexování od konce lze použít záporných čísel. Pole jsou zapisována do složených závorek:
pole = {5, -14, 42., "řetězec", Sin}; (* v poli lze ukládat i funkce *)
pole[[2]] (* vrátí: -14 *)
pole[[-2]] (* vrátí: "řetězec" *)
pole[[3]] = 1; (* do třetího prvku uloží číslo 1; výjimka z imutability *)
pole (* vrátí: {5, -14, 1, "řetězec", Sin} *)
Asociativní pole se zapisují do speciálních závorek skládajících se ze svislic a úhlových závorek, kde je každý pár (klíč, hodnota) propojen pomocí šipky ->
. Asociativní pole podporují jednak vyhledání hodnoty pomocí klíče, který se vkládá do jednoduchých hranatých závorek, jednak i indexování známé z normálních polí, a to pomocí dvojitých hranatých závorek. Klíčem může být libovolný výraz:
asoc = <|"a" -> 5, var -> -14, 345 -> 42., "klic" -> Sin|>;
asoc["a"] (* vrátí: 5 *)
asoc[[2]] (* vrátí: -14 *)
asoc[[3]] = 1; (* do třetí hodnoty uloží číslo 1 *)
asoc (* vrátí: <|"a" -> 5, var -> -14, 345 -> 1, "klic" -> Sin|> *)
Funkce lze definovat dvěma způsoby a to buď jako symboly, anebo jako anonymní funkce, které se ve WL označují jako ryzí funkce (angl. pure functions)[41]. První způsob přiřazuje k danému symbolu definici, jejíž standardní syntaxe zní následovně:
umocni[x_] := x^2
kde umocni
je identifikátor funkce, x_
je vzor odpovídající vstupnímu parametru a znak :=
vyjadřuje odložené vyhodnocení, na jehož pravé straně je tělo funkce. Volání takové funkce probíhá pomocí hranatých závorek:
umocni[3] (* vrátí: 9 *)
umocni[y] (* vrátí: y^2 *)
Jak vidno, pracovat lze i se symboly, které nemají přiřazenu hodnotu, jako v případě symbolu y
výše.
Tělo funkce může být značně složitější a vynucovat si tak lokální proměnné. V takovém případě lze použít například blokové struktury Block
či Module
:
složitějšíFunkce[x_, y_] := Module[{z = 0}, (* z je lokální proměnná *)
If[x < 0, z = 1];
Return[y^z]; (* příkaz Return lze vynechat a psát jen y^z bez koncového středníku *)
];
složitějšíFunkce[1, 2] (* vrátí: 1 *)
složitějšíFunkce[-1, 2] (* vrátí: 2 *)
z (* vrátí: z; mimo tělo funkce totiž z nemá přiřazenu žádnou hodnotu *)
Funkce jsou ve WL first-class objekty a tak lze s nimi manipulovat jako s běžnými proměnnými:
mojeFunkce = umocni;
mojeFunkce[3] (* vrátí: 9 *)
Ryzí funkce lze zapisovat speciální syntaxí používající křížek #
k označení nepojmenovaných vstupů, přičemž ke konci těla funkce se připojuje ampersand &
. Výše definovanou funkci umocni tak lze přepsat jako ryzí funkci do tvaru:
umocni = (#^2) &;
umocni[3] (* vrátí: 9 *)
Velkou kapitolu tvoří ve WL vzory (angl. patterns)[42][43] a nahrazovací pravidla (angl. rules), jež umožňují flexibilní manipulaci s daty či kódem a poskytují tak jistou alternativu ke standardním funkcím. Máme-li například pole čísel, kde chceme každý výskyt čísla 3 nahradit řetězcem "tři"
, lze toto učinit následujícím zápisem:
{1, 2, 3, 4, 3, 2, 1} /. 3->"tři" (* vrátí: {1, 2, "tři", 4, "tři", 2, 1} *)
Tento zápis se skládá z výrazu, v němž chceme provést substituci, v příkladu výše tedy číselné pole {1, 2, 3, 4, 3, 2, 1}
, a zápisu /. 3->"tři"
[pozn. 9], kde 3->"tři"
je samotné nahrazovací pravidlo, jež udává, jaká substituce se má provést. Konečně znaky /.
jsou syntaktický cukr pro funkci ReplaceAll
, která provádí samotné nahrazení. Je-li nahrazovacích pravidel udáno více, je nutno je zabalit do složených závorek, v případě jediného pravidla toto nutné není:
{1, 2, 3, 4, 3, 2, 1} /. {2->"dva", 3->"tři"} (* vrátí: {1, "dva", "tři", 4, "tři", "dva", 1} *)
Stejně tak lze upravovat další výrazy, například algebraické výrazy či části kódu:
x^3 + 3 x^2 + 5 x + 1 /. x->y (* vrátí: y^3 + 3 y^2 + 5 y + 1 *)
x^2 /. x->2 (* vrátí: 4 *)
Podobně jako u přiřazení, i u nahrazovacích pravidel existuje odložená varianta, která se zapisuje symboly :>
, kde se pravá strana pravidla vyhodnotí až ve chvíli volání. Chceme-li každý výskyt celého čísla v poli nahradit jeho druhou mocninou, lze toto provést následovně[pozn. 10]:
{2, 3, x, y} /. x_Integer:>x^2 (* vrátí: {4, 9, x, y} *)
Způsob, jakým jsou vzory používány pro nalezení odpovídající části kódu, se anglicky nazývá pattern matching[42], což lze přeložit jako porovnávání se vzorem.
Součástí WL je kromě funkcí a interních proměnných i přístup k datům uchovávaným na serverech společnosti Wolfram Research. Tato firma označuje WL jako knowledge based[44], to jest založený na znalostech. Jedná se například o astronomická, klimatická či finanční data, jež lze získat funkcemi jako WeatherData
, FinancialData
atd. Alternativním přístupem je vyjádření znalostí pomocí ontologie, v níž je každý objekt představován výrazem Entity
. Například počet obyvatel České republiky lze získat voláním
Entity["Country", "CzechRepublic"]["Population"]
Obsah stránek na Wikipedii lze obdržet funkcí WikipediaData
, položky z projektu Wikidata lze získat voláním funkce WikidataData
. Podklady funkcí jako GeoGraphics
, jež slouží pro práci s geografickými daty, jsou založeny na mapách tvořených v rámci projektu OpenStreetMap. Přímo z editoru WL lze vyvolat i funkcionalitu projektu Wolfram|Alpha a to buď funkcí WolframAlpha
, anebo speciální syntaxí, kdy je kurzor přesunut na začátek prázdného řádku a poté je dvakrát zmáčknuta klávesa pro rovnítko. Vyvolá se tak speciální řádka, do níž lze vložit dotaz v přirozené angličtině, pod níž se vykreslí zpracovaný výstup.
Vývojář WL udržuje kromě toho několik online repozitářů poskytujících dodatečná data a funkce:
ResourceObject
, jenž obsahuje název zdroje.NetModel
.ResourceFunction
.PacletInstall
.Databin
zasílat kontinuálně na server a pak je kdykoliv ze serveru stáhnout a zpracovat.[pozn. 11]Pro ilustraci používání výše uvedených zdrojů přímo z editoru WL uveďme použití funkcí z repozitáře Wolfram Function Repository. Řekněme například, že máme dvě křivky a chceme spočíst plochu sevřenou mezi těmito křivkami. Nevíme ale, jakou funkci k tomu použít, přičemž podobná funkce se v základní sadě funkcí WL nenachází. Můžeme proto zkusit vyhledat tuto funkci v online repozitáři na základě klíčových slov, která v aktuálním případě zvolíme jako "area" (to jest "plocha") a "curve" (to jest "křivka"). Pokud taková funkce existuje, můžeme jí poté rovnou použít:
ResourceSearch["area curve"] (* vyhledej funkci v repozitáři na základě klíčových slov "area" a "curve" *)
(* volání funkce ResourceSearch vrátí interaktivní databázi relevantních dostupných funkcí *)
(* na základě popisků v databázi usoudíme, že námi hledaná funkce se jmenuje "AreaBetweenCurves" *)
(* v databázi lze nalézt i odkaz na dokumentaci této funkce *)
ResourceFunction["AreaBetweenCurves"][{x, x^2}, {x, 0, 1}] (* voláme funkci ResourceFunction["AreaBetweenCurves"] *)
Výpočetní systém automaticky stáhne definici dané funkce z online repozitáře do lokálního výpočetního jádra a tuto funkci tak lze rovnou použít.
Výchozím pracovním prostředím pro psaní kódu ve WL je tak zvaný notebook, kde je kód strukturován do hierarchie elementárních částí zvaných buňky (angl. cells). Příkazy jsou psány do vstupních buněk a po vyhodnocení vstupní buňky je výsledek výpočtu vrácen do výstupní buňky, která se automaticky pod tou vstupní vytvoří. Buňky lze dále seskupovat do větších celků a utvářet tak strukturu kódu podobnou kapitolám a podkapitolám známým z psaných knih. Buňky plní různé role, lze vkládat i čistě textové buňky či nadpisy. Tyto buňky nejsou při běhu kódu vyhodnoceny a mohou tak zastupovat funkci komentářů.
Každá vstupní buňka je uvedena řetězcem "In[n]:="
, kde n
je její pořadové číslo. Výstup kódu obsaženého v takové buňce je vypsán pod tuto buňku do nové výstupní buňky, jež je uvozena řetězcem "Out[n]="
. Konkrétní kód v editoru, jakým je tradičně Mathematica, pak může vypadat následovně:
In[1]:= 1 + 2*3
Out[1]= 7
Spočti obsah kruhu
In[2]:= polomer = 0.2;
obsahKruhu = Pi*polomer^2;
In[3]:= obsahKruhu
Out[3]= 0.125664
Druhá buňka byla nastavena jako textová buňka a tak není její obsah vyhodnocen. Ve třetí vstupní buňce je specifikováno více příkazů. Jejich výstup je středníky potlačen a výstup tak není vypsán.
Interně je každá buňka představována výrazem Cell[obsah, styl, volby]
, kde obsah
může být buď čistý text, anebo strukturovanější data zabalená do výrazu s hlavičkou BoxData
; styl
označuje styl buňky, jako například vstupní ("Input"
), výstupní ("Output"
), textová ("Text"
), titul ("Title"
) apod.; a volby
jsou dodatečná nastavení buňky, jakým může být například barva pozadí, čas poslední změny dané buňky či identifikační číslo buňky. Buňka tedy obsahuje více informací, než které jsou uživateli v editoru přímo přístupné a z tohoto pohledu nelze kód notebooku označit jako WYSIWYG.
Vypiš text "Ahoj světe!":
Print["Ahoj světe!"];
Definuj Fibonacciho posloupnost:
f[0] = 0;
f[1] = 1;
f[n_] := f[n - 1] + f[n - 2]
Tato implementace je neefektivní, protože při výpočtu daného prvku posloupnosti jsou rekurzivně počítány všechny prvky předchozí. Tomu lze předejít využitím memoizace, kde je každý prvek spočten jen jednou a poté držen v paměti[50]:
f[0] = 0;
f[1] = 1;
f[n_] := f[n] = f[n - 1] + f[n - 2]
Spočti faktoriál:
f[0] = 1;
f[n_] := n f[n-1]
Podobně jako výše u Fibonacciho posloupnosti lze i zde využít memoizace.
Provádět operace s poli lze několika způsoby, které si ilustrujeme na příkladu, kde je cílem spočíst druhou mocninu každého prvku zadaného jednorozměrného pole.
For
cyklu:pole = {1, 2, 3, 4};
For[i = 1, i <= Length[pole], i++,
pole[[i]] = pole[[i]]^2;
];
pole (* vrátí: {1, 4, 9, 16} *)
Map
:#^2& /@ {1, 2, 3, 4} (* vrátí: {1, 4, 9, 16} *)
Zde je ryzí funkce #^2&
aplikována na každý prvek pole zápisem /@
, jenž odpovídá volání funkce Map
.
Listable
:{1, 2, 3, 4}^2 (* vrátí: {1, 4, 9, 16} *)
Funkce Power
, kterou jsme vyvolali stříškou ^
, má atribut Listable
. Je-li funkce s tímto atributem zavolána na seznam, to jest výraz s hlavičkou List
, je tato funkce automaticky aplikována na každý prvek seznamu. Tento atribut mají například i funkce Sin
, Plus
či Divide
.
Ve WL má mnoho funkcí ekvivalentní zkrácený zápis poskytující syntaktický cukr pro kompaktnější vyjádření. Pro příklad uvažme pole a = {12, p, {b, c}, {}}
, kde je naším cílem nahradit každý jeho prvek, jenž je sám seznamem o alespoň jednom prvku, jeho posledním prvkem. To lze provést následujícím kódem:
anovy = {};
For[i = 1, i <= Length[a], i++,
If[Head[a[[i]]] == List && Length[a[[i]]] >= 1, elem = a[[i, -1]], elem = a[[i]]];
AppendTo[anovy, elem]
];
anovy (* vrátí: {12, p, c, {}} *)
S použitím vzorů a funkcionálního přístupu lze téhož dosáhnout i následovně:
Map[Function[y,
ReplaceAll[y,
RuleDelayed[Pattern[x, List[BlankSequence[]]], Last[x]]
]
], a] (* vrátí: {12, p, c, {}} *)
Tento druhý přístup lze přepsat do velmi kompaktního tvaru s použitím syntaktického cukru:
(# /. {x : {__} :> x[[-1]]}) & /@ a (* vrátí: {12, p, c, {}} *)
Na každý prvek pole a
je pomocí funkce Map
, představované zápisem /@
, aplikována ryzí funkce (# /. {x : {__} :> x[[-1]]}) &
, jež vezme vstupní parametr, představovaný slotem #
, a pomocí funkce ReplaceAll
, představované zápisem /.
, provede substituci zadanou předpisem x : {__} :> x[[-1]]
. Toto substituční pravidlo nahradí každý výskyt seznamu o alespoň jednom prvku, představovaného výrazem {__}
a pojmenovaného x
, jeho posledním prvkem, to jest výrazem x[[-1]]
.
WL umožňuje symbolické programování, kde s kódem samotným lze nakládat jako s daty. Pro ilustraci uveďme následující příklad, kde je cílem vytvořit vícerozměrné pole, kde dimenzi pole dopředu nevíme a je nechaná jako nezávislá proměnná. Pro konkrétnost mějme funkci f
, kterou chceme aplikovat na vícerozměrné pole o dané dimenzi D. Toho lze dosáhnout funkcí Table
, jejíž syntaxe zní
Table[f[i1, i2, ..., iD], {i1, pocatek, konec}, {i2, pocatek, konec}, ..., {iD, pocatek, konec}];
kde i1
, i2
atd. jsou indexovací proměnné a kde tři tečky ...
nejsou součástí kódu, ale pouze naznačují, že je potřeba dodat tolik indexovacích proměnných, kolik má být dimenze výsledného pole. Důležité v tomto ohledu je to, že výše uvedený zápis není jen syntaxí pro funkci Table
, ale skutečně výraz tvaru hlavicka[telo]
, jehož hlavičkou je identifikátor Table
a jehož tělo je tvořeno několika prvky, z nichž první má tvar f[i1, i2, ..., iD]
a zbylé prvky jsou seznamy. Přitom první prvek každého seznamu je symbol tvaru i1
, i2
, atd., druhý prvek pocatek
je celé číslo a třetí prvek každého seznamu je celé číslo konec
.
Tato reprezentace kódu umožňuje napsat program, který vytvoří výše zadaný výraz pro libovolnou vstupní dimenzi a rozsah hodnot indexovací proměnné. Pro začátek uvažme dvourozměrné pole, jehož prvky jsou celá čísla v rozsahu -1 až 1. Následující kód bere jako vstupní proměnné proměnnou dimenze
určující dimenzi pole a proměnné pocatek
a konec
určující rozsah hodnot v každé dimenzi. Výsledek je uložen do proměnné pole
:
dimenze = 2;
pocatek = -1;
konec = 1;
(* vygeneruj indexovací proměnné *)
indexy = Symbol["i"<>ToString@#]& /@ Range[dimenze] (* vrátí: {i1, i2} *)
(* pro každou proměnnou specifikuj rozsah hodnot *)
rozsahy = {#, pocatek, konec}& /@ indexy (* vrátí: {{i1, -1, 1}, {i2, -1, 1}} *)
(* vlož rozsahy do funkce Table, která vytvoří pole indexací přes dané rozsahy *)
pole = Table[f@@indexy, Evaluate[Sequence@@rozsahy]]
(* vrátí: {{f[-1,-1], f[-1,0], f[-1,1]}, {f[0,-1], f[0,0], f[0,1]}, {f[1,-1], f[1,0], f[1,1]}} *)
Tento kód lze snadno přizpůsobit pro libovolnou jinou dimenzi změnou hodnoty proměnné dimenze
(samozřejmě za předpokladu, že funkce f
bere více proměnných). Výše uvedený kód tak v podstatě odpovídá posloupnosti vnořených for cyklů, kde počet těchto cyklů není dopředu znám a je určen hodnotou proměnné zadané uživatelem.
Při definici funkce lze kromě samotného těla funkce specifikovat i značné množství dalších prvků, které ovlivňují chování funkce a její zobrazení v editoru. Uvažme funkci f[a,b]
, jejíž hlavní funkcí je dělit parametr a
parametrem b
. Spolu s tím chceme mít možnost nastavit, zda dělení má probíhat standardně či celočíselně. Pro ilustraci rozšířených možností definice ve WL jsou do následujícího kódu dodány i vzory, které by v reálném případě patrně nebyly potřeba.
(* smaž předchozí definice *)
ClearAll[f]
(* dokumentační a chybová hlášení *)
f::usage = "Dělení kladného celého čísla";
f::invpars = "Neplatné vstupy ``, ``.";
f::divzero = "Dělení nuly nulou.";
(* první blok *)
SyntaxInformation[f] = {"ArgumentsPattern" -> {__}};
SetAttributes[f, Listable];
Options[f] = {"Celociselne"->False};
Default[f] = 2;
(* druhý blok *)
f[0, 0, ___] := Message[f::divzero];Indeterminate;
f[x_Integer?NonNegative, y_., OptionsPattern[]] /; y > 0 :=
Module[{meth = OptionValue["Celociselne"]}, If[meth, Quotient[x, y], x / y]]
f[x_, y_, ___] := Message[f::invpars, x, y]
Nejprve příkazem ClearAll
smažeme všechny předchozí definice symbolu f
, pokud nějaké existovaly. Poté uvedeme dokumentační řetězec a chybová hlášení. Dále specifikujeme, viz sekci kódu "první blok", že se má uživateli v editoru barevně zvýraznit, nenapíše-li žádný vstupní parametr. Nastavením Listable
jako atribut způsobíme, že při aplikaci funkce f
na seznam se tato automaticky aplikuje na jednotlivé prvky. Funkce f
má jednu volbu s názvem "Celociselne"
, udávající, má-li se provést dělení celočíselné či standardní. První blok kódu je zakončen příkazem Default[f] = 2
, který udává, že pokud není dodán vstupní parametr, je automaticky použita hodnota 2.
Druhý blok kódu obsahuje definici samotnou. Nejprve je ošetřen hraniční případ, kdy by mělo dojít k dělení nuly nulou. V takovém případě je vypsána chybová hláška a vrácen předdefinovaný symbol Indeterminate
, to jest, neurčité. Poté je dodána hlavní definice, která implementuje dělení vstupních parametrů. Tato definice je zavolána ale pouze tehdy, je-li první parametr nezáporné (NonNegative
) celé číslo (Integer
) a současně je druhý parametr kladný (y > 0
). Pokud jsou tyto podmínky splněny, je nejprve vyvolána hodnota volby "Celociselne"
a uložena do lokální proměnné meth
. Je-li meth
rovno True
, je návratová hodnota výrazu If
rovna výrazu Quotient[x,y]
. Tento výraz je vyhodnocen a protože není If
konstrukt ukončen středníkem, tak je výsledek výpočtu Quotient[x,y]
vrácen funkcí f
jako její návratová hodnota. Pokud je meth
rovno False
, probíhá vyhodnocení stejně, akorát je místo funkce Quotient
použito reálného dělení.
Konečně, pokud nejsou splněny výše uvedené podmínky na vstupní parametry, je použita poslední část definice na posledním řádku začínající konstruktem f[x_, y_, ___]
. Tento konstrukt neobsahuje žádné podmínky a vrací chybové hlášení. Nevyhodnotí se však vždy. Výše uvedená definice nezachycuje např. případy tvaru f[-10]
. Takový výraz zůstává nevyhodnocen.
Existuje několik programů sloužících k psaní či alespoň prohlížení kódu a výstupů WL jazyka, přičemž až na jednu výjimku v podobě pluginu pro IntelliJ IDEA jsou všechny tyto programy vyvíjené společností Wolfram Research. Níže je jejich výběr.
WolframKernel
či wolfram
v závislosti na operačním systému[pozn. 12].wolframscript
mohou obsahovat shebang, díky nimž z nich lze v UNIXových systémech udělat spustitelný program.Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.