Hlavní navigace

Úpravy Emacsu s Emacs Lisp: možnosti nabízené knihovnou Dash

Pavel Tišnovský

V předposledním článku, který se bude zabývat výhradně Emacs Lispem a nikoli samotným Emacsem, si popíšeme některé možnosti poskytované knihovnou Dash. Ta nabízí programátorům užitečné funkce inspirované jazykem Clojure.

Doba čtení: 30 minut

11. Klasická funkce vyššího řádu -reduce

12. Další formy funkce -reduce

13. Získání mezivýsledků variant funkce -reduce

14. Speciální formy „reducerů“

15. Konstrukce seznamů pomocí -iterate

16. Threading makra podruhé

17. Další formy threading maker

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

19. Literatura

20. Odkazy na Internetu

1. Úpravy Emacsu s Emacs Lisp: možnosti nabízené knihovnou Dash

Velká část pondělního článku byla věnována popisu vlastností makra cl-loop, které bylo do Emacsu přeportováno z Common Lispu. Připomeňme si, že s využitím tohoto makra je možné rozšířit možnosti Emacs Lispu o různé varianty programových smyček, ať již se jedná o smyčky s počitadlem, smyčky určené pro průchod seznamy nebo vektory, smyčky umožňující výpočet a akumulaci hodnot z procházených seznamů (minimum, maximum, suma, …) či dokonce i o různé kombinace těchto typů programových smyček. Makro cl-loop může být užitečné například pro uživatele přecházející na Emacs Lisp z konvenčních programovacích jazyků, ovšem musíme přitom akceptovat fakt, že se v něm používá vlastní DSL (doménově specifický jazyk), který je určitým mixem mezi zápisem používaným v Lispech a zápisem ze strukturovaných jazyků.

Ovšem ve chvíli, kdy se makro cl-loop nebo i standardní speciální forma while používá například pro zpracování prvků v seznamech nebo vektorech, je vhodné se zamyslet nad tím, zda neexistují lepší způsoby řešení těchto úkolů. Existují totiž i alternativní způsoby nabízené například knihovnou pojmenovanou seq; přičemž jedno z možných – a nutno říci, že i velmi elegantních – řešení spočívá ve využití knihovny pojmenované dash. Tato knihovna nabízí makra a funkce vyššího řádu určené pro funkcionálně pojaté zpracování různých typů sekvencí. Jedná se například o filtraci prvků, postupnou redukci sekvencí, aplikaci dalších funkcí na prvky sekvence, rozdělení sekvence podle nějakého kritéria atd.

Většina funkcí, které v knihovně dash nalezneme, nijak nemodifikuje původní (zdrojovou sekvenci), takže jsou tyto operace nedestruktivní. Na druhou stranu je ovšem nutno poznamenat, že seznamy nejsou v Emacs Lispu neměnitelné (immutable), tak, jak je tomu v Clojure, takže je nutné při použití knihovny dash dávat pozor na to, jaké další funkce se volají. Jen pro ukázku si uveďme funkci, která „tiše“ modifikuje původní seznam a je tedy z tohoto pohledu destruktivní:

(setq l '(1 2 3))
(1 2 3)
 
l
(1 2 3)
 
(sort l '>)
(3 2 1)
 
l
(1)
Poznámka: naprostá většina dále popsaných funkcí má stejné jméno (až na pomlčku či dvě pomlčky na začátku jména), jako funkce ze standardní knihovny programovacího jazyka Clojure. Je zde ovšem jeden velmi důležitý rozdíl – zatímco v jazyku Clojure se typicky pracuje s potenciálně nekonečnými sekvencemi, které jsou vyhodnocovány líně (tj. až tehdy, kdy je hodnota nějakého prvku skutečně zapotřebí), je tomu v knihovně Dash jinak. V ní se totiž pracuje s běžnými seznamy, typicky bez líného vyhodnocování. To má dopad na parametry některých funkcí. Pravděpodobně nejvíce je to patrné u funkce iterate, která v Clojure vrací nekonečnou sekvenci, kdežto v knihovně Dash musíme specifikovat celkový počet prvků, které budou touto funkcí vygenerovány.

2. Funkce -filter

Důležitá poznámka: všechny demonstrační příklady si dnes budeme uvádět v interaktivní podobě, kde tučně zvýrazněný řádek znamená text zapsaný přímo programátorem (například do REPLu Emacs Lispu) a běžným písmem vypsaný řádek pak ukazuje výsledek vypsaný intepretrem Emacs Lispu. Na konci článku jsou uvedeny zdrojové kódy všech dnes popisovaných příkladů, takže si je můžete vyzkoušet buď přímo v Emacsu (ve scratch bufferu atd.) nebo s využitím příkazu emacs -script název-skriptu.el spouštěného z příkazové řádky.

První funkce, s níž se dnes seznámíme, se při zpracování seznamů používá velmi často. Tato funkce se jmenuje -filter a používá se pro vytvoření nového seznamu, který bude obsahovat jen ty prvky ze seznamu původního, které odpovídají nějakému predikátu (predikát je obecně funkce vracející pro svůj jediný vstup pravdivostní hodnotu). Původní seznam přitom není modifikován, takže je ho později možné použít i pro další účely. Ukažme si použití této funkce. Nejdříve musíme provést import všech potřebných modulů, tj. konkrétně knihovny dash a taktéž knihovny cl-lib, protože v ní najdeme použitelné predikáty:

(package-initialize)
(require 'dash)
(require 'cl-lib)

Dále vytvoříme vstupní seznam obsahující sekvenci celých čísel od –10 do 10 (včetně obou krajních hodnot), což samozřejmě není nic složitého:

(setq numbers (number-sequence -10 10))
 
(print numbers)
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Následně se můžeme pokusit vyfiltrovat všechny sudé hodnoty (důležité je zde slovo vyfiltrovat, nikoli odfiltrovat):

(print (-filter 'cl-evenp numbers))
 
(-10 -8 -6 -4 -2 0 2 4 6 8 10)

Podobně, pouze výměnou predikátu, získáme liché hodnoty:

(print (-filter 'cl-oddp numbers))
 
(-9 -7 -5 -3 -1 1 3 5 7 9)

Predikátem může být jakákoli funkce s jedním parametrem, která vrací pravdivostní hodnotu (nebo jinou hodnotu automaticky převedenou na t nebo nil):

(print (-filter (lambda (n) (zerop (% n 3))) numbers))
 
(-9 -6 -3 0 3 6 9)

Již minule jsme se seznámili s anaforickým makrem –filter umožňujícím zkrácený zápis anonymní funkce – zapisuje se jen její tělo, v němž se použije parametr it:

(print (--filter (zerop (% it 3)) numbers))
 
(-9 -6 -3 0 3 6 9)

Samozřejmě nám nic nebrání si vytvořit vlastní predikát s využitím pojmenované (neanonymní) funkce a následně tento predikát použít při filtraci:

(defun positive?
  (n)
  (> n 0))
 
(print (-filter 'positive? numbers))
 
(1 2 3 4 5 6 7 8 9 10)

Složitější predikát pro kladná a současně lichá čísla:

(print (-filter (lambda (n) (and (cl-oddp n) (positive? n))) numbers))
 
(1 3 5 7 9)

Další složitější predikát pro kladná nebo lichá čísla:

(print (-filter (lambda (n) (or (cl-oddp n) (positive? n))) numbers))
 
(-9 -7 -5 -3 -1 1 2 3 4 5 6 7 8 9 10)

3. Makro -remove

Výše popsaná funkce -filter sloužila k výběru prvků odpovídajících nějakému predikátu. Přesně opačně se chová funkce pojmenovaná -remove, protože zde predikát slouží k vyloučení prvků. Ve skutečnosti se ovšem (opět) vstupní seznam nemění, prvky jsou „odstraněny“ ze seznamu výsledného.

Ukázku použití této funkce začneme stejně, jako tomu bylo v předchozím příkladu. Import potřebných modulů a vytvoření seznamu se sekvencí celých čísel od –10 do 10 je naprosto shodné s příkladem uvedeným v předchozí kapitole, takže jen krátce:

(package-initialize)
(require 'dash)
(require 'cl-lib)
 
(setq numbers (number-sequence -10 10))
 
(print numbers)
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Vytvoření nového seznamu bez sudých prvků:

(print (-remove 'cl-evenp numbers))
 
(-9 -7 -5 -3 -1 1 3 5 7 9)

Vytvoření nového seznamu bez lichých prvků:

(print (-remove 'cl-oddp numbers))
 
(-10 -8 -6 -4 -2 0 2 4 6 8 10)

Vlastní predikát pro ignorování prvků dělitelných třemi:

(print (-remove (lambda (n) (zerop (% n 3))) numbers))
 
(-10 -8 -7 -5 -4 -2 -1 1 2 4 5 7 8 10)

Dtto, ovšem nyní s využitím anaforického makra –remove:

(print (--remove (zerop (% it 3)) numbers))
 
(-10 -8 -7 -5 -4 -2 -1 1 2 4 5 7 8 10)

Další varianta, tentokrát s vlastním predikátem:

(defun positive?
  (n)
  (> n 0))
 
(print (-remove 'positive? numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0)

Složitější predikát pro kladná a současně lichá čísla (ta jsou odstraněna):

(print (-remove (lambda (n) (and (cl-oddp n) (positive? n))) numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 2 4 6 8 10)

Další složitější predikát pro kladná nebo lichá čísla (ta jsou odstraněna):

(print (-remove (lambda (n) (or (cl-oddp n) (positive? n))) numbers))
 
(-10 -8 -6 -4 -2 0)

4. Odstranění konkrétních prvků pomocí -remove-item, -remove-at a -remove-at-indices

Poměrně užitečné jsou i další varianty funkce -remove. Význam těchto variant je vypsán v následující tabulce:

Funkce Význam
-remove-item vrátí nový seznam, v němž se nebudou nacházet prvky specifikované hodnoty
-remove-at vrátí nový seznam, v němž se nebude nacházet prvek, který byl původně na pozici n
-remove-at-indices vrátí nový seznam, v němž se nebudou nacházet prvky umístěné na pozicích specifikovaných v prvním seznamu
Poznámka: od této chvíle budu všude pro jednoduchost používat pro formy z knihovny dash označení „funkce“, i když ve skutečnosti jsou některé dále popsané formy realizovány makrem. Z pohledu běžného uživatele je to však jedno a navíc se může způsob konkrétní implementace v další verzi knihovny dash změnit.

Samozřejmě si opět ukážeme, jak se tyto funkce používají. Začneme již klasicky – prostou sekvencí celých čísel od –10 do 10:

(package-initialize)
(require 'dash)
(require 'cl-lib)
 
(setq numbers (number-sequence -10 10))
 
(print numbers)
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Vytvoření nového seznamu, v němž nebudou prvky s hodnotou 0:

(print (-remove-item 0 numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 1 2 3 4 5 6 7 8 9 10)

Vytvoření nového seznamu, v němž nebude prvek s hodnotou 42. Takový prvek neexistuje, takže získáme kopii seznamu původního:

(print (-remove-item 42 numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Pro další testy použijeme seznam s devíti čísly:

(setq numbers '(0 0 0 1 1 1 2 2 2))
 
(print numbers)
 
(0 0 0 1 1 1 2 2 2)

Vytvoření nového seznamu, v němž nebudou prvky s hodnotou 0:

(print (-remove-item 0 numbers))
 
(1 1 1 2 2 2)

Vytvoření nového seznamu, v němž nebudou prvky s hodnotou 1:

(print (-remove-item 1 numbers))
 
(0 0 0 2 2 2)

Vytvoření nového seznamu, v němž nebudou prvky s hodnotou 2:

(print (-remove-item 2 numbers))
 
(0 0 0 1 1 1)

Vytvoření seznamu bez prvku na první pozici:

(print (-remove-at 0 numbers))
 
(-9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Vytvoření seznamu bez prvku na druhé pozici:

(print (-remove-at 1 numbers))
 
(-10 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Vytvoření seznamu bez prvku na desáté pozici:

(print (-remove-at 10 numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 1 2 3 4 5 6 7 8 9 10)

Odstranění prvku s indexem 0:

(print (-remove-at-indices '(0) numbers))
 
(-9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Odstranění prvků s indexy 0, 1 a 2:

(print (-remove-at-indices '(0 1 2) numbers))
 
(-7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Odstranění prvků s indexy 0, 10 a 20:

(print (-remove-at-indices '(10 20 0) numbers))
 
(-9 -8 -7 -6 -5 -4 -3 -2 -1 1 2 3 4 5 6 7 8 9)

5. Odstranění prvních či posledních prvků odpovídajících predikátu ze seznamu

Další dvě užitečné funkce se jmenují -remove-first a -remove-last. Tyto funkce slouží k odstranění prvního resp. posledního prvku, který odpovídá nějakému predikátu. Tyto funkce sice vypadají nenápadně, ale díky nim lze z programového kódu odstranit poměrně velké množství složitějších programových smyček. Inicializace příkladu je shodná s příklady předchozími (import modulů, vytvoření sekvence celých čísel), takže si ji již nebudeme znovu uvádět. Původní číselná sekvence vypadá následovně:

(print numbers)
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Ukažme si vytvoření nového seznamu bez prvního prvku, který je sudým číslem. Čistě náhodou se jedná o zcela první prvek seznamu (car):

(print (-remove-first 'cl-evenp numbers))
 
(-9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Vytvoření nového seznamu bez prvního prvku, který je lichým číslem. V našem konkrétním případě se jedná o prvek s hodnotou –9, takže ta ve výsledném seznamu chybí:

(print (-remove-first 'cl-oddp numbers))
 
(-10 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Samozřejmě je možné si vyzkoušet i složitější příklad, například odstranění prvního prvku beze zbytku dělitelného třemi:

(print (-remove-first (lambda (n) (zerop (% n 3))) numbers))
 
(-10 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Dtto, ovšem s využitím formy, v níž není nutné explicitně zapisovat formu lambda:

(print (--remove-first (zerop (% it 3)) numbers))
 
(-10 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Odstranění prvního záporného prvku (-10):

(print (--remove-first (> 0 it) numbers))
 
(-9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Odstranění prvního kladného prvku (jedničky):

(print (--remove-first (< 0 it) numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 2 3 4 5 6 7 8 9 10)

Další příklady již ukazují funkci -remove-last:

(print numbers)
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Odstranění posledního sudého čísla (10):

(print (-remove-last 'cl-evenp numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9)

Odstranění posledního lichého čísla (9):

(print (-remove-last 'cl-oddp numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 10)

Odstranění posledního čísla dělitelného třemi (9):

(print (-remove-last (lambda (n) (zerop (% n 3))) numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 10)

Odstranění posledního čísla dělitelného třemi (9), podoba s anaforickým makrem:

(print (--remove-last (zerop (% it 3)) numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 10)

Odstranění posledního záporného prvku (-1):

(print (--remove-last (> 0 it) numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 0 1 2 3 4 5 6 7 8 9 10)

Odstranění posledního kladného prvku (10):

(print (--remove-last (< 0 it) numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9)

6. Získání podseznamu: take, take-last, drop, drop-last

Ve chvíli, kdy budeme potřebovat získat prvních n či posledních n prvků ze vstupního seznamu, použijeme funkce take a take-last. Vstupem nyní bude seznam obsahující celočíselné hodnoty 0 až 20:

(setq numbers (number-sequence 0 20))
 
(print numbers)
 
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)

Získání prvních pěti prvků:

(print (-take 5 numbers))
 
(0 1 2 3 4)

Získání prvních 100 prvků (nedojde k chybě, vrátí se celý kratší seznam):

(print (-take 100 numbers))
 
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)

Získání posledních pěti prvků:

(print (-take-last 5 numbers))
 
(16 17 18 19 20)

Získání posledních 100 prvků (taktéž nedojde k chybě, vrátí se celý kratší seznam):

(print (-take-last 100 numbers))
 
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)

Opakem jsou funkce -drop a -drop-last vracející původní seznam bez prvních či posledních n prvků. Vstupem bude stejný seznam jako v předchozích příkladech:

(setq numbers (number-sequence 0 20))
 
(print numbers)
 
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)

Kopie seznamu bez prvních pěti prvků:

(print (-drop 5 numbers))
 
(5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)

Zajímavá situace – zde se vrátí prázdný seznam (odstraněny jsou všechny prvky):

(print (-drop 100 numbers))
 
nil

Kopie seznamu bez posledních pěti prvků:

(print (-drop-last 5 numbers))
 
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)

Podobná situace, jako v příkladu s -drop 100:

(print (-drop-last 100 numbers))
 
nil
Poznámka: díky těmto funkcím se vyhneme nepříjemnému počítání prvků, získávání délky seznamu atd.

7. Získání sekvence prvků ze seznamu na základě podmínky: take-while a drop-while

I další dvojice funkcí nazvaných -take-while a -drop-while je v praxi velmi užitečná, protože díky nim můžeme nahradit další typ potenciálně složitějších programových smyček. Funkce -take-while prochází seznamem a pokud platí podmínka (predikát), tvoří z jednotlivých prvků seznam výstupní. Jakmile podmínka platit přestane, je funkce ukončena a celý doposud vytvořený seznam se vrátí. Naproti tomu se u funkce -drop-while postupuje pochopitelně obráceně: najde se první prvek, pro nějž je predikát nepravdivý a následně se vrátí seznam obsahující zbytek seznamu (od tohoto prvku do konce).

V příkladech se vrátíme k původní sekvenci celých čísel od –10 do 10:

(setq numbers (number-sequence -10 10))
 
(print numbers)
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Získání prvních prvků menších než 0, tato hodnota způsobí ukončení generování seznamu:

(print (-take-while (lambda (n) (< n 0)) numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1)

Dtto, ovšem nyní s využitím anaforického makra –take-while:

(print (--take-while (< it 0) numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1)

V dalším příkladu již první prvek neodpovídá podmínce, takže výsledkem je prázdný seznam, který je v Emacs Lispu totožný s nil:

(print (--take-while (> it 0) numbers))
 
nil

Predikát t je vždy pravdivý a získáme tak kopii seznamu:

(print (--take-while t numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Predikát přestane být splněn pro prvek 0, takže se vrátí seznam od tohoto prvku do konce:

(print (-drop-while (lambda (n) (< n 0)) numbers))
 
(0 1 2 3 4 5 6 7 8 9 10)

Dtto, ovšem nyní s využitím anaforického makra –drop-while:

(print (--drop-while (< it 0) numbers))
 
(0 1 2 3 4 5 6 7 8 9 10)

Zde není predikát splněn ani pro první prvek – vrátí se kopie seznamu:

(print (--drop-while (> it 0) numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

Predikát t je vždy pravdivý a získáme tak prázdný seznam (všechny prvky jsou odstraněny):

(print (--drop-while t numbers))
 
nil

Opak předchozího predikátu – získáme zde kopii původního seznamu:

(print (--drop-while nil numbers))
 
(-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10)

8. Funkce -concat a -flatten

Funkce -concat se používá pro spojení většího množství seznamů, přičemž prvky seznamů jsou na stejné úrovni, na rozdíl od standardní formy cons. Funkci si otestujeme na třech seznamech:

(setq l1 '(1 2 3))
(setq l2 '(3 4 5))
(setq l3 '(7 8 9))

Výsledek by měl vypadat následovně:

(print (-concat l1 l2 l3))
 
(1 2 3 3 4 5 7 8 9)

Pokud seznamy obsahují vnořené podseznamy, je tato struktura ve výsledku zachována:

(setq l1 '(1 (2 3)))
(setq l2 '((3 4) 5))
(setq l3 '(7 (8) 9))

Výsledek spojení těchto tří seznamů:

(print (-concat l1 l2 l3))
 
(1 (2 3) (3 4) 5 7 (8) 9)

Funkce -flatten vrátí původní seznam, ovšem ve „zploštělé“ podobě, v níž budou prvky všech podseznamů postupně vložené do jednorozměrného lineárně vázaného seznamu. Význam této funkce je patrný z následující trojice příkladů:

(print (-flatten '(1 2 3 4 5)))
 
(1 2 3 4 5)
(print (-flatten '(1 (2 3) (4 5))))
 
(1 2 3 4 5)
(print (-flatten '(1 (2 (3 (4 (5)))))))
 
(1 2 3 4 5)

9. Funkce -flatten-n

Zajímavější je funkce pojmenovaná -flatten-n, která taktéž konvertuje vstupní datovou strukturu do její „zploštělé“ podoby, ovšem jen do specifikované úrovně. Pro již zploštělý seznam získáme jeho kopii:

(dotimes (n 3)
  (print (-flatten-n n '(1 2 3 4 5))))
 
(1 2 3 4 5)
(1 2 3 4 5)
(1 2 3 4 5)

Pro datovou strukturu s pouhými dvěma úrovněmi rekurze je chování následující:

(dotimes (n 3)
  (print (-flatten-n n '(1 (2 3) (4 5)))))
 
(1 (2 3) (4 5))
(1 2 3 4 5)
(1 2 3 4 5)

A konečně si ukažme chování na datové struktuře s pěti rekurzivními úrovněmi:

(dotimes (n 7)
  (print (-flatten-n n '(1 (2 (3 (4 (5))))))))
 
(1 (2 (3 (4 (5)))))
(1 2 (3 (4 (5))))
(1 2 3 (4 (5)))
(1 2 3 4 (5))
(1 2 3 4 5)
(1 2 3 4 5)
(1 2 3 4 5)
Poznámka: můžeme vidět, že další „zplošťování“ lineárního seznamu již nemá žádný vliv, ovšem nejedná se o chybu.

10. Redukce seznamů s akumulací výsledné hodnoty

Další velmi užitečnou funkcí vyššího řádu je funkce nazvaná -reduce a její různé varianty. Při použití těchto funkcí dochází k postupné redukci prvků uložených ve vstupní sekvenci, a to (postupnou) aplikací zvolené uživatelské funkce na jednotlivé prvky a po krocích počítaný mezivýsledek, jenž se většinou nazývá akumulátor. Příklad výpočtu faktoriálu deseti postupnou redukcí seznamu (1 2 3 4 5 6 7 8 9 10):

(-reduce (lambda (n acc) (* n acc)) (number-sequence 1 10))
 
3628800

Ve skutečnosti existuje několik podobně koncipovaných funkcí, které se liší například tím, zda je seznam zpracovávána od svého začátku nebo od konce. K dispozici jsou i funkce vracející všechny postupně tvořené mezivýsledky.

11. Klasická funkce vyššího řádu -reduce

Základní formou „reduceru“ je funkce nazvaná jednoduše -reduce. Její možnosti si vyzkoušíme na nám již dobře známé sekvenci celých čísel, tentokrát v rozsahu 0 až 10 (což se bude dobře počítat):

(setq numbers (number-sequence 0 10))
 
(print numbers)
 
(0 1 2 3 4 5 6 7 8 9 10)

Pokud této funkci vyššího řádu předáme funkci +, bude postupně aplikována na mezivýsledek a každý prvek seznamu. Prvním mezivýsledkem je první prvek vstupního seznamu (tedy v našem případě nula):

(print (-reduce '+ numbers))
 
55

Stejného výsledku, i když s větší námahou, dosáhneme použitím anonymní funkce. Této funkci se předává jak mezivýsledek (akumulátor), tak i n-tý prvek seznamu; seznamem se prochází od začátku:

(print (-reduce (lambda (acc n) (+ acc n)) numbers))
 
55

Funkce -reduce existuje i ve variantě anaforického makra umožňujícího zkrácený zápis (preferovaná forma, v níž jsou acc a it povinná jména):

(print (--reduce (+ acc it) numbers))
 
55

Relativně snadno můžeme akumulací získat prvek s minimální hodnotou:

(print (--reduce (min acc it) numbers))
 
0

Popř. prvek s hodnotou maximální:

(print (--reduce (max acc it) numbers))
 
10

12. Další formy funkce -reduce

Existují i další formy funkce -reduce. Relativně často se můžeme setkat s použitím funkce nazvaná -reduce-r, která seznam taktéž postupně redukuje, ale od konce. Pozor si musíme dát na to, že je prohozeno i pořadí argumentů předávaných do redukční funkce, což nás ovšem nemusí zajímat ve chvíli, kdy použijeme anaforické makro. Opět začněme sekvencí celých čísel:

(setq numbers (number-sequence 0 10))
 
(print numbers)
 
(0 1 2 3 4 5 6 7 8 9 10)

U součtu (dvou prvků) je jedno, z jaké strany je seznam zpracováván:

(print (-reduce-r '+ numbers))
 
55

Použití explicitně zapsané anonymní funkce:

(print (-reduce-r (lambda (n acc) (+ acc n)) numbers))
 
55

Samozřejmě můžeme použít anaforické makro a nezajímat se o pořadí předávaných argumentů:

(print (--reduce-r (+ acc it) numbers))
 
55

Postupné zjištění prvku s minimální hodnotou:

(print (--reduce-r (min acc it) numbers))
 
0

Postupné zjištění prvku s maximální hodnotou:

(print (--reduce-r (max acc it) numbers))
 
10

Druhá mocnina prvku, jehož čtverec má maximální hodnotu:

(print (--reduce (max acc (* it it)) numbers))
 
100

Pokud seznamem procházíme odzadu, získáme odlišný výsledek (poslední prvek se přímo stane počáteční hodnotou akumulátoru):

(print (--reduce-r (max acc (* it it)) numbers))
 
81

Podívejme se nyní na viditelnější rozdíl mezi -reduce a -reduce-r. Vytvoříme si seznam s řetězci „0“, „1“ až „10“:

(setq numbers (->> (number-sequence 0 10)
                   (--map (format "%s" it))))

Test, jak seznam skutečně vypadá:

(print numbers)
 
("0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "10")

Nyní pomocí concat a –reduce vytvoříme nový řetězec s ciframi:

(print (--reduce (concat acc "-" it) numbers))
 
"0-1-2-3-4-5-6-7-8-9-10"

Pro –reduce-r samozřejmě získáme řetězec, kde jsou čísla v sestupné řadě:

(print (--reduce-r (concat acc "-" it) numbers))
 
"10-9-8-7-6-5-4-3-2-1-0"

Další formou je funkce –reduce-from, u níž se explicitně stanoví počáteční hodnota akumulátoru (nepůjde tedy o první prvek):

(print (--reduce-from (concat acc "-" it) "START" numbers))
 
"START-0-1-2-3-4-5-6-7-8-9-10"

Samozřejmě můžeme zkombinovat vlastnosti -reduce-r a -reduce-from realizované ve funkci -reduce-r-from a v anaforickém makru –reduce-r-from:

(print (--reduce-r-from (concat acc "-" it) "START" numbers)
 
"START-10-9-8-7-6-5-4-3-2-1-0"

13. Získání mezivýsledků variant funkce -reduce

V některých situacích se nám mohou hodit i mezivýsledky, které jsou postupně funkcemi -reduce nebo -reduce-r získávány. K tomu slouží další funkce nazvané -reductions, -reductions-r, -reductions-from a konečně -reductions-r-from. Chování těchto funkcí bude ukázáno na dalších příkladech; základem bude (opět) sekvence řetězců „0“, „1“, „2“ atd.:

(setq numbers (->> (number-sequence 0 10)
                   (--map (format "%s" it))))

Test, jak seznam skutečně vypadá:

(print numbers)
 
("0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "10")

Dále si vytvoříme pomocnou funkci nazvanou cc, která získá hodnotu akumulátoru a n-tého prvku. Výsledkem bude řetězec získaný spojením obou hodnot znakem „-“:

(defun cc
  (acc it)
  (concat acc "-" it))

Dtto pro varianty „-r“:

(defun ccr
  (it acc)
  (concat acc "-" it))

Řetězce získávané postupným voláním -reduce:

(print (-reductions 'cc numbers))
 
("0" "0-1" "0-1-2" "0-1-2-3" "0-1-2-3-4" "0-1-2-3-4-5" "0-1-2-3-4-5-6" "0-1-2-3-4-5-6-7" "0-1-2-3-4-5-6-7-8" "0-1-2-3-4-5-6-7-8-9" "0-1-2-3-4-5-6-7-8-9-10")

Řetězce získávané postupným voláním -reduce-r (pozor na pořadí, zde se vrací prvky tak, jak se rozbaluje rekurzivní zanoření):

(print (-reductions-r 'ccr numbers))
 
("10-9-8-7-6-5-4-3-2-1-0" "10-9-8-7-6-5-4-3-2-1" "10-9-8-7-6-5-4-3-2" "10-9-8-7-6-5-4-3" "10-9-8-7-6-5-4" "10-9-8-7-6-5" "10-9-8-7-6" "10-9-8-7" "10-9-8" "10-9" "10")

Varianta s „-from“, na které je patrné, že se skutečně nezačíná prvním prvkem seznamu, ale specifikovanou hodnotou:

(print (-reductions-from 'cc "START" numbers))
 
("START" "START-0" "START-0-1" "START-0-1-2" "START-0-1-2-3" "START-0-1-2-3-4" "START-0-1-2-3-4-5" "START-0-1-2-3-4-5-6" "START-0-1-2-3-4-5-6-7" "START-0-1-2-3-4-5-6-7-8" "START-0-1-2-3-4-5-6-7-8-9" "START-0-1-2-3-4-5-6-7-8-9-10")

Kombinace předchozích dvou příkladů:

(print (-reductions-r-from 'ccr "START" numbers))
 
("START-10-9-8-7-6-5-4-3-2-1-0" "START-10-9-8-7-6-5-4-3-2-1" "START-10-9-8-7-6-5-4-3-2" "START-10-9-8-7-6-5-4-3" "START-10-9-8-7-6-5-4" "START-10-9-8-7-6-5" "START-10-9-8-7-6" "START-10-9-8-7" "START-10-9-8" "START-10-9" "START-10" "START")
Poznámka: u „-r“ variant nesmíme zapomenout na otočení parametrů.

14. Speciální formy „reducerů“

V knihovně dash nalezneme i speciální formy reducerů, které slouží ke specifickým účelům. I tyto formy si ukážeme na několika příkladech. Vstupem bude nám již známý seznam s celými čísly, tentokrát začneme od jedničky:

(setq numbers (number-sequence 1 10))
 
(print numbers)
 
(1 2 3 4 5 6 7 8 9 10)

Reducer pro výpočet sumy všech prvků nemusíme složitě implementovat, protože již existuje:

(print (-sum numbers))
 
55

Podobně lze získat součin všech prvků (proto hodnoty začínaly od jedničky a nikoli od nuly):

(print (-product numbers))
 
3628800

Nalezení prvku s minimální hodnotou:

(print (-min numbers))
 
1

Nalezení prvku s maximální hodnotou:

(print (-max numbers))
 
10

Další příklady ukážou použití reducerů -min-by a -max-by, které také hledají minimální a maximální prvky, ovšem s využitím uživatelsky definovaného komparátoru.

Vstup:

(setq words '("Lorem" "ipsum" "dolor" "sit" "amet", "consectetur" "adipiscing" "elit", "sed" "do" "eiusmod" "tempor"))
 
(print words)
 
("Lorem" "ipsum" "dolor" "sit" "amet" (\, "consectetur") "adipiscing" "elit" (\, "sed") "do" "eiusmod" "tempor")

Nalezení nejkratšího slova:

(print (--min-by (> (length it) (length other)) words))
 
"do"

Nalezení nejdelšího slova:

(print (--max-by (> (length it) (length other)) words))
 
"adipiscing"
Poznámka: předchozí operace jsou samozřejmě rychlejší, než snaha o seřazení slov podle délky a posléze výběr prvního nebo posledního prvku (zde navíc nebude zaručena stabilita).

15. Konstrukce seznamů pomocí -iterate

Velmi užitečnou funkcí je funkce -iterate, která slouží ke konstrukci seznamů obsahujících hodnoty vygenerované programově. Funkci -iterace se předávají tři parametry: takzvaná generující funkce, počáteční hodnota a celkový počet hodnot. Výsledný seznam obsahuje ve svém prvním prvku počáteční hodnotu. Generující funkce je nejprve volána s touto počáteční hodnotou a výsledek je opět vložen do generovaného seznamu. Potom je tatáž funkce opět volána, ovšem je jí předána dříve vygenerovaná hodnota atd. atd. Tímto způsobem lze programově vytvořit například některé aritmetické či geometrické řady apod.

Generující funkce vrací svůj vstup, výsledkem bude řada stejných čísel:

(print (-iterate (lambda (it) it) 0 10))
 
(0 0 0 0 0 0 0 0 0 0)

Stejný příklad, ovšem používající anaforické makro:

(print (--iterate it 0 10))
 
(0 0 0 0 0 0 0 0 0 0)

Generující funkce vrací původní hodnotu zvýšenou o jedničku; výsledkem je aritmetická řada:

(print (-iterate (lambda (it) (+ 1 it)) 0 10))
 
(0 1 2 3 4 5 6 7 8 9)

Stejný příklad, ovšem používající anaforické makro:

(print (--iterate (+ 1 it) 0 10))
 
(0 1 2 3 4 5 6 7 8 9)

Vytvoření řady s mocninami dvojky (musí se začínat hodnotou 1, ne 0):

(print (-iterate (lambda (it) (* 2 it)) 1 10))
 
(1 2 4 8 16 32 64 128 256 512)

Stejný příklad, ovšem používající anaforické makro:

(print (--iterate (* 2 it) 1 10))
 
(1 2 4 8 16 32 64 128 256 512)

Exponenciální řada s přetečením výsledků :-)

(print (-iterate (lambda (it) (* it it)) 2 10))
 
(2 4 16 256 65536 4294967296 0 0 0 0)

Stejný příklad, ovšem opět používající anaforické makro:

(print (--iterate (* it it) 2 10))
 
(2 4 16 256 65536 4294967296 0 0 0 0)
Poznámka: forma -iterate má v Clojure odlišné chování, protože generuje nekonečnou líně vyhodnocovanou sekvenci. V Emacs Lispu je tomu jinak, protože zde musíme dopředu zadat počet vygenerovaných hodnot.

16. Threading makra podruhé

O takzvaných threading makrech jsme se ve stručnosti zmínili již v předchozím článku, ovšem jedná se o tak užitečná makra, že si zaslouží poněkud delší popis a uvedení většího množství demonstračních příkladů. Víme již, že základní threading makro zapisované znaky → se používá ve chvíli, kdy potřebujeme nahradit relativně málo čitelný zápis:

(a (b (c x)))

za zápis:

(-> x c b a)

který můžeme číst „hodnotu x předej funkci c, návratovou hodnotu funkce c předej do funkce b a konečně návratovou hodnotu funkce b předej do funkce a. Výsledná hodnota této funkce bude současně i výsledkem celého threading makra“

Ve skutečnosti nejsme nijak omezeni celkovým počtem funkcí, které jsou do makra předány; jediné omezení spočívá v tom, že výsledek předchozí funkce se předá do funkce další na prvním místě. Podívejme se na jednoduchý příklad, v němž jsou použity pouze funkce s jedním vstupem (a samozřejmě s jedním výstupem):

(print
    (-> '(1 (2 3) 4)
        -flatten
        -sum
        -))

Předchozí forma je čitelnější, než původní „lispovský“ zápis:

(print (- (-sum (-flatten '(1 (2 3) 4)))))

Funkce mohou mít i vedlejší efekt, protože vše, co threading makro provádí, je „pouhá“ reorganizace zdrojového kódu.

(-> (number-sequence 0 30)
    length
    print)

S výsledkem:

31

Popř. poněkud složitější příklad, v němž funkci + předáme jako první parametr výsledek volání funkce length:

(-> (number-sequence 0 30)
    length
    (+ 1)
    print)

S výsledkem:

32

Na předchozím příkladu je nejdůležitější pochopit význam třetího řádku:

    (+ 1)

Ve skutečnosti totiž threading makro předá výsledek předchozí funkce (ať je jakýkoli) ihned za volání +, takže se ve skutečnosti provede:

    (+ previous_result 1)

Zkusme si ještě nepatrně složitější příklad s funkcemi akceptujícími větší počet parametrů:

(-> '(1 (2 (3 (4 5))))
    -flatten
    -sum
    (+ 1)
    (* 2 2)
    print)

17. Další formy threading maker

U mnoha výše popsaných funkcí a maker se seznam předává na posledním místě (poslední parametr). Proto většinou není možné použít výše uvedené threading makro ->, ale makro nazvané ->>:

(->> '(1 2 3 4 5 6 7 8)
     (--filter (< it 5))
     -sum
     print)

popř.:

(->> '(1 2 3 4 5 6 7 8)
     (--filter (< it 5))
     (--map (* 2 it))
    -sum
     print)

V posledním (nejsložitějším) příkladu je použito makro -->, které umožňuje postupné probublávání mezivýsledků, podobně, jako je tomu u maker předchozích. Ovšem mezivýsledky jsou vkládány na místo, kde je použit symbol it. Pozor ovšem na to, že v následujícím příkladu má it dva významy a threading makru patří pouze zvýrazněné symboly:

MIF18 tip v článku témata

(--> '(1 2 3 4 5 6 7 8)
     (--map (format "%s" it) it)
     (--reduce (concat acc "-" it) it)
     (concat "*" it "*")
     print)

Výsledek:

"*1-2-3-4-5-6-7-8*"
Poznámka: všimněte si, že zde nedochází k žádným problémům při vnořování maker.

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

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

# Demonstrační příklad Popis Cesta
1 01_dash_filters.el filtr realizovaný funkcí -filter nebo makrem –filter https://github.com/tisnik/elisp-examples/blob/master/elisp-4/01_dash_filters.el
2 02_dash_remove.el použití základní funkce -remove https://github.com/tisnik/elisp-examples/blob/master/elisp-4/02_dash_remove.el
3 03_dash_remove_item_etc.el funkce -remove-item a -remove-at https://github.com/tisnik/elisp-examples/blob/master/elisp-4/03_dash_remove_item_etc.el
4 04_dash_remove_first.el použití funkce -remove-first https://github.com/tisnik/elisp-examples/blob/master/elisp-4/04_dash_remove_first.el
5 05_dash_remove_last.el použití funkce -remove-last https://github.com/tisnik/elisp-examples/blob/master/elisp-4/05_dash_remove_last.el
6 06_dash_take.el použití funkce -take https://github.com/tisnik/elisp-examples/blob/master/elisp-4/06_dash_take.el
7 07_dash_drop.el použití funkce -drop https://github.com/tisnik/elisp-examples/blob/master/elisp-4/07_dash_drop.el
8 08_dash_take_while.el použití funkce -take-while https://github.com/tisnik/elisp-examples/blob/master/elisp-4/08_dash_take_while.el
9 09_dash_drop_while.el použití funkce -drop-while https://github.com/tisnik/elisp-examples/blob/master/elisp-4/09_dash_drop_while.el
10 10_dash_concat.el spojení seznamů a prvků https://github.com/tisnik/elisp-examples/blob/master/elisp-4/10_dash_concat.el
11 11_dash_flatten.el vytvoření „plochého“ seznamu https://github.com/tisnik/elisp-examples/blob/master/elisp-4/11_dash_flatten.el
12 12_dash_flattenn.el vytvoření „plochého“ seznamu n-té úrovně https://github.com/tisnik/elisp-examples/blob/master/elisp-4/12_dash_flattenn.el
13 13_dash_reduce.el funkce -reduce https://github.com/tisnik/elisp-examples/blob/master/elisp-4/13_dash_reduce.el
14 14_dash_reduce-r.el další varianty reducerů https://github.com/tisnik/elisp-examples/blob/master/elisp-4/14_dash_reduce-r.el
15 15_dash_reduce-from.el další varianty reducerů https://github.com/tisnik/elisp-examples/blob/master/elisp-4/15_dash_reduce-from.el
16 16_dash_reductions.el získání produktů vytvářených reducery https://github.com/tisnik/elisp-examples/blob/master/elisp-4/16_dash_reductions.el
17 17_dash_special_reducers.el speciální formy reducerů https://github.com/tisnik/elisp-examples/blob/master/elisp-4/17_dash_special_reducers.el
18 18_dash_iterate.el forma iterate https://github.com/tisnik/elisp-examples/blob/master/elisp-4/18_dash_iterate.el
19 19_threading_macros.el ukázky threading maker https://github.com/tisnik/elisp-examples/blob/master/elisp-4/19_threading_macros.el

19. Literatura

  1. Tutorial for the Common Lisp Loop Macro
    http://www.ai.sri.com/pkarp/loop.html
  2. Common Lisp's Loop Macro Examples for Beginners
    http://www.unixuser.org/~e­uske/doc/cl/loop.html
  3. A modern list api for Emacs. No 'cl required.
    https://github.com/magnars/dash.el
  4. The LOOP Facility
    http://www.lispworks.com/do­cumentation/HyperSpec/Body/06_a­.htm
  5. McCarthy
    „Recursive functions of symbolic expressions and their computation by machine, part I“
    1960
  6. Guy L. Steele
    „History of Scheme“
    2006, Sun Microsystems Laboratories
  7. Kolář J., Muller K.:
    „Speciální programovací jazyky“
    Praha 1981
  8. „AutoLISP Release 9, Programmer's reference“
    Autodesk Ltd., October 1987
  9. „AutoLISP Release 10, Programmer's reference“
    Autodesk Ltd., September 1988
  10. McCarthy, John; Abrahams, Paul W.; Edwards, Daniel J.; Hart, Timothy P.; Levin, Michael I.
    „LISP 1.5 Programmer's Manual“
    MIT Press. ISBN 0 262 130 1 1 4
  11. Carl Hewitt; Peter Bishop and Richard Steiger
    „A Universal Modular Actor Formalism for Artificial Intelligence“
    1973
  12. Feiman, J.
    „The Gartner Programming Language Survey (October 2001)“
    Gartner Advisory
  13. Harold Abelson, Gerald Jay Sussman, Julie Sussman:
    Structure and Interpretation of Computer Programs
    MIT Press. 1985, 1996 (a možná vyšel i další přetisk)
  14. Paul Graham:
    On Lisp
    Prentice Hall, 1993
    Dostupné online na stránce http://www.paulgraham.com/on­lisptext.html
  15. David S. Touretzky
    Common LISP: A Gentle Introduction to Symbolic Computation (Dover Books on Engineering)
  16. Peter Norvig
    Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp

20. Odkazy na Internetu

  1. The mapcar Function (An Introduction to Programming in Emacs Lisp)
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­intr/mapcar.html
  2. Anaphoric macro
    https://en.wikipedia.org/wi­ki/Anaphoric_macro
  3. Some Common Lisp Loop Macro Examples
    https://www.youtube.com/wat­ch?v=3yl8o6r_omw
  4. A Guided Tour of Emacs
    https://www.gnu.org/softwa­re/emacs/tour/
  5. The Roots of Lisp
    http://www.paulgraham.com/ro­otsoflisp.html
  6. Evil (Emacs Wiki)
    https://www.emacswiki.org/emacs/Evil
  7. Evil (na GitHubu)
    https://github.com/emacs-evil/evil
  8. Evil (na stránkách repositáře MELPA)
    https://melpa.org/#/evil
  9. Evil Mode: How I Switched From VIM to Emacs
    https://blog.jakuba.net/2014/06/23/e­vil-mode-how-to-switch-from-vim-to-emacs.html
  10. GNU Emacs (home page)
    https://www.gnu.org/software/emacs/
  11. GNU Emacs (texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?GnuEmacs
  12. An Introduction To Using GDB Under Emacs
    http://tedlab.mit.edu/~dr/gdbin­tro.html
  13. An Introduction to Programming in Emacs Lisp
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­intr/index.html
  14. 27.6 Running Debuggers Under Emacs
    https://www.gnu.org/softwa­re/emacs/manual/html_node/e­macs/Debuggers.html
  15. GdbMode
    http://www.emacswiki.org/e­macs/GdbMode
  16. Emacs (Wikipedia)
    https://en.wikipedia.org/wiki/Emacs
  17. Emacs timeline
    http://www.jwz.org/doc/emacs-timeline.html
  18. Emacs Text Editors Family
    http://texteditors.org/cgi-bin/wiki.pl?EmacsFamily
  19. Vrapper aneb spojení možností Vimu a Eclipse
    https://mojefedora.cz/vrapper-aneb-spojeni-moznosti-vimu-a-eclipse/
  20. Vrapper aneb spojení možností Vimu a Eclipse (část 2: vyhledávání a nahrazování textu)
    https://mojefedora.cz/vrapper-aneb-spojeni-moznosti-vimu-a-eclipse-cast-2-vyhledavani-a-nahrazovani-textu/
  21. Emacs/Evil-mode – A basic reference to using evil mode in Emacs
    http://www.aakarshnair.com/posts/emacs-evil-mode-cheatsheet
  22. From Vim to Emacs+Evil chaotic migration guide
    https://juanjoalvarez.net/es/de­tail/2014/sep/19/vim-emacsevil-chaotic-migration-guide/
  23. Introduction to evil-mode {video)
    https://www.youtube.com/wat­ch?v=PeVQwYUxYEg
  24. EINE (Emacs Wiki)
    http://www.emacswiki.org/emacs/EINE
  25. EINE (Texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?EINE
  26. ZWEI (Emacs Wiki)
    http://www.emacswiki.org/emacs/ZWEI
  27. ZWEI (Texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?ZWEI
  28. Zmacs (Wikipedia)
    https://en.wikipedia.org/wiki/Zmacs
  29. Zmacs (Texteditors.org)
    http://texteditors.org/cgi-bin/wiki.pl?Zmacs
  30. TecoEmacs (Emacs Wiki)
    http://www.emacswiki.org/e­macs/TecoEmacs
  31. Micro Emacs
    http://www.emacswiki.org/e­macs/MicroEmacs
  32. Micro Emacs (Wikipedia)
    https://en.wikipedia.org/wi­ki/MicroEMACS
  33. EmacsHistory
    http://www.emacswiki.org/e­macs/EmacsHistory
  34. Seznam editorů s ovládáním podobným Emacsu či kompatibilních s příkazy Emacsu
    http://www.finseth.com/emacs.html
  35. evil-numbers
    https://github.com/cofi/evil-numbers
  36. Debuggery a jejich nadstavby v Linuxu (1.část)
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/
  37. Debuggery a jejich nadstavby v Linuxu (2.část)
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/
  38. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  39. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://fedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  40. Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
    https://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/
  41. Org mode
    https://orgmode.org/
  42. The Org Manual
    https://orgmode.org/manual/index.html
  43. Kakoune (modální textový editor)
    http://kakoune.org/
  44. Vim-style keybinding in Emacs/Evil-mode
    https://gist.github.com/tro­yp/6b4c9e1c8670200c04c16036805773d8
  45. Emacs – jak začít
    http://www.abclinuxu.cz/clan­ky/navody/emacs-jak-zacit
  46. Programovací jazyk LISP a LISP machines
    https://www.root.cz/clanky/pro­gramovaci-jazyk-lisp-a-lisp-machines/
  47. Evil-surround
    https://github.com/emacs-evil/evil-surround
  48. Spacemacs
    http://spacemacs.org/
  49. Lisp: Common Lisp, Racket, Clojure, Emacs Lisp
    http://hyperpolyglot.org/lisp
  50. Common Lisp, Scheme, Clojure, And Elisp Compared
    http://irreal.org/blog/?p=725
  51. Does Elisp Suck?
    http://irreal.org/blog/?p=675
  52. Emacs pro mírně pokročilé (9): Elisp
    https://www.root.cz/clanky/emacs-elisp/
  53. If I want to learn lisp, are emacs and elisp a good choice?
    https://www.reddit.com/r/e­macs/comments/2m141y/if_i_wan­t_to_learn_lisp_are_emacs_an­d_elisp_a/
  54. Clojure(Script) Interactive Development Environment that Rocks!
    https://github.com/clojure-emacs/cider
  55. An Introduction to Emacs Lisp
    https://harryrschwartz.com/2014/04/08/an-introduction-to-emacs-lisp.html
  56. Emergency Elisp
    http://steve-yegge.blogspot.com/2008/01/emergency-elisp.html
  57. Racket
    https://racket-lang.org/
  58. The Racket Manifesto
    http://felleisen.org/matthi­as/manifesto/
  59. MIT replaces Scheme with Python
    https://www.johndcook.com/blog/2009/03/26/mit-replaces-scheme-with-python/
  60. Adventures in Advanced Symbolic Programming
    http://groups.csail.mit.e­du/mac/users/gjs/6.945/
  61. Why MIT Switched from Scheme to Python (2009)
    https://news.ycombinator.com/i­tem?id=14167453
  62. Starodávná stránka XLispu
    http://www.xlisp.org/
  63. AutoLISP
    https://en.wikipedia.org/wi­ki/AutoLISP
  64. Seriál PicoLisp: minimalistický a výkonný interpret Lispu
    https://www.root.cz/serialy/picolisp-minimalisticky-a-vykonny-interpret-lispu/
  65. Common Lisp
    https://common-lisp.net/
  66. Getting Going with Common Lisp
    https://cliki.net/Getting%20Started
  67. Online Tutorial (Common Lisp)
    https://cliki.net/online%20tutorial
  68. Guile Emacs
    https://www.emacswiki.org/e­macs/GuileEmacs
  69. Guile Emacs History
    https://www.emacswiki.org/e­macs/GuileEmacsHistory
  70. Guile is a programming language
    https://www.gnu.org/software/guile/
  71. MIT Scheme
    http://groups.csail.mit.e­du/mac/projects/scheme/
  72. SIOD: Scheme in One Defun
    http://people.delphiforum­s.com/gjc//siod.html
  73. CommonLispForEmacs
    https://www.emacswiki.org/e­macs/CommonLispForEmacs
  74. Elisp: print, princ, prin1, format, message
    http://ergoemacs.org/emac­s/elisp_printing.html
  75. Special Forms in Lisp
    http://www.nhplace.com/ken­t/Papers/Special-Forms.html
  76. Basic Building Blocks in LISP
    https://www.tutorialspoin­t.com/lisp/lisp_basic_syn­tax.htm
  77. Introduction to LISP – University of Pittsburgh
    https://people.cs.pitt.edu/~mi­los/courses/cs2740/Lectures/Lis­pTutorial.pdf
  78. Why don't people use LISP
    https://forums.freebsd.org/threads/why-dont-people-use-lisp.24572/
  79. Structured program theorem
    https://en.wikipedia.org/wi­ki/Structured_program_the­orem
  80. Clojure: API Documentation
    https://clojure.org/api/api
Našli jste v článku chybu?