Hlavní navigace

Jazyky Hy a Clojure-py: moderní dialekty LISPu určené pro Python VM

Pavel Tišnovský

Již jsme setkali s mnoha dialekty jazyka LISP. Především se jednalo o Scheme, Clojure a jako varianty (včetně jazyků Pixie a Wisp). Existují však i zajímavé dialekty LISPu určené pro Python VM, především elegantní jazyk Hy.

Doba čtení: 33 minut

11. Pojmenované funkce

12. Rekurze a tail rekurze

13. Jazyk Clojure-py

14. Instalace jazyka clojure-py

15. Spuštění interpretu a práce s interaktivní smyčkou REPL

16. Zpracování sekvencí a další základní mechanismy, na nichž je Clojure postaveno

17. Rozdílné chování oproti originálnímu jazyku Clojure

18. Repositář s demonstračními příklady

19. Odkazy na předchozí části tohoto seriálu o jazyku Clojure

20. Odkazy na Internetu

1. Jazyky Hy a Clojure-py: moderní dialekty LISPu určené pro Python VM

V dnešním článku se (prozatím ovšem jen ve stručnosti) seznámíme s dvojicí dialektů LISPu určených pro běh ve virtuálním stroji Pythonu. Tyto dialekty jsou tedy navrženy takovým způsobem, aby se LISPovské programy v nich zapsané překládaly do bajtkódu Python VM, a to buď přímo nebo nepřímo přes mezikód představovaný Pythonem popř. AST (abstraktním syntaktickým stromem). Zaměříme se zejména na velmi pěkně navržený projekt Hy, který podporuje obousměrnou komunikaci s Pythonem a tudíž i se všemi knihovnami, debuggery atd. Popíšeme si i projekt Clojure-py; ten je však v současnosti neudržovaný, má několik chyb a prozatím běží pouze na Pythonu 2.

Spojení dialektu LISPu s virtuálním strojem Pythonu popř. přímo s programovacím jazykem Python (tzv.interop) není zajímavé jen z technologického hlediska, protože může mít i praktické využití. Ostatně LISP byl jazykem používaným pro vývoj AI, a to už v padesátých letech minulého století (z dnešního pohledu se ale mnohdy jednalo spíš o hledání slepých uliček). A dnes má v oblasti AI a strojového učení stejně výsadní postavení Python, takže se vlastně spojení LISP+Python jeví pragmaticky.

2. Python VM jakožto platforma pro další programovací jazyky

Fakt, že různé dialekty jazyků LISP a Scheme vznikají pro prakticky všechny moderní virtuální stroje (typicky pro JVM, VM JavaScriptu nebo VM Pythonu) vlastně není nic překvapivého. Musíme si totiž uvědomit, že praktická použitelnost programovacího jazyka je do značné míry určena i celým ekosystémem, který je programátorům k dispozici. A ekosystém Pythonu je dnes již velmi rozsáhlý a obsahuje kvalitní a v celém světě používané knihovny i celé frameworky, takže se může jednat o vhodný základ, na němž je možné postavit moderní varianty LISPu. Podobným způsobem ostatně vznikl i jazyk Clojure (což je taktéž dialekt LISPu, i když v mnoha ohledech vylepšený), který vlastně vůbec nemá svůj vlastní VM – původně Clojure vzniklo pro virtuální stroj Javy (JVM), další varianty posléze byly implementovány pro CLR (ClojureCLR) a ClojureScript, který je kompilovaný do JavaScriptu, takže může běžet buď v prohlížeči, v Node.js atd.

Pro Python VM je v současnosti k dispozici hned několik dialektů LISPu, Scheme či Clojure. Jedná se například o méně známé projekty Lizpop, Lispy či Lython, dále o SchemePy, Clojure-py o němž se zmíníme v dalším textu a především velmi zajímavým způsobem implementovaný programovací jazyk <a>Hy, kterému bude věnována větší část dnešního článku. Do této kategorie částečně spadá i jazyk Pixie, s nímž jsme se již na stránkách Roota seznámili.

3. Programovací jazyk Hy umožňující elegantní propojení LISPu a Pythonu

Programovací jazyk Hy je ve velké míře inspirován (poměrně populárním) jazykem Clojure, s nímž jsme se na stránkách Roota již mnohokrát setkali (viz odkazy). Ovšem zatímco interpret Clojure překládá všechny zapsané výrazy do bajtkódu JVM a teprve poté je spouští, pracuje Hy odlišně, protože kód generuje pomocí AST a dokonce dokáže zdrojový LISPovský kód transformovat do Pythonu a teprve poté ho spustit. To je výhodná vlastnost, protože umožňuje Hy integrovat například s debuggery atd. Překlad přes AST nebo Python podporuje jak Python 2.x, tak i Python 3.x. Další důležitou vlastností Hy možnost plné kooperace mezi kódem zapsaným přímo v tomto jazyku a Pythoním kódem, což znamená, že je možné použít všechny Pythonovské knihovny a frameworky (včetně Numpy, PyTorch, Flask atd.) a naopak – například mít napsanou aplikaci v Pythonu a pro manipulaci se symboly použít Hy (v tomto ohledu jsou homoikonické programovací jazyky s makry podle mého názoru mnohem lepší než samotný Python).

4. Instalace jazyka Hy

Instalace jazyka Hy je ve skutečnosti velmi snadná, protože je tento jazyk pochopitelně dostupný na PyPi, popř. je možné získat jeho zdrojové kódy na GitHubu. V dalším textu si popíšeme instalaci s využitím nástroje pip. Pro jednoduchost provedeme lokální instalaci pro právě přihlášeného uživatele, takže pip zavoláme s přepínačem –user. Používáme pip3, ovšem instalaci je v případě potřeby možné provést i pro Python 2:

$ pip3 install --user hy
 
Downloading/unpacking hy
  Downloading hy-0.14.0-py2.py3-none-any.whl (68kB): 68kB downloaded
Downloading/unpacking clint>=0.4 (from hy)
  Downloading clint-0.5.1.tar.gz
  Running setup.py (path:/tmp/pip_build_tester/clint/setup.py) egg_info for package clint
 
    warning: no files found matching '*' under directory 'docs'
Downloading/unpacking astor>=0.6 (from hy)
  Downloading astor-0.6.2-py2.py3-none-any.whl
Downloading/unpacking rply>=0.7.5 (from hy)
  Downloading rply-0.7.5-py2.py3-none-any.whl
Downloading/unpacking args (from clint>=0.4->hy)
  Downloading args-0.1.0.tar.gz
  Running setup.py (path:/tmp/pip_build_tester/args/setup.py) egg_info for package args
 
Downloading/unpacking appdirs (from rply>=0.7.5->hy)
  Downloading appdirs-1.4.3-py2.py3-none-any.whl
Installing collected packages: hy, clint, astor, rply, args, appdirs
  Running setup.py install for clint
 
    warning: no files found matching '*' under directory 'docs'
  Running setup.py install for args
 
Successfully installed hy clint astor rply args appdirs
Cleaning up...

Po instalaci (pro právě přihlášeného uživatele) by se měly v adresáři ~/.local/bin objevit nové spustitelné soubory hy, hy2py atd.:

$ ls -l ~/.local/bin
 
total 40
-rwxr-xr-x 1 tester tester 217 dub 24 21:21 flake8
-rwxr-xr-x 1 tester tester 218 kvě 17 21:27 hy
-rwxr-xr-x 1 tester tester 224 kvě 17 21:27 hy2py
-rwxr-xr-x 1 tester tester 224 kvě 17 21:27 hy2py3
-rwxr-xr-x 1 tester tester 218 kvě 17 21:27 hy3
-rwxr-xr-x 1 tester tester 220 kvě 17 21:27 hyc
-rwxr-xr-x 1 tester tester 220 kvě 17 21:27 hyc3
-rwxr-xr-x 1 tester tester 215 dub 24 21:21 pycodestyle
-rwxr-xr-x 1 tester tester 214 dub 24 21:21 pyflakes
-rwxr-xr-x 1 tester tester 207 dub 24 21:21 radon
Poznámka: v dalším textu předpokládám, že adresář ~/.local/bin je součástí PATH. Pokud tomu tak není, bude nutné interpret i překladač volat s uvedením plné cesty, což je ovšem zbytečně komplikované. Obsah proměnné PATH zjistíte snadno pomocí echo $PATH. Také je možné použít whereis hy pro zjištění, zda shell nalezne daný příkaz.

5. Spuštění interpretu jazyka Hy a práce s interaktivní smyčkou REPL

Interpret jazyka Hy se spouští velmi jednoduše: příkazem hy. Tento příkaz podporuje několik přepínačů, z nichž nejzajímavější je –spy, o němž se zmíníme v dalším textu:

$ hy --help
usage: hy [-h | -i cmd | -c cmd | -m module | file | -] [arg] ...
 
optional arguments:
  -h, --help            show this help message and exit
  -c COMMAND            program passed in as a string
  -m MOD                module to run, passed in as a string
  -i ICOMMAND           program passed in as a string, then stay in REPL
  --spy                 print equivalent Python code before executing
  --repl-output-fn REPL_OUTPUT_FN
                        function for printing REPL output (e.g.,
                        hy.contrib.hy-repr.hy-repr)
  -v, --version         show program's version number and exit
  --show-tracebacks     show complete tracebacks for Hy exceptions
 
  file         program read from script
  module       module to execute as main
  -            program read from stdin
  [arg] ...    arguments passed to program in sys.argv[1:]

Zkusme si nyní interpret spustit a získat tak přístup k interaktivní smyčce REPL. Interpret se spustí prakticky okamžitě (na rozdíl od klasického Clojure založeného na JVM, kde je prodleva již patrnější). Výzva interpretru (prompt) se skládá ze znaků ⇒ (opět na rozdíl od Clojure bez uvedení výchozího jmenného prostoru):

$ hy
 
hy 0.14.0 using CPython(default) 3.6.3 on Linux
=>

V interaktivní smyčce je každý výraz po svém načtení (Read) ze standardního vstupu ihned vyhodnocen (Eval) a výsledná hodnota je následně vypsána do terminálu (Print) a interpret bude očekávat načtení dalšího výrazu (Loop). Některé výrazy jsou jednoduché, protože se vyhodnotí samy na sebe. Příkladem mohou být numerické hodnoty, pravdivostní hodnoty, řetězce atd:

=> 42
42
 
=> 3.1415
3.1415
 
=> 1+2j
(1+2j)
 
=> 6j
6j
 
=> 4/7
Fraction(4, 7)
 
=> True
True
 
=> False
False
 
=> None
 

6. Základní datové typy

V této kapitole si popíšeme základní jednoduché datové typy. Především se jedná o pravdivostní hodnoty. A právě v tomto ohledu se Hy odlišuje od dalších dialektů LISPu, protože pro pravdivostní hodnoty používá True a False převzaté přímo z Pythonu. I prázdná hodnota se zapisuje jinak než v mnoha dalších interpretrech LISPu, protože se používá Pythonovské None. Rozdíly shrnuje následující tabulka:

Dialekt Pravda Nepravda Prázdná hodnota
Common Lisp t nil nil
Scheme #t #f '()
Clojure true false nil
Hy True False None
Poznámka: další zajímavé porovnání různých dialektů LISPu naleznete na stránce http://hyperpolyglot.org/lisp.

Můžeme si to vyzkoušet:

=> None
 
=> True
True
 
=> False
False
 
=> nil
Traceback (most recent call last):
  File "/home/tester/.local/lib/python3.4/site-packages/hy/importer.py", line 201, in hy_eval
    return eval(ast_compile(expr, "<eval>", "eval"), namespace)
  File "<eval>", line 1, in <module>
NameError: name 'nil' is not defined
 
=> true
Traceback (most recent call last):
  File "/home/tester/.local/lib/python3.4/site-packages/hy/importer.py", line 201, in hy_eval
    return eval(ast_compile(expr, "<eval>", "eval"), namespace)
  File "<eval>", line 1, in <module>
NameError: name 'true' is not defined
 
=> false
Traceback (most recent call last):
  File "/home/tester/.local/lib/python3.4/site-packages/hy/importer.py", line 201, in hy_eval
    return eval(ast_compile(expr, "<eval>", "eval"), namespace)
  File "<eval>", line 1, in <module>
NameError: name 'false' is not defined

Programovací jazyk Hy rozlišuje mezi celými čísly, čísly s plovoucí řádovou čárkou, komplexními čísly a konečně zlomky. První tři numerické typy jsou převzaty z Pythonu a jsou s ním plně kompatibilní; zlomky jsou typické pro prakticky všechny LISPovské jazyky a jsou interně implementovány objektem. U numerických hodnot je možné používat prefixy 0×, 0o a 0b:

=> ; numerické hodnoty
=> ;   celá čísla
=> 42
42
 
=> 0x2a
42
 
=> 0o52
42
 
=> 0b00101010
42
 
=> ; čísla s plovoucí řádovou čárkou
=> 3.14
3.14
 
=> ; komplexní čísla
=> 1+2j
(1+2j)
=> 3j
3j
 
=> ; zlomky
=> 2/3
Fraction(2, 3)

Řetězce se zapisují prakticky stejně jako v Pythonu, ovšem musí se použít uvozovky a nikoli apostrofy (ty mají odlišný význam). Podporovány jsou i prefixy pro Unicode řetězce (výchozí) a tzv. „raw“ řetězce (typicky používané při zápisu regulárních výrazů):

=> ; řetězce
=> "Hello world!"
'Hello world!'
 
=> u"Hello world!"
'Hello world!'
 
=> r"Hello world!"
'Hello world!'

Specialitou jsou tzv.„here dokumenty“, tj. víceřádkové řetězce s prakticky libovolným obsahem, jejichž zápis byl inspirován programovacím jazykem Lua. Tyto řetězce začínají znaky #[XXX[ a končí znaky ]XXX], přičemž za XXX je možné doplnit libovolně dlouhou (i prázdnou) sekvenci znaků, která se v řetězci v ideálním případě nebude vyskytovat:

; "here" řetězce
#[[
 -------
( Lisp! )
 -------
  \
   \   \_\_    _/_/
    \      \__/
           (oo)\_______
           (__)\       )\/\
               ||----w |
               ||     ||
]]
 
#[---[
 -------
( Lisp! )
 -------
  \
   \   \_\_    _/_/
    \      \__/
           (oo)\_______
           (__)\       )\/\
               ||----w |
               ||     ||
]---]

7. Tečka-dvojice: základ pro tvorbu složitějších datových struktur v klasickém LISPu

Programovací jazyk LISP je založen na zpracování seznamů. Jak jsou však seznamy uloženy v operační paměti počítače a jak s nimi interpretry tohoto jazyka pracují? Základní interní strukturou, která je však dostupná i programátorům aplikací v jazyce LISP, je takzvaná tečka-dvojice (dotted-pair). Tuto strukturu si můžeme představit jako dvojici ukazatelů, přičemž každý z těchto ukazatelů může obsahovat adresu atomu, adresu další tečka-dvojice nebo speciální hodnotu nil odpovídající v céčku hodnotě NULL či v Javě hodnotě null, tj. jedná se o speciální hodnotu, která interpretru říká, že daný ukazatel neobsahuje žádný odkaz. Tečka-dvojici lze v LISPovských programech zapisovat formou dvojice výrazů (takzvaných S-výrazů) oddělených tečkou, které jsou uzavřeny do kulatých závorek (i když je pravda, že se s tečka-dvojicemi v reálných programech příliš často nesetkáme, především z důvodu nepřehledného zápisu s velkým množstvím závorek).

Tečka dvojice jsou podporovány i v jazyku Hy, i když je prakticky nevyužijeme:

=> '(a . None)
<HyCons (
  HySymbol('a')
. HySymbol('None'))>
 
=> '(a . b)
<HyCons (
  HySymbol('a')
. HySymbol('b'))>
 
=> '(a . (b . (c . None)))
<HyCons (
  HySymbol('a')
  HySymbol('b')
  HySymbol('c')
. HySymbol('None'))>
 
=> '( (a . b) (c . d))
HyExpression([
  <HyCons (
    HySymbol('a')
  . HySymbol('b'))>,
  <HyCons (
    HySymbol('c')
  . HySymbol('d'))>])
Poznámka: pomocí tečka-dvojic je možné v LISPu (ne v Hy!) vytvořit klasický seznam následujícím způsobem: první ukazatel každé n-té tečka-dvojice odkazuje na n-tý prvek seznamu (například atom), druhý ukazatel se odkazuje na další (n plus první) tečka-dvojici. Speciálním případem je poslední tečka-dvojice, jejíž druhý ukazatel obsahuje výše uvedenou speciální hodnotu nil. Z následujícího příkladu (obsahujícího ekvivalentní datové struktury) je patrné, že použití syntaxe pro zápis seznamů je přehlednější a současně i kratší, než explicitní zápis tečka-dvojic; ovšem právě znalost vnitřní reprezentace seznamů pomocí tečka-dvojic nám umožňuje pochopit, jak pracují některé základní funkce:
; seznam zapsaný pomocí tečka-dvojic
(1.(2.(3.(4.(5.nil)))))
 
; běžný způsob zápisu seznamu
(1 2 3 4 5)
 
; interní struktura seznamu v paměti
;         .
;        / \
;       1   .
;          / \
;         2   .
;            / \
;           3   .
;              / \
;             4   .
;                / \
;               5   nil

Poznamenejme, že další struktury vytvořené pomocí rekurzivně zanořených tečka-dvojic není možné převést na běžné seznamy. Například jednoduchý binární strom se třemi úrovněmi a čtyřmi listy lze reprezentovat buď pomocí tečka-dvojic (v paměti se vytvoří skutečná obdoba binárního stromu), popř. je možné tuto datovou strukturu „simulovat “pomocí seznamů (ovšem v tomto případě bude paměťová náročnost vyšší kvůli nutnosti ukončení všech podseznamů tečka dvojicí obsahující ve svém druhém ukazateli hodnotu nil):

; binární strom se třemi úrovněmi a čtyřmi listy vytvořený pomocí tečka dvojic
((A.B).(C.D))
; interní podoba této struktury v operační paměti:
;     .
;    / \
;   .   .
;  / \ / \
;  A B C D
 
; binární strom vytvořený pomocí LISPovských seznamů
((A B) (C D))
; interní podoba této struktury v operační paměti:
;         .
;        / \
;       /   \
;      /     \
;     /       \
;    .         .
;   / \       / \
;   A  .     .  nil
;     / \   / \
;     B nil C  .
;             / \
;             D nil

8. Strukturované datové typy – kolekce a n-tice

Programovací jazyk Hy podporuje všechny základní strukturované datové typy, které známe z jazyka Clojure. Jedná se o seznamy, vektory, mapy i množiny:

# Typ kolekce Zápis konstruktoru
1 Seznam '(prvky)
2 Vektor [prvky]
3 Mapa {dvojice klíč-hodnota}
4 Množina #{unikátní prvky}

Povšimněte si, že u seznamů je nutné použít znak ' (quote), jinak by se zápis seznamu chápal jako volání funkce nebo vyhodnocení makra.

Poznámka: tyto kolekce jsou měnitelné, na rozdíl od Clojure.

Do této skupiny datových typů můžeme zařadit i n-tici (tuple), která se zapisuje takto:

(, prvky)

Jedná se o specialitu jazyka Hy a v dalších dialektech LISPu ji nenajdeme, i když se jedná o velmi užitečnou strukturu – viz též Lisp's Mysterious Tuple Problem.

Příklady:

; pomocné proměnné
(setv positionx 1)
(setv positionY 2)
(setv positionZ 3)
 
 
; Seznamy
 
; prázdný seznam
'()
 
; seznam čísel
'(1 2 3 4)
 
; seznam řetězců
'("prvni" "druhy" "treti")
 
; seznam "keywords"
'(:prvni :druhy :treti)
 
; seznam s proměnnými
'(positionX positionY positionZ)
 
; vnořené seznamy
'( '(:x :y) '(:z :w) )
 
 
; Vektory
 
; prázdný vektor
[]
 
; vektor čísel
[1 2 3 4]
 
; vektor řetězců
["prvni" "druhy" "treti"]
 
; vektor "keywords"
[:prvni :druhy :treti]
 
; vektor proměnných
[positionX positionY positionZ]
 
 
; n-tice
 
; prázdná n-tice
(,)
 
; n-tice s čísly
(, 1 2 3 4)
 
; n-tice řetězců
(, "prvni" "druhy" "treti")
 
; n-tice "keywords"
(, :prvni :druhy :treti)
 
 
; Mapa
 
; prázdná mapa
{}
 
; mapování typu string-string
{"prvni" "first" "druhy" "second" "treti" "third"}
 
; mapa s vyhodnocením proměnných
{"X" positionX "y" positionY "z" positionZ}
 
 
; Množina
#{"prvni" "druhy" "treti"}

9. Aritmetické operace a relační operátory

Zatímco v naprosté většině „mainstreamových“ programovacích jazyků, jakými jsou například Céčko, Java, JavaScript či Python, se aritmetické a logické výrazy zapisují v takzvané infixové notaci, při níž jsou binární operátory zapisovány mezi dvojici operandů, autor jazyka Hy (resp. přesněji řečeno již tvůrci LISPu) se od tohoto způsobu zápisu distancovali – namísto toho jsou v Hy i LISPu všechny základní aritmetické i logické (a samozřejmě též relační) operace zapisovány jako volání funkcí či speciálních forem, tj. vždy v prefixové podobě. Důvodů, proč byla zvolena tato forma zápisu výrazů, je více. Prvním důvodem je fakt, že syntaxe LISPu byla původně navrhována s ohledem na to, že později dojde k její změně, tj. samotná syntaxe nebyla pro tvůrce tohoto programovacího jazyka tak prioritní jako jeho sémantika (paradoxní přitom je, že se nakonec syntaxe LISPu nezměnila, protože takzvané M-výrazy se nedočkaly většího rozšíření, podobně jako další snahy o úpravu syntaxe LISPu tak, aby se eliminovalo množství závorek či právě prefixový zápis aritmetických výrazů).

Druhý důvod spočíval v tom, že zavedení infixových operátorů by do jazyka zavádělo zbytečné další komplikace: musely by se například řešit a přesně specifikovat priority operací (a u některých operací i jejich asociativita, viz například ** v Pythonu), se zapsanými výrazy by se složitěji prováděly různé symbolické manipulace (integrace, derivace, zjednodušování výrazů), infixové operátory by nebylo možné předávat jako parametry do jiných funkcí (apply, reduce) atd. Vzhledem k tomu, že aritmetické operátory jsou zapisovány jako volání funkcí, musí se znak či jméno příslušného operátoru uvádět ve vyhodnocovaném seznamu na prvním místě, podobně jako jméno jakékoli jiné funkce. Všechny dílčí podvýrazy se samozřejmě vyhodnocují dříve než celý výraz. Většina aritmetických funkcí není omezena pouze na dva parametry, což znamená, že je například možné zavoláním jedné funkce nazvané + sečíst i více než dvě numerické hodnoty.

Podívejme se na okomentované příklady:

; Test překladu aritmetických výrazů
 
 
; operace rozdílu - druhý argument funkce je odečten od prvního
(- 1 2)
 
; součet řady čísel
(+ 1 2 3 4 5 6 7 8 9 10)
 
; níže uvedený výraz v infixové notaci odpovídá: 1-2-3-4-5....-10:
(- 1 2 3 4 5 6 7 8 9 10)
 
; POZOR - závorky v LISPu nemají mnoho společného
; s vyjádřením priority aritmetických operací
; (nelze je použít tak volně jako například v céčku)
(* (+ 1 2) (+ 3 4))
 
(+ (* 1 2) (* 3 4))
 
; Hy umí, podobně jako některé implementace LISPu,
; pracovat se zlomky, tj. snaží se racionální
; čísla vyjádřit formou zlomku (ideální jazyk do škol)
; Hy se chová jinak - se zlomky sice pracuje, ale / přeloží do operátoru /
(/ 1 2)
 
(/ 1 2 3)
 
; zkusíme výpočet složitějšího zlomku
(/ (+ 1 2) (+ 3 4))
 
; dělení modulo
(% 10 3)
 
; 2^10
(** 2 10)
 
; neracionální (reálná) čísla se vypisují tak, jak to
; známe z ostatních programovacích jazyků (samozřejmě
; v případě speciálních požadavků programátora lze použít
; různé formátovací funkce na úpravu výstupu)
(* 0.3 (/ (+ 1 2) (+ 3 4)))
 
; namísto numerických hodnot lze použít i proměnné
(setv variableA 1)
(setv variableB 2)
(setv variableC 3)
(setv variableD 4)
 
(+ variableA variableB variableC variableD)
(* (+ variableA variableB) (+ variableC variableD))
(+ (* variableA variableB) (* variableC variableD))
(/ (+ variableA variableB) (+ variableC variableD))
 
(+= variableA 10)
(*= variableB 2)

Programovací jazyk Hy obsahuje i úplnou sadu relačních operátorů, které v závislosti na hodnotách předaných parametrů (operandů) vrací hodnotu True (pravda) či False (nepravda):

; Test překladu relačních výrazů
 
 
; porovnání dvou číselných hodnot
; relace "menší než"
(< 1 2)
 
; relace "větší než"
(> 1 2)
 
; relace "menší nebo rovno"
(<= 1 2)
 
; relace "větší nebo rovno"
(>= 1 2)
 
; porovnání dvou výrazů na ekvivalenci
(= 1 2)
(= 1 1)
 
; podvýrazy se nejprve vyhodnotí a posléze se porovnají
; vyhodnocené výsledky (v tomto případě dva atomy)
(= (+ 1 1) (/ 4 2) (** 2 10))

Namísto numerických hodnot lze použít i proměnné:

(setv variableA 1)
(setv variableB 2)
(setv variableC 3)
(setv variableD 4)
 
(< variableA variableB)
(> variableA variableB)
(<= variableA variableB)
(>= variableA variableB)
(= variableA variableB)
(= variableA variableA)
 
(= (+ variableA variableA) (/ variableC variableD))
 
(or (= variableA 10) (= variableB 20) (> variableC 0))
(and (> variableA 0) (< variableA 100))

10. Anonymní funkce

Pro vytvoření nové bezejmenné (tj. anonymní) funkce se používá speciální forma nazvaná fn, které se v tom nejjednodušším případě předá vektor obsahující jména parametrů, za nímž je uveden seznam, který představuje tělo funkce (znalci LISPu patrně znají formu lambda, která má podobný význam, ale zde být použita nemůže, protože se jedná o klíčové slovo Pythonu). Samozřejmě, že v těle funkce je možné použít symbolická jména jejích parametrů a návratovou hodnotou funkce je hodnota získaná vyhodnocením těla funkce. Speciální forma fn při použití ve smyčce REPL vypíše řetězec, který reprezentuje interní identifikátor funkce – jinými slovy na tento řetězec můžeme v naprosté většině případů zapomenout, protože se s ním přímo nepracuje. Ukažme si tedy způsob deklarace funkce se dvěma parametry pojmenovanými x a y, která vypočítá a vrátí součet těchto parametrů:

; anonymní funkce
(fn [x y] (+ x y))

Co se vlastně stalo? Vytvořili jsme novou funkci, která však nebyla přiřazena k žádnému symbolu (tj. nebyla „pojmenována“) ani jsme tuto funkci nikde nezavolali. Výše uvedený zápis je tedy prakticky stejně užitečný jako prosté zapsání jakékoli hodnoty nebo symbolu na vstup smyčky REPL. Pokud by se funkce měla zavolat, lze použít nám již známý zápis ve tvaru seznamu – již víme, že prvním parametrem vyhodnocovaného seznamu (není před ním apostrof!) je funkce a dalšími prvky pak parametry této funkce:

((fn [x y] (* x y)) 6 7)
42

Sice je pěkné, že jsme dokázali funkci zavolat s předáním parametrů, ovšem mnohdy (ne vždy!) je nutné funkci „pojmenovat“, přesněji řečeno ji přiřadit k symbolu. My vlastně již víme, jak se to dělá, protože funkce jsou hodnotami a pro přiřazení symbolu k hodnotě se používá speciální forma setv. Tudíž následující zápis je sice zdlouhavý, ale zcela korektní:

(setv multiply (fn [x y] (* x y)))
#'user/multiply

Předchozím příkazem jsme vytvořili novou funkci a navázali ji na symbol, tudíž došlo k jejímu pojmenování. Nyní je již možné funkci zavolat s využitím navázaného symbolu. Samozřejmě se zde opět využívá nám již známý zápis ve tvaru seznamu:

(multiply 6 7)
42

11. Pojmenované funkce

Vzhledem k tomu, že se uživatelské funkce v reálných programech vytváří a současně i pojmenovávají velmi často, vznikla potřeba nahradit zápis (sevv název (fn parametry (tělo))) něčím kratším, ideálně i s použitím menšího množství závorek. Pro tyto účely vzniklo makro se jménem defn, které se až na malé detaily podobá LISPovskému zápisu defun. Při použití makra defn se v tom nejjednodušším případě předávají tři parametry: název nově vytvářené funkce, vektor obsahující jména parametrů funkce a konečně seznam představující tělo této funkce. Naši funkci multiply tedy můžeme vytvořit a současně i pojmenovat následujícím způsobem:

(defn multiply [x y] (* x y))

A ihned ji můžeme použít:

(multiply 6 7)
42

Následují příklady dalších funkcí:

; Test překladu deklarace funkcí
 
; anonymní funkce
(fn [x y] (+ x y))
 
; funkce navázaná na symbol == pojmenovaná funkce
(defn add
    [x y]
    (+ x y))
 
; lokální symboly (proměnné)
(defn add-abs
    [x y]
    (setv abs-x (if (< x 0) (- x) x))
    (setv abs-y (if (< y 0) (- y) y))
    (+ abs-x abs-y))
 
(defn inc
    [num]
    (+ num 1))
 
(print (add-abs -10 -20))

12. Rekurze a tail rekurze

Zcela typickým příkladem rekurzivní funkce je funkce pro výpočet faktoriálu, jejíž jednoduchá varianta (neochráněná před všemi typy vstupů) může vypadat takto:

; rekurzivní výpočet faktoriálu
 
(defn factorial
    [n]
    (if (<= n 1)
        1
        (* n (factorial (- n 1)))))

Otestování je snadné:

(print (factorial 10))
3628800
 
(for [n (range 1 11)]
     (print n (factorial n)))
1 1
2 2
3 6
4 24
5 120
6 720
7 5040
8 40320
9 362880
10 3628800

Přílišnému nadšení nad tím, jak jednoduše nyní můžeme počítat faktoriál z libovolně velkého čísla, však nepodléhejme, protože například již pro 10000! dojde k nepříjemnému překvapení:

(factorial 10000)
 
RuntimeError: maximum recursion depth exceeded in comparison

Důvod, proč předchozí volání funkce factorial skončilo s chybou, spočívá v tom, že došlo k přeplnění zásobníku při rekurzivním volání. Na zásobník se totiž musí ukládat parametry předávané volané funkci a taktéž body návratu (zjednodušeně řečeno návratové adresy). Aby k přetečení zásobníku nedocházelo, můžeme naši funkci upravit tak, aby se využívalo takzvané tail rekurze. Velmi zjednodušeně řečeno je tail rekurze použita tehdy, pokud je posledním příkazem nějaké funkce příkaz pro rekurzivní volání té samé funkce. V tomto případě se nemusí na zásobník nic ukládat a namísto toho se prostě provede skok. V jazyku Hy, ale i v Clojure, se musí tail rekurze zapsat explicitně, což má své přednosti i zápory (podle mě převažují přednosti, protože již ze zápisu programu je zcela zřejmé, kdy k tail rekurzi skutečně dojde):

; rekurzivní výpočet faktoriálu - TCO
 
(require [hy.contrib.loop [loop]])
 
(defn factorial
    [n]
    (loop [[cnt n]
           [acc 1]]
        (if (zero? cnt)
             acc
             (recur (dec cnt) (* acc cnt)))))
 
(print (factorial 10))
 
(for [n (range 1 11)]
     (print n (factorial n)))

Oba dva příklady uvedené výše jsou však spíše školní. V praxi se faktoriál dá vypočítat s využitím funkce vyššího řádu reduce, která postupně aplikuje nějakou specifikovanou funkci se dvěma parametry (zde konkrétně funkci *) na hodnotu získanou ze sekvence a na hodnotu automaticky vytvořeného akumulátoru. Jen si musíme dát pozor na to, aby první prvek sekvence obsahoval jedničku a nikoli nulu:

; nerekurzivní výpočet faktoriálu
 
(defn factorial
    [n]
    (if (neg? n)
        (raise (ValueError "natural number expected"))
        (reduce * (range 1 (inc n)))))
 
(print (factorial 10))
 
(for [n (range 1 11)]
     (print n (factorial n)))
 
(print (factorial -10))

13. Jazyk Clojure-py

Druhým LISPovským programovacím jazykem, s nímž se dnes ve stručnosti seznámíme, je jazyk nazvaný Clojure-py. Soudě podle názvu by se mělo jednat o další variantu jazyka Clojure, ovšem projekt Clojure-py je v současnosti v dosti nestabilním stavu, takže například dochází k pádu VM (přesněji řečeno k pádu interpretru) atd. I z tohoto důvodu si o Clojure-py řekneme jen základní informace, protože pro praktické nasazení je mnohem lepší a především méně riskantní použít Hy.

14. Instalace jazyka clojure-py

Instalace interpretru Clojure-py může opět proběhnou s využitím nástroje pip, ovšem musíte si ověřit, že používáte pip pro Python 2. Instalaci provedeme pouze pro přihlášeného uživatele, ať si zbytečně nerozbijeme systém nestabilním balíčkem:

$ pip install --user clojure_py
 
Collecting clojure_py
  Using cached https://files.pythonhosted.org/packages/e9/d1/77ca45d549ee5879c615eb4431db4c94b4c90cb2be6705d652efcc08e02e/clojure_py-0.2.4.tar.gz
Installing collected packages: clojure-py
  Running setup.py install for clojure-py ... done
Successfully installed clojure-py-0.2.4
You are using pip version 9.0.1, however version 10.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

O tom, že jazyk Clojure-py již není aktivně vyvíjen, svědčí i chyba, která vznikne při snaze o instalaci tohoto balíčku pomocí pip3, tedy pro Python 3:

$ pip3 install --user clojure_py
 
Collecting clojure_py
  Downloading https://files.pythonhosted.org/packages/e9/d1/77ca45d549ee5879c615eb4431db4c94b4c90cb2be6705d652efcc08e02e/clojure_py-0.2.4.tar.gz (100kB)
    100% |████████████████████████████████| 102kB 995kB/s
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-build-asotz3jt/clojure-py/setup.py", line 10, in <module>
        from clojure.main import VERSION
      File "/tmp/pip-build-asotz3jt/clojure-py/clojure/__init__.py", line 2, in <module>
        import clojure.main
      File "/tmp/pip-build-asotz3jt/clojure-py/clojure/main.py", line 68
        print s, filename
              ^
    SyntaxError: Missing parentheses in call to 'print'. Did you mean print(print s, filename)?
 
    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-asotz3jt/clojure-py/

15. Spuštění interpretu a práce s interaktivní smyčkou REPL

Po (doufejme že úspěšné) instalaci si můžeme spustit interpret, resp. přesněji řečeno interaktivní smyčku REPL:

$ clojurepy
 
clojure-py 0.2.4
Python 2.7.14 (default, Dec 11 2017, 16:08:01)
[GCC 7.2.1 20170915 (Red Hat 7.2.1-2)]
user=>

A můžeme začít psát výrazy, které mají prakticky stejnou sémantiku, jako je tomu v Clojure:

user=> (println 42)
42
nil
 
; sekvence
user=> (def x (range 10))
#'user/x
 
user=> x
(0 1 2 3 4 5 6 7 8 9)
 
; nová sekvence s přidaným prvkem
user=> (conj x 10)
(10 0 1 2 3 4 5 6 7 8 9)

K dispozici je i nápověda k jednotlivým funkcím a makrům:

user=> (doc conj)
conj[oin]. Returns a new collection with the xs
    'added'. (conj nil item) returns (item).  The 'addition' may
    happen at different 'places' depending on the concrete type.
nil

Generátorová notace seznamu:

user=> (for [n (range 0 11)] (factorial n))
(1 1 2 6 24 120 720 5040 40320 362880 3628800)

Deklarace funkce pro výpočet faktoriálu:

user=> (defn factorial [n] (reduce * (range 1 (inc n))))
#'user/factorial

Otestování funkce pro výpočet faktoriálu:

user=> (factorial 42)
1405006117752879898543142606244511569936384000000000

16. Zpracování sekvencí a další základní mechanismy, na nichž je Clojure postaveno

Jen ve stručnosti si ukažme práci se sekvencemi a kolekcemi, které tvoří základní heterogenní datové struktury Clojure:

user=> [1 2 3]
[1 2 3]
user=> '(1 2 3)
(1 2 3)
user=> {:name "Eda" :surname "Wasserfall"}
{:surname "Wasserfall", :name "Eda"}
user=> (def seznam '(1 2 3 'a 'b "hello"))
#'user/seznam
 
user=> (def vektor [1 2 3 'a 'b "hello"])
#'user/vektor
 
user=> (def mnozina #{1 2 3})
#'user/mnozina
 
user=> (def vektor [1 2 3 'a 'b "hello"])
#'user/vektor
 
user=> (def mnozina #{1 2 3})
#'user/mnozina
 
user=> (empty? [1 2 3])
false
 
user=> (count [1 2 3])
3
 
user=> (def seznam '(1 2 3))
#'user/seznam
 
user=> (conj seznam 100)
(100 1 2 3)
 
user=> (def vektor [1 2 3])
#'user/vektor
 
user=> (conj vektor 100)
[1 2 3 100]
 
user=> (pop [1 2 3])
[1 2]
 
user=> (nth [1 2 3] 1)
2

Použití funkce vyššího řádu apply:

user => (doseq [n (range 1 11)] (println n (apply * (range 1 (inc n)))))
1 1
2 2
3 6
4 24
5 120
6 720
7 5040
8 40320
9 362880
10 3628800
nil

Použití funkce vyššího řádu reduce:

user => (doseq [n (range 1 11)] (println n (reduce * (range 1 (inc n)))))
1 1
2 2
3 6
4 24
5 120
6 720
7 5040
8 40320
9 362880
10 3628800
nil

Použití funkce vyššího řádu map:

user=> (def a (range 1 11))
#'user/a
user=> (->> a (map #(* % 2)) (reduce +))
110

17. Rozdílné chování oproti originálnímu jazyku Clojure

Některé funkce a datové typy Clojure nejsou v Clojure-py prozatím implementovány, takže relativně často dochází k chybám. Příkladem může být pokus o práci se zlomky:

user=> 3/7
Compiling 3/7
Traceback (most recent call last):
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/repl.py", line 117, in run_repl
    out = execute(line)
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/repl.py", line 67, in execute
    res = comp.compile(s)
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/compiler.py", line 1342, in compile
    + str(type(itm)), None)
CompilerException: Compiler Exception  don't know how to compile <class 'fractions.Fraction'>

Co je však mnohem horší – nefungují ani některé funkce pro zpracování sekvencí:

user=> (filter even? [1 2 3 4])
(2 4)
 
user=> (filter even? (map inc (range 10)))
(Traceback (most recent call last):
  File "/home/tester/.local/bin/clojurepy", line 11, in <module>
    load_entry_point('clojure-py==0.2.4', 'console_scripts', 'clojurepy')()
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/main.py", line 91, in main
    clojure.repl.run_repl(comp)
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/repl.py", line 123, in run_repl
    RT.printTo(out)
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/rt.py", line 328, in printTo
    protocols.writeAsReplString(obj, writer)
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/protocol.py", line 46, in __call__
    return getattr(x, self.attrname)(*args)
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/core.clj", line 796, in clojure.core/LazySeq_writeAsReplString
    (loop [s (.seq self)]
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/core.clj", line 0, in clojure.core/LazySeq_seq
    (loop [s (.seq self)]
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/core.clj", line 0, in clojure.core/LazySeq_sval
    (loop [s (.seq self)]
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/core.clj", line 1812, in clojure.core/filter304_auto_
    (when (pred (.nth c i))
AttributeError: 'ArrayChunk' object has no attribute 'nth'

Některé základní funkce z Clojure v Clojure-py nenajdeme:

user=> (into {} (for [x (range 1 10)] [x (* x x)]))
Compiling into
Compiling (into, {}, (for, [x, (range, 1, 10)], [x, (*, x, x)]))
Traceback (most recent call last):
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/repl.py", line 117, in run_repl
    out = execute(line)
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/repl.py", line 67, in execute
    res = comp.compile(s)
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/compiler.py", line 1315, in compile
    c.extend(self.compileForm(itm))
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/compiler.py", line 1226, in compileForm
    c = self.compile(form.first())
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/compiler.py", line 1313, in compile
    c.extend(self.compileSymbol(itm))
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/compiler.py", line 1284, in compileSymbol
    return self.compileAccessList(sym)
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/compiler.py", line 1241, in compileAccessList
    code = self.getAccessCode(sym)
  File "/home/tester/.local/lib/python2.7/site-packages/clojure/lang/compiler.py", line 1252, in getAccessCode
    " reference " + str(self.getNamesString(False)), None)
CompilerException: Compiler Exception could not resolve 'into', 'into' not found in user reference fn_1082
Poznámka: nedostatků a nedodělků je více, ale již ty výše zmíněné znamenají, že je Clojure-py vhodné možná pro výuku, ale ne pro produkční nasazení.

18. Repositář s demonstračními příklady

Zdrojové kódy všech dnes zmíněných demonstračních příkladů byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/lisps-for-python-vm. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, stále doslova několik kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:

19. Odkazy na předchozí části tohoto seriálu o jazyku Clojure

  1. Clojure 1: Úvod
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/
  2. Clojure 2: Symboly, kolekce atd.
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/
  3. Clojure 3: Funkcionální programování
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-3-cast-funkcionalni-programovani/
  4. Clojure 4: Kolekce, sekvence a lazy sekvence
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-4-cast-kolekce-sekvence-a-lazy-sekvence/
  5. Clojure 5: Sekvence, lazy sekvence a paralelní programy
    http://www.root.cz/clanky/clojure-a-bezpecne-aplikace-pro-jvm-sekvence-lazy-sekvence-a-paralelni-programy/
  6. Clojure 6: Podpora pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/
  7. Clojure 7: Další funkce pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/
  8. Clojure 8: Identity, stavy, neměnné hodnoty a reference
    http://www.root.cz/clanky/programovaci-jazyk-clojure-8-identity-stavy-nemenne-hodnoty-a-referencni-typy/
  9. Clojure 9: Validátory, pozorovatelé a kooperace s Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-9-validatory-pozorovatele-a-kooperace-mezi-clojure-a-javou/
  10. Clojure 10: Kooperace mezi Clojure a Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/
  11. Clojure 11: Generátorová notace seznamu/list comprehension
    http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/
  12. Clojure 12: Překlad programů z Clojure do bajtkódu JVM I:
    http://www.root.cz/clanky/programovaci-jazyk-clojure-12-preklad-programu-z-clojure-do-bajtkodu-jvm/
  13. Clojure 13: Překlad programů z Clojure do bajtkódu JVM II:
    http://www.root.cz/clanky/programovaci-jazyk-clojure-13-preklad-programu-z-clojure-do-bajtkodu-jvm-pokracovani/
  14. Clojure 14: Základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  15. Clojure 15: Tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  16. Clojure 16: Složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  17. Clojure 17: Využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/
  18. Clojure 18: Základní techniky optimalizace aplikací
    http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  19. Clojure 19: Vývojová prostředí pro Clojure
    http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/
  20. Clojure 20: Vývojová prostředí pro Clojure (Vimu s REPL)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-20-vyvojova-prostredi-pro-clojure-integrace-vimu-s-repl/
  21. Clojure 21: ClojureScript aneb překlad Clojure do JS
    http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/
  22. Leiningen: nástroj pro správu projektů napsaných v Clojure
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/
  23. Leiningen: nástroj pro správu projektů napsaných v Clojure (2)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-2/
  24. Leiningen: nástroj pro správu projektů napsaných v Clojure (3)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-3/
  25. Leiningen: nástroj pro správu projektů napsaných v Clojure (4)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-4/
  26. Leiningen: nástroj pro správu projektů napsaných v Clojure (5)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-5/
  27. Leiningen: nástroj pro správu projektů napsaných v Clojure (6)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-6/
  28. Programovací jazyk Clojure a databáze (1.část)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/
  29. Pluginy pro Leiningen
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/
  30. Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi/
  31. Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi-2/
  32. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk/
  33. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-2/
  34. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure/
  35. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (2)
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-2/
  36. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (3)
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-3/
  37. Programovací jazyk Clojure a práce s Gitem
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem/
  38. Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (dokončení)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-dokonceni/
  39. Programovací jazyk Clojure a práce s Gitem (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem-2/
  40. Programovací jazyk Clojure – triky při práci s řetězci
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-retezci/
  41. Programovací jazyk Clojure – triky při práci s kolekcemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-kolekcemi/
  42. Programovací jazyk Clojure – práce s mapami a množinami
    http://www.root.cz/clanky/programovaci-jazyk-clojure-prace-s-mapami-a-mnozinami/
  43. Programovací jazyk Clojure – základy zpracování XML
    http://www.root.cz/clanky/programovaci-jazyk-clojure-zaklady-zpracovani-xml/
  44. Programovací jazyk Clojure – testování s využitím knihovny Expectations
    http://www.root.cz/clanky/programovaci-jazyk-clojure-testovani-s-vyuzitim-knihovny-expectations/
  45. Programovací jazyk Clojure – některé užitečné triky použitelné (nejenom) v testech
    http://www.root.cz/clanky/programovaci-jazyk-clojure-nektere-uzitecne-triky-pouzitelne-nejenom-v-testech/
  46. Enlive – výkonný šablonovací systém pro jazyk Clojure
    http://www.root.cz/clanky/enlive-vykonny-sablonovaci-system-pro-jazyk-clojure/
  47. Nástroj Leiningen a programovací jazyk Clojure: tvorba vlastních knihoven pro veřejný repositář Clojars
    http://www.root.cz/clanky/nastroj-leiningen-a-programovaci-jazyk-clojure-tvorba-vlastnich-knihoven-pro-verejny-repositar-clojars/
  48. Novinky v Clojure verze 1.8.0
    http://www.root.cz/clanky/novinky-v-clojure-verze-1–8–0/
  49. Asynchronní programování v Clojure s využitím knihovny core.async
    http://www.root.cz/clanky/asynchronni-programovani-v-clojure-s-vyuzitim-knihovny-core-async/
  50. Asynchronní programování v Clojure s využitím knihovny core.async (pokračování)
    http://www.root.cz/clanky/asynchronni-programovani-v-clojure-s-vyuzitim-knihovny-core-async-pokracovani/
  51. Asynchronní programování v Clojure s využitím knihovny core.async (dokončení)
    http://www.root.cz/clanky/asynchronni-programovani-v-clojure-s-vyuzitim-knihovny-core-async-dokonceni/
  52. Vytváříme IRC bota v programovacím jazyce Clojure
    http://www.root.cz/clanky/vytvarime-irc-bota-v-programovacim-jazyce-clojure/
  53. Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
    https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/
  54. Multimetody v Clojure aneb polymorfismus bez použití OOP
    https://www.root.cz/clanky/multimetody-v-clojure-aneb-polymorfismus-bez-pouziti-oop/
  55. Práce s externími Java archivy v programovacím jazyku Clojure
    https://www.root.cz/clanky/prace-s-externimi-java-archivy-v-programovacim-jazyku-clojure/
  56. Pixie: lehký skriptovací jazyk s „kouzelnými“ schopnostmi
    https://www.root.cz/clanky/pixie-lehky-skriptovaci-jazyk-s-kouzelnymi-schopnostmi/
  57. Programovací jazyk Pixie: funkce ze základní knihovny a použití FFI
    https://www.root.cz/clanky/pro­gramovaci-jazyk-pixie-funkce-ze-zakladni-knihovny-a-pouziti-ffi/
  58. Novinky v Clojure verze 1.9.0
    https://www.root.cz/clanky/novinky-v-clojure-verze-1–9–0/
  59. Validace dat s využitím knihovny spec v Clojure 1.9.0
    https://www.root.cz/clanky/validace-dat-s-vyuzitim-knihovny-spec-v-clojure-1–9–0/
  60. Použití jazyka Gherkin při tvorbě testovacích scénářů pro aplikace psané v Clojure
    https://www.root.cz/clanky/pouziti-jazyka-gherkin-pri-tvorbe-testovacich-scenaru-pro-aplikace-psane-v-nbsp-clojure/
  61. Použití jazyka Gherkin při tvorbě testovacích scénářů pro aplikace psané v Clojure (2)
    https://www.root.cz/clanky/pouziti-jazyka-gherkin-pri-tvorbe-testovacich-scenaru-pro-aplikace-psane-v-nbsp-clojure-2/

20. Odkazy na Internetu

  1. Python becomes a platform
    https://khinsen.wordpress­.com/2012/03/15/python-becomes-a-platform/
  2. Python becomes a platform. Thoughts on the release of clojure-py
    https://news.ycombinator.com/i­tem?id=3708974
  3. SchemePy
    https://pypi.org/project/SchemePy/
  4. lispy
    https://pypi.org/project/lispy/
  5. Lython
    https://pypi.org/project/Lython/
  6. Lizpop
    https://pypi.org/project/lizpop/
  7. Budoucnost programovacích jazyků
    http://www.knesl.com/budoucnost-programovacich-jazyku
  8. LISP Prolog and Evolution
    http://blog.samibadawi.com/2013/05/lisp-prolog-and-evolution.html
  9. List of Lisp-family programming languages
    https://en.wikipedia.org/wi­ki/List_of_Lisp-family_programming_languages
  10. clojure_py na indexu PyPi
    https://pypi.python.org/py­pi/clojure_py
  11. PyClojure
    https://github.com/eigenhom­bre/PyClojure
  12. Hy na GitHubu
    https://github.com/hylang/hy
  13. Hy: The survival guide
    https://notes.pault.ag/hy-survival-guide/
  14. Hy běžící na monitoru terminálu společnosti Symbolics
    http://try-hy.appspot.com/
  15. Welcome to Hy’s documentation!
    http://docs.hylang.org/en/stable/
  16. Hy na PyPi
    https://pypi.org/project/hy/#des­cription
  17. Getting Hy on Python
    https://lwn.net/Articles/596626/
  18. Programming Can Be Fun with Hy
    https://opensourceforu.com/2014/02/pro­gramming-can-fun-hy/
  19. Přednáška o projektu Hy (pětiminutový lighttalk)
    http://blog.pault.ag/day/2013/04/02
  20. Hy (Wikipedia)
    https://en.wikipedia.org/wiki/Hy
  21. Clojure home page
    http://clojure.org/
  22. Clojure Sequences
    http://clojure.org/sequences
  23. Clojure Data Structures
    http://clojure.org/data_structures
  24. Clojure
    https://en.wikipedia.org/wiki/Clojure
  25. Clojars:
    https://clojars.org/
  26. Seznam knihoven na Clojars:
    https://clojars.org/projects
  27. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  28. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  29. 4Clojure
    http://www.4clojure.com/
  30. ClojureDoc (rozcestník s dokumentací jazyka Clojure)
    http://clojuredocs.org/
  31. SICP (The Structure and Interpretation of Computer Programs)
    http://mitpress.mit.edu/sicp/
  32. Pure function
    http://en.wikipedia.org/wi­ki/Pure_function
  33. Funkcionální programování
    http://cs.wikipedia.org/wi­ki/Funkcionální_programová­ní
  34. Čistě funkcionální (datové struktury, jazyky, programování)
    http://cs.wikipedia.org/wi­ki/Čistě_funkcionální
  35. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  36. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  37. Threading macro (dokumentace k jazyku Clojure)
    https://clojure.github.io/clo­jure/clojure.core-api.html#clojure.core/->
  38. Understanding the Clojure → macro
    http://blog.fogus.me/2009/09/04/un­derstanding-the-clojure-macro/
  39. Emacs LISP
    https://www.root.cz/clanky/historie-vyvoje-textovych-editoru-eine-zwei-emacs/#k08
  40. Programovací jazyk LISP a LISP machines
    https://www.root.cz/clanky/pro­gramovaci-jazyk-lisp-a-lisp-machines/
  41. Speciální formy, lambda výrazy a makra v programovacím jazyku LISP
    https://www.root.cz/clanky/specialni-formy-lambda-vyrazy-a-makra-v-programovacim-jazyku-lisp/
  42. Programovací jazyky používané (nejen) v SSSR (část 3 – LISP)
    https://www.root.cz/clanky/pro­gramovaci-jazyky-pouzivane-nejen-v-nbsp-sssr-cast-3-ndash-lisp/
Našli jste v článku chybu?