Hlavní navigace

Úpravy Emacsu s Emacs Lisp: všemocné makro cl-loop a knihovna dash

Pavel Tišnovský

V Common Lispu se používá makro loop sloužící pro konstrukci všech typů programových smyček. Toto makro založené na vlastním doménově specifickém jazyku je dostupné i v Emacs Lispu.

Doba čtení: 29 minut

11. Přímá změna hodnoty prvků seznamu, přes nějž se iteruje

12. Klauzule append

13. Klauzule pro automatický výpočet sumy, nalezení maximálního a minimálního prvku atd.

14. Smyčka vracející programátorem specifikované (naakumulované) hodnoty

15. Lokální proměnné použité uvnitř těla smyčky

16. Alternativní funkcionální přístup při zpracování sekvencí – použití knihovny dash

17. Threading makra

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

19. Literatura

20. Odkazy na Internetu

1. Různé varianty funkce print

Dnešní článek je věnován především popisu univerzální programové smyčky cl-loop, ovšem ještě předtím se musíme alespoň ve stručnosti seznámit s různými variantami funkce print, protože tyto varianty budeme používat v demonstračních příkladech. V následující tabulce je vypsána většina funkcí, které lze použít pro zobrazení nějakých hodnot uživateli. Dnes využijeme zejména první tři funkce:

Funkce Stručný popis
print tisk hodnoty takovým způsobem, aby ji bylo možné načíst zpět pomocí read
prin1 dtto, ovšem bez konce řádku
princ tisk hodnoty tak, aby byla dobře čitelná uživatelem (nekompatibilní s read)
terpri odřádkování (terminate print)
message zobrazení zprávy v bufferu (pro zprávy)
insert vložení zprávy do bufferu (jakoby byla zpráva napsána na klávesnici)

Vyzkoušení vlastností prvních čtyř funkcí z předchozí tabulky:

(print 42)
(print "Hello")
(print :world)
(print '(1 2 3))
 
(print "-----------------------------------------")
 
(prin1 42)
(terpri)
(prin1 "Hello")
(terpri)
(prin1 :world)
(terpri)
(prin1 '(1 2 3))
(terpri)
 
(print "-----------------------------------------")
 
(princ 42)
(terpri)
(princ "Hello")
(terpri)
(princ :world)
(terpri)
(princ '(1 2 3))
(terpri)

Na standardním výstupu by se měl objevit následující text. Povšimněte si především různého způsobu zobrazení řetězců:

42
 
"Hello"
 
:world
 
(1 2 3)
 
"-----------------------------------------"
42
"Hello"
:world
(1 2 3)
 
"-----------------------------------------"
42
Hello
:world
(1 2 3)

2. Malé zopakování z minula – smyčka implementovaná pomocí speciální formy while

Vzhledem k tomu, že převážná část dnešního článku bude věnována různým formám programových smyček, zopakujeme si (velmi krátce) informace, které již známe z předchozích částí. Emacs Lisp, podobně jako jazyk Clojure a některé další dialekty Lispu, obsahuje ve své standardní knihovně jen jediný typ programové smyčky, a to konkrétně smyčku typu while. Chování této smyčky je známé z klasických strukturovaných jazyků, takže jen ve stručnosti:

(setq i 10)
 
(while (> i 0)
  (princ (format "i = %d\n" i))
  (setq i (- i 1)))

S výsledkem:

i = 10
i = 9
i = 8
i = 7
i = 6
i = 5
i = 4
i = 3
i = 2
i = 1

Počítání opačným směrem:

(setq i 0)
 
(while (< i 10)
  (princ (format "i = %d\n" i))
  (setq i (1+ i)))

S výsledkem:

i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9

Speciální forma while na první pohled vypadá jednoduše použitelná. Současně se (z pohledu teorie) vlastně jedná o jediný typ programové smyčky, kterou je zapotřebí implementovat, protože všechny ostatní typy smyček je možné z while odvodit (viz též Structured program theorem). Ovšem z hlediska praktického použití je patrné, že se while používá poměrně složitě, protože je ji většinou zapotřebí doplnit o další pomocné konstrukce; v našem případě o počitadlo smyčky. Z tohoto důvodu byly do různých dialektů LISPu postupně přidávány i další typy smyček, přičemž nejvíce univerzální je smyčka loop realizovaná v Common Lispu. A právě možnostmi nabízenými touto smyčkou se budeme zabývat v navazujících kapitolách.

3. Využití makra cl-loop pro konstrukci různých forem programových smyček

Poznámka na začátek: v Common Lispu se skutečně setkáme s makrem pojmenovaným loop. V Emacs Lispu je situace nepatrně odlišná, protože se toto makro importuje z knihovny nazvané cl-lib, kde prefix „cl“ samozřejmě znamená „Common Lisp“. Mj. i z tohoto důvodu se v Emacs Lispu toto makro jmenuje „cl-loop“:
(require 'cl-lib)

Smyčka loop ve formě, v jaké byla navržena v Common Lispu, programátorům nabízí svůj vlastní doménově specifický jazyk (DSL). Z dalších demonstračních příkladů bude patrné, že tento jazyk používá styl zápisu, který je kombinací klasických strukturovaných jazyků (Algol, Pascal, C) a možností LISPu. Je tomu tak z toho důvodu, aby bylo přímo ze zápisu smyčky, typicky již po přečtení prvního řádku, patrné, jak bude smyčka prováděna. K tomuto účelu se uvnitř smyčky loop používají symboly for, repeat, in, finally atd., které mají svůj speciální význam, ale pouze uvnitř samotné formy loop.

Poznámka: v Common Lispu je možné tyto symboly zapisovat i ve formě keywordů, tj. s dvojtečkami na začátku, což může být čitelnější.

Podívejme se nyní na pravděpodobně nejjednodušší prakticky použitelný příklad využívající smyčku cl-loop, v níž bude použita dvojice symbolů se speciálním významem, o nichž jsme se zmínili v předchozím odstavci. Konkrétně budeme implementovat programovou smyčku, jejíž tělo se bude n-krát opakovat. K zápisu této varianty smyčky nám pomohou dva symboly se jmény repeat a do. Povšimněte si, že zápis smyčky vypadá prakticky stejně, jako by tomu bylo v některém z klasických strukturovaných jazyků (samozřejmě pokud si odmyslíme kulaté závorky, do kterých toto makro vkládáme a které jsou v Lispu povinné):

(cl-loop repeat počet-opakování do ...)

Podívejme se na praktický příklad (prozatím velmi jednoduchý):

(require 'cl-lib)
 
(cl-loop repeat 10 do
         (princ "Hello world!\n"))

Makro cl-loop zavolané tímto způsobem nevrací žádnou hodnotu, takže se předpokládá, že smyčka vykoná svoji činnost jen díky tomu, že některá funkce volaná při každé iteraci bude mít vedlejší efekt, například že vypíše zprávu na obrazovku atd., což je ostatně přesně náš případ. Po spuštění výše popsané smyčky se na výstupu skutečně zobrazí deset totožných zpráv:

Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Poznámka: povšimněte si, že při použití loop-repeat-do vlastně nemáme k dispozici počitadlo smyčky. V případě, že je nutné počitadlo využít, je výhodnější použít další varianty smyčky, například loop-for, které budou popsány v šesté, sedmé a osmé kapitole.

4. Použití klauzulí while a until

Ve smyčce cl-loop je možné použít i klauzule while a until, za nimiž se zapisuje podmínka. Ta je od těla smyčky oddělena speciálním symbolem do, takže zápisy vypadají následovně:

Vyhodnocení podmínky na začátku každé iterace, tělo smyčky se zavolá, pokud je podmínka splněna:

(cl-loop while podmínka do ...)

Vyhodnocení podmínky na začátku každé iterace, tělo smyčky se zavolá, pokud podmínka splněna naopak není (po splnění podmínky se smyčka opustí):

(cl-loop until podmínka do ...)

Demonstrační příklad bude velmi jednoduchý, protože v něm použijeme jedinou řídicí proměnnou i, kterou budeme nejprve zmenšovat o jedničku a poté v druhé smyčce naopak zvyšovat až do chvíle, kdy překročí hodnotu 10:

(require 'cl-lib)
 
(setq i 10)
 
(cl-loop while (> i 0) do
         (princ (format "i = %d\n" i))
         (setq i (- i 1)))
 
(cl-loop until (> i 10) do
         (princ (format "i = %d\n" i))
         (setq i (+ i 1)))

Výsledek:

i = 10
i = 9
i = 8
i = 7
i = 6
i = 5
i = 4
i = 3
i = 2
i = 1
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10

5. Počítané smyčky typu for

Makro cl-loop samozřejmě podporuje i tvorbu klasických programových smyček, v nichž se postupně mění hodnota počitadla. Základní forma této smyčky vypadá následovně:

(cl-loop for i to 10 do
         (princ (format "i = %d\n" i)))

Výsledek ukazuje, že se počítá od nuly a končí se až po dosažení koncové hodnoty (ne o jedničku dříve):

i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10

Alternativně můžeme v tomto případě namísto symbolu to použít spíše upto:

(cl-loop for i upto 10 do
         (princ (format "i = %d\n" i)))

Se shodným výsledkem:

i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10

Mnohdy potřebujeme, aby horní meze nebylo dosaženo a smyčka skončila těsně předtím. Namísto komplikovaných výpočtů použijte below a nikoli to či upto:

(cl-loop for i below 10 do
         (princ (format "i = %d\n" i)))

Skutečně se počítá jen do 9 a nikoli 10:

i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9

Užitečná je možnost přidání další (libovolné) podmínky, která pro počitadlo musí platit. Jsou přeskočeny ty iterace, kdy podmínka není splněna (ovšem smyčka není ukončena):

(cl-loop for i below 10 when (cl-evenp i) do
         (princ (format "i = %d\n" i)))

Výsledkem je sekvence sudých čísel:

i = 0
i = 2
i = 4
i = 6
i = 8

Popř.:

(cl-loop for i below 10 when (cl-oddp i) do
         (princ (format "i = %d\n" i)))

Výsledkem je sekvence čísel lichých:

i = 1
i = 3
i = 5
i = 7
i = 9

6. Specifikace počáteční hodnoty počitadla smyčky

Samozřejmě je možné v případě potřeby specifikovat i počáteční hodnotu počitadla; tj. nemusí se vždy začínat na nule:

(cl-loop for i from 1 to 10 do
         (princ (format "i = %d\n" i)))

Výsledek:

i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10

Totožná smyčka, ovšem s jiným symbolem (lépe čitelným):

(cl-loop for i from 1 upto 10 do
         (princ (format "i = %d\n" i)))

Výsledek je stejný:

i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10

Při počítání směrem nahoru lze namísto from použít symbol upfrom se shodným významem:

(cl-loop for i upfrom 1 upto 10 do
         (princ (format "i = %d\n" i)))

Výsledek:

i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10

Kombinace from a below:

(cl-loop for i from 1 below 10 do
         (princ (format "i = %d\n" i)))

Výsledek je kratší o poslední iteraci:

i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9

Další kombinace již obsahují klauzuli when:

(cl-loop for i from 1 to 10 when (cl-evenp i) do
         (princ (format "i = %d\n" i)))

Výsledek:

i = 2
i = 4
i = 6
i = 8
i = 10
(cl-loop for i from 1 upto 10 when (cl-evenp i) do
         (princ (format "i = %d\n" i)))

Výsledek:

i = 2
i = 4
i = 6
i = 8
i = 10
(cl-loop for i upfrom 1 upto 10 when (cl-evenp i) do
         (princ (format "i = %d\n" i)))

Výsledek:

i = 2
i = 4
i = 6
i = 8
i = 10
(cl-loop for i from 1 below 10 when (cl-evenp i) do
         (princ (format "i = %d\n" i)))

Výsledek:

i = 2
i = 4
i = 6
i = 8

7. Specifikace kroku, s nímž se mění hodnota počitadla smyčky

Další příklad si již ukážeme v celku. Používá se v něm symbol by, za nímž se udává krok, tj. o jakou hodnotu se bude počitadlo měnit. Výchozí hodnotou je pochopitelně jednička:

(require 'cl-lib)
 
(cl-loop for i from 0 to 30 by 3 do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(cl-loop for i from 0 upto 30 by 3 do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(cl-loop for i upfrom 0 upto 30 by 3 do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(cl-loop for i upfrom 0 below 30 by 3 do
         (princ (format "i = %d\n" i)))
 
(princ "===========================\n")
 
(cl-loop for i from 0 to 30 by 3 when (cl-evenp i) do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(cl-loop for i from 0 upto 30 by 3 when (cl-evenp i) do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(cl-loop for i upfrom 0 upto 30 by 3 when (cl-evenp i) do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(cl-loop for i upfrom 0 below 30 by 3 when (cl-evenp i) do
         (princ (format "i = %d\n" i)))

Podívejme se nyní na výsledky produkované tímto příkladem:

i = 0
i = 3
i = 6
i = 9
i = 12
i = 15
i = 18
i = 21
i = 24
i = 27
i = 30
---------------------------
i = 0
i = 3
i = 6
i = 9
i = 12
i = 15
i = 18
i = 21
i = 24
i = 27
i = 30
---------------------------
i = 0
i = 3
i = 6
i = 9
i = 12
i = 15
i = 18
i = 21
i = 24
i = 27
i = 30
---------------------------
i = 0
i = 3
i = 6
i = 9
i = 12
i = 15
i = 18
i = 21
i = 24
i = 27
===========================
i = 0
i = 6
i = 12
i = 18
i = 24
i = 30
---------------------------
i = 0
i = 6
i = 12
i = 18
i = 24
i = 30
---------------------------
i = 0
i = 6
i = 12
i = 18
i = 24
i = 30
---------------------------
i = 0
i = 6
i = 12
i = 18
i = 24

8. Snižování hodnoty počitadla smyčky

Prozatím jsme v předchozích smyčkách počitadlo vždy zvyšovali, ať již o jedničku nebo o jinou hodnotu. Počítat je však možné i opačným směrem. V tomto případě však nestačí za by zadat záporné číslo! Je nutné použít jiný zápis, a to s využitím symbolů downto nebo above. Opět si ukažme některé povolené kombinace (pozor na rozdílné chování downto a above):

(require 'cl-lib)
 
(cl-loop for i from 10 downto 1 do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(cl-loop for i from 10 above 1 do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(cl-loop for i upfrom 10 above 1 do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(cl-loop for i upfrom 10 above 1 by 2 do
         (princ (format "i = %d\n" i)))
 
(princ "===========================\n")
 
(cl-loop for i from 10 downto 1 when (cl-evenp i) do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(cl-loop for i from 10 above 1 when (cl-evenp i) do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(cl-loop for i upfrom 10 above 1 when (cl-evenp i) do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(cl-loop for i upfrom 10 above 1 by 2 when (cl-evenp i) do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")
 
(cl-loop for i upfrom 10 above 1 by 2 when (cl-oddp i) do
         (princ (format "i = %d\n" i)))
 
(princ "---------------------------\n")

Po spuštění budou výsledky následující:

i = 10
i = 9
i = 8
i = 7
i = 6
i = 5
i = 4
i = 3
i = 2
i = 1
---------------------------
i = 10
i = 9
i = 8
i = 7
i = 6
i = 5
i = 4
i = 3
i = 2
---------------------------
i = 10
i = 9
i = 8
i = 7
i = 6
i = 5
i = 4
i = 3
i = 2
---------------------------
i = 10
i = 8
i = 6
i = 4
i = 2
===========================
i = 10
i = 8
i = 6
i = 4
i = 2
---------------------------
i = 10
i = 8
i = 6
i = 4
i = 2
---------------------------
i = 10
i = 8
i = 6
i = 4
i = 2
---------------------------
i = 10
i = 8
i = 6
i = 4
i = 2
---------------------------
---------------------------
Poznámka: poslední smyčka skutečně neproběhne ani jednou (proč?).

9. Procházení prvky seznamu aneb smyčka typu for-each

V případě, že se má procházet všemi prvky seznamu (nebo vektoru), lze použít klauzuli for in, která používá následující styl zápisu:

(cl-loop for prvek in seznam
  ...
  ...
  ...
  tělo smyčky
  ...
  ...
  ...)

Vidíme, že se tento zápis opět do značné míry podobá syntaxi, s níž se setkáme v běžných programovacích jazycích. Typicky se tato smyčka používá ve chvíli, kdy se v ní volá funkce s vedlejším efektem. Pokud tomu tak není a je nutné ze smyčky vrátit výsledek aplikace nějaké funkce na prvky seznamu, používá se klauzule collect, a to přibližně tímto způsobem:

(cl-loop for prvek in seznam
    collect prvek)

V následujícím příkladu je tato varianta smyčky použita, i když se ve skutečnosti dá nahradit funkcí mapcar:

(require 'cl-lib)
 
(setq lst (number-sequence 0 10))
 
(print
    (cl-loop for i in lst
             collect i))
 
(print
    (cl-loop for i in lst
             collect (* i i)))
 

(defun factorial (n)
  (let ((accumulator 1))
    (dolist (value (number-sequence 1 n))
      (setq accumulator (* accumulator value)))
    accumulator))
 
(print
    (cl-loop for i in lst
             collect (factorial i)))

Výsledky:

(0 1 2 3 4 5 6 7 8 9 10)
 
(0 1 4 9 16 25 36 49 64 81 100)
 
(1 1 2 6 24 120 720 5040 40320 362880 3628800)

10. Rozdíl mezi formami for i in a for i on

Existuje ještě jedna varianta smyčky for-each, ovšem tato varianta používá zápis se symbolem on a nikoli in:

(cl-loop for prvek on seznam
    collect prvek)

Tato varianta prochází seznamem odlišně – v první iteraci se do řídicí proměnné smyčky vloží celý seznam, ve druhé iteraci seznam bez prvního prvku atd. atd.:

(require 'cl-lib)
 
(setq lst (number-sequence 0 10))
 
(setq result
    (cl-loop for i on lst
             collect i))
 
(dolist (item result)
  (print item))

Výsledek nyní bude značně odlišný – bude se jednat o seznam seznamů:

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

11. Přímá změna hodnoty prvků seznamu, přes nějž se iteruje

Nyní se dostáváme do oblasti, v níž se vlastnosti makra cl-loop již prakticky ztotožnily s možnostmi nefunkcionálních jazyků, se všemi výhodami a nevýhodami, které tento přístup přináší. V některých situacích je totiž vhodné, aby programová smyčka přímo měnila hodnoty seznamu (nebo pole), jehož prvky se ve smyčce prochází. I to je možné, a to díky následující konstrukci:

(cl-loop for prvek in-ref seznam do
  ...
  ...
  ...
  tělo smyčky
  ...
  ...
  ...)

Povšimněte si rozdílu oproti předchozím smyčkám – namísto speciálního symbolu in je použit symbol in-ref. Díky použití tohoto symbolu se v každé iteraci získá reference na prvek procházeného seznamu, nikoli hodnota. Této vlastnosti můžeme využít například pro zvýšení hodnoty prvku v seznamu o jedničku pomocí funkce cl-incf:

(cl-loop for i in-ref lst do
         (cl-incf i)))

Po zavolání této smyčky bude původní seznam lst obsahovat nové hodnoty (o jedničku zvýšené). Podívejme se nyní na demonstrační příklad, v němž bude použit právě tento typ smyčky, to hned dvakrát. V první smyčce zvýšíme hodnotu prvků původního seznamu o jedničku, ve smyčce druhé pak prvky vynulujeme:

(require 'cl-lib)
 
(setq lst (number-sequence 0 10))
 
(print lst)
 
(print
    (cl-loop for i in-ref lst do
             (cl-incf i)))
 
(print lst)
 
(cl-loop for i in-ref lst do
         (setf i 0))
 
(print lst)

Po spuštění se na výstupu objeví následující řádky:

(0 1 2 3 4 5 6 7 8 9 10)
 
nil
 
(1 2 3 4 5 6 7 8 9 10 11)
 
(0 0 0 0 0 0 0 0 0 0 0)
Poznámka: nil se zobrazí proto, že smyčka nevrací žádnou hodnotu.

12. Klauzule append

V předchozích příkladech jsme pro získání výsledné hodnoty smyčky používali klauzuli collect. Použít je možné i klauzuli append, která pracuje podobně, ale pokud této klauzuli předáme seznam, budou všechny jeho prvky přidány do výsledného seznamu (každý zvlášť). Můžeme tím tedy nahradit operaci flatten:

(require 'cl-lib)
 
(setq letters '((alfa beta) () (gama delta) (omega) ()))
 
(setq result
  (cl-loop for i in letters
           append i))
 
(dolist (item result)
  (print item))
alfa
 
beta
 
gama
 
delta
 
omega

13. Klauzule pro automatický výpočet sumy, nalezení maximálního a minimálního prvku atd.

Podívejme se ještě na některé složitější konstrukce, které dokážeme s makrem cl-loop vytvořit. Poměrně často se setkáme se situací, kdy je nutné vypočítat sumu všech prvků nějakého seznamu nebo vektoru. To lze provést několika způsoby (reduce atd.), ovšem při použití smyčky cl-loop lze využít klauzuli sum pro akumulaci výsledků:

(require 'cl-lib)
 
(setq lst (number-sequence 0 10))
 
(setq result
    (cl-loop for i in lst
             sum i))
 
(princ (format "Result: %d" result))

Předchozí zápis vracel implicitně jedinou hodnotu ze smyčky, a to sumu prvků. Toto chování lze popsat i explicitně s využitím klauzule finally, do níž zapíšeme příkaz, který se má vykonat při ukončování smyčky. Povšimněte si, že zde používáme lokální proměnnou total (lze ji pojmenovat různě):

(setq result
    (cl-loop for i in lst
             sum i into total
             finally return total))
 
(princ (format "Result: %d" result))

Výsledkem bude v obou případech:

Result: 55
Poznámka: právě na těchto příkladech asi začíná být viditelná síla doménově specifického jazyka makra cl-loop.

14. Smyčka vracející programátorem specifikované (naakumulované) hodnoty

Předchozí příklad je možné ještě více „vyšperkovat“, například vypočítat počet všech prvků, jejich součet, maximální hodnotu a minimální hodnotu. To vše v jediné smyčce a bez použití podmínek. Povšimněte si způsobu, jak ze smyčky vrátit více hodnot:

(require 'cl-lib)
 
(setq lst (number-sequence 0 10))
 
(setq result
    (cl-loop for i in lst
             count i into counter
             sum i into total
             maximize i into max-value
             minimize i into min-value
             finally return (list min-value max-value total counter)))
 
(princ (format "Min value %d\n" (nth 0 result)))
(princ (format "Max value %d\n" (nth 1 result)))
(princ (format "Sum value %d\n" (nth 2 result)))
(princ (format "Values %d\n" (nth 3 result)))

Výsledky vypočtené předchozím příkladem:

Min value 0
Max value 10
Sum value 55
Values 11

Zcela stejně můžeme postupovat při analýze generátoru náhodných čísel. Povšimněte si, že v klauzuli finally pochopitelně můžeme volat nějakou funkci, zde dělení (výpočet průměru):

(require 'cl-lib)
 
(setq rnd (cl-loop repeat 1000
                   collect (random 10000)))
 
 
(setq result
    (cl-loop for x in rnd
             count x into counter
             sum x into total
             maximize x into max-value
             minimize x into min-value
             finally return (list min-value max-value total (/ total counter) counter)))
 
 
(princ (format "Min value %d\n" (nth 0 result)))
(princ (format "Max value %d\n" (nth 1 result)))
(princ (format "Sum value %d\n" (nth 2 result)))
(princ (format "Avg value %d\n" (nth 3 result)))
(princ (format "Values %d\n" (nth 4 result)))

Výsledky (mohou se pochopitelně lišit):

Min value 0
Max value 9990
Sum value 4992707
Avg value 4992
Values 1000

15. Lokální proměnné použité uvnitř těla smyčky

V posledním příkladu s makrem cl-loop je ukázáno, že uvnitř programové smyčky je možné v případě potřeby použít lokální proměnné. Tato smyčka slouží k vyhledání odmocniny nějaké vstupní hodnoty, popř. celého čísla, které je této odmocnině nejblíže. Povšimněte si způsobu zápisu koncové podmínky pomocí until (ta se ovšem kombinuje s from to, takže se nikdy nebude jednat o nekonečnou smyčku):

(require 'cl-lib)
 
(defun find-sqrt
  (value)
  (cl-loop for x from 1 to 200
           for square = (* x x)
           until (>= square value)
           finally return x))
 
(princ (format "Sqrt of %d = %d\n" 1764 (find-sqrt 1764)))

Výsledek předchozího příkladu:

Sqrt of 1764 = 42

16. Alternativní funkcionální přístup při zpracování sekvencí – použití knihovny dash

V samotném závěru dnešního článku se zmíníme o zajímavé knihovně nazvané dash, jejíž zdrojové kódy je možné nalézt na GitHubu, konkrétně na adrese https://github.com/magnars/dash.el. Makra a funkce, které jsou touto knihovnou nabízeny, jsou inspirovány standardní knihovnou programovacího jazyka Clojure, zejména funkcemi pro funkcionálně pojaté zpracování různých typů sekvencí (filtrace, redukce, aplikace funkce na prvky sekvence, rozdělení sekvence atd).

To ovšem není vše, protože knihovna dash obsahuje i takzvaná threading makra, která mohou být velmi užitečná a navíc mohou zvýšit čitelnost zdrojových kódů. Při použití threading maker je možné zredukovat počet kulatých závorek, ovšem mnohem důležitější je fakt, že sekvence zápisu funkcí v programovém kódu bude odpovídat jejich volání (ve standardním Lispu je to vlastně naopak). Dnes se seznámíme jen s nejzákladnějšími funkcemi a makry poskytovanými touto knihovnou, její další možnosti budou popsány v samostatném článku.

Poznámka: jméno této knihovny naznačuje, že všechny funkce a makra, která v ní nalezneme, začínají znakem „-“ nebo dvojicí znaků „–“. Možná se jedná o narážku na knihovnu Underscore, která taktéž vývojářům nabízí funkcionální prvky, ovšem nikoli do jazyka Emacs Lisp, ale do JavaScriptu.

Nejdříve si na několika screenshotech ukážeme, jakým způsobem se knihovna dash instaluje. Nebude to nic těžkého, pouze stačí mít povolen repositář Melpa, což jsme si již ukázali v článku o pluginu Evil:

Obrázek 1: Zadáme příkaz M-x list-packages.

Obrázek 2: Nalezneme balíček s knihovnou dash.

Obrázek 3: Provedeme instalaci (tlačítko Install).

Do .emacs doplníme:

(require 'dash)

Podívejme se nyní na některé základní možnosti, které programátorům knihovna dash nabízí. Základní funkcí (vyššího řádu), kterou v této knihovně naleznete, je klasická funkce map, která se zde ovšem jmenuje -map nebo –map. Příklad použití:

(package-initialize)
(require 'dash)
 
(defun factorial
  (n)
  (apply '* (number-sequence 1 n)))
 
(print
  (-map 'factorial '(0 1 2 3 4 5 6 7 8 9 10)))

S výsledky:

(1 1 2 6 24 120 720 5040 40320 362880 3628800)
Poznámka: funkce -map je ve skutečnosti do knihovny dash přidána jen kvůli jmenným konvencím. Interně se totiž používá standardní funkce mapcar [1]:
mapcar is a built-in function in ‘C source code’.
 
(mapcar FUNCTION SEQUENCE)
 
Apply FUNCTION to each element of SEQUENCE, and make a list of the results.
The result is a list just as long as SEQUENCE.
SEQUENCE may be a list, a vector, a bool-vector, or a string.

Předchozí příklad tedy můžeme přepsat na:

(print
  (mapcar 'factorial '(0 1 2 3 4 5 6 7 8 9 10)))

Samozřejmě je možné, aby se přímo použila anonymní funkce namísto pojmenované funkce factorial:

(print
  (-map (lambda (n) (apply '* (number-sequence 1 n))) '(0 1 2 3 4 5 6 7 8 9 10)))

A právě při čtení předchozího výrazu vás možná napadlo, že použití lambda je pro tak jednoduchou věc, jako je definice anonymní funkce používané na jediném místě, zbytečně zdlouhavé. Pro kratší zápis je možné použít variantu –map:

(print
  (--map (apply '* (number-sequence 1 it)) '(0 1 2 3 4 5 6 7 8 9 10)))

Povšimněte si toho, že namísto anonymní funkce můžeme napsat přímo její tělo a v těle lze použít symbol it, který nahrazuje pojmenovaný parametr n z předchozího příkladu. it je v tomto případě rezervovaný symbol s přesně tímto význame (podobný % v Clojure), protože ve skutečnosti je –map makrem, s následující definicí:

(defmacro --map (form list)
  "Anaphoric form of `-map'."
  (declare (debug (form form)))
`(mapcar (lambda (it) ,form) ,list))

Expanzí volání tohoto makra získáme kód (přesněji řečeno formu), který se podobá prvnímu příkladu s plnohodnotnou anonymní funkci (pro tyto účely můžeme ignorovat řádek s výstupem pro ladění).

Poznámka: ve skutečnosti se jedná o takzvané anaforické makro. Makru se předává forma form odpovídající tělu anonymní funkce a blíže neupřesněný seznam s hodnotami list, které se anonymní funkci předají. Makro následně při své expanzi anonymní funkci vytvoří, přičemž použije it jako jméno parametru této anonymní funkce. Tato anonymní funkce bude předána standardní funkci vyššího řádu mapcar zmíněné výše, samozřejmě společně s hodnotami list. Expanze by mohla vypadat takto (prozatím vynecháme popis funkce znaků ` a ,):
(mapcar (lambda (it) (apply '* (number-sequence 1 it))) '(0 1 2 3 4 5 6 7 8 9 10))

Ještě se v rychlosti podíváme na další užitečnou funkci pojmenovanou -filter, jejíž význam a různé varianty budou popsány příště:

(package-initialize)
(require 'dash)
(require 'cl-lib)
 
(setq numbers (number-sequence 0 30))
 
(print numbers)
 
(print (-filter 'cl-evenp numbers))
 
(print (-filter 'cl-oddp numbers))
 
(print (-filter (lambda (n) (zerop (% n 3))) numbers))
 
(print (--filter (zerop (% it 3)) numbers))

Výsledky filtrace původních seznamu se 31 hodnotami 0..30:

(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30)
 
(0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30)
 
(1 3 5 7 9 11 13 15 17 19 21 23 25 27 29)
 
(0 3 6 9 12 15 18 21 24 27 30)
 
(0 3 6 9 12 15 18 21 24 27 30)

17. Threading makra

V programovacím jazyku Clojure existuje dvojice velmi užitečných maker, která je možné použít pro zjednodušení zápisu „kolony“ funkcí, kterým se postupně předávají výsledky předchozích funkcí. Jedná se vlastně o lispovskou obdobu klasické Unixové kolony používané při spojování filtrů v shellu (ls –1 | sort atd.). V Clojure je možné namísto poměrně nečitelného zápisu:

(f (g (h x)))

použít zápis:

(-> x h g f)

Tímto zápisem se specifikuje, že se nějaká hodnota x předá funkci h, výsledek této funkce se předá funkci g a výsledek funkce g se nakonec předá funkci f. Podobně fungující makro je dostupné i v knihovně dash a tím pádem pochopitelně i v celém Emacs Lispu (pokud si pochopitelně tuto knihovnu naimportujete), takže si ho můžeme ihned vyzkoušet:

(-> (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. Tento parametr není ve zdrojovém textu viditelný:

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

S výsledkem:

32

V mnoha případech nám však nemusí vyhovovat, že se předchozí výsledek předá další funkci v koloně v prvním parametru. Dobrým příkladem mohou být již výše zmíněné funkce vyššího řádu. Pokud se tyto funkce používají v koloně, musí se namísto makra → použít druhé threading makro nazvané ->>, které předchozí výsledek předá další funkci v posledním parametru, nikoli v parametru prvním. Opět se podívejme na jednoduchý příklad:

(->> (number-sequence 0 30)
     (-filter 'cl-evenp)
     (-filter (lambda (n) (zerop (% n 3))))
     reverse
     print)

Výsledek by měl vypadat takto:

(30 24 18 12 6 0)

V předchozím jsme funkci vyššího řádu -filter předávali složitější predikáty, které si samozřejmě můžeme pro větší čitelnost definovat v nových uživatelských funkcích:

(defun even-numbers
  (numbers)
  (-filter 'cl-evenp numbers))
 
 
(defun div-by-three-numbers
  (numbers)
  (-filter (lambda (n) (zerop (% n 3))) numbers))

Následně se nám předchozí příklad zjednoduší:

(->> (number-sequence 0 30)
     even-numbers
     div-by-three-numbers
     reverse
     print)

Výsledek bude shodný s předchozím příkladem:

(30 24 18 12 6 0)
Poznámka: schválně si zkuste příklad přečíst nahlas – bude přesně odpovídat tomu, jak budou funkce volány v runtime.

První hodnotou, která do threading makra vstupuje, může být přímo zpracovávané číslo (bude ihned předáno generátoru number-sequence ve druhém parametru):

(->> 100
     (number-sequence 0)
     even-numbers
     div-by-three-numbers
     reverse
     print)

Výsledek:

(96 90 84 78 72 66 60 54 48 42 36 30 24 18 12 6 0)
Poznámka: podrobnější popis všech užitečných variant threading maker bude uveden v navazujícím článku.

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_print_variants.el různé varianty funkce print https://github.com/tisnik/elisp-examples/blob/master/elisp-3/01_print_variants.el
2 02_while_loop_A.el jednoduchá smyčka typu while https://github.com/tisnik/elisp-examples/blob/master/elisp-3/02_while_loop_A.el
3 03_while_loop_B.el jednoduchá smyčka typu while https://github.com/tisnik/elisp-examples/blob/master/elisp-3/03_while_loop_B.el
4 04_cl_loop_repeat.el klauzule repeat https://github.com/tisnik/elisp-examples/blob/master/elisp-3/04_cl_loop_repeat.el
5 05_cl_loop_while_until.el klauzule while a until https://github.com/tisnik/elisp-examples/blob/master/elisp-3/05_cl_loop_while_until.el
6 06_cl_loop_for_to.el smyčka s počitadlem https://github.com/tisnik/elisp-examples/blob/master/elisp-3/06_cl_loop_for_to.el
7 07_cl_loop_for_from_to.el smyčka s počitadlem https://github.com/tisnik/elisp-examples/blob/master/elisp-3/07_cl_loop_for_from_to.el
8 08_cl_loop_for_from_to_by.el smyčka s počitadlem https://github.com/tisnik/elisp-examples/blob/master/elisp-3/08_cl_loop_for_from_to_by.el
9 09_cl_loop_for_from_dowto.el smyčka s počitadlem https://github.com/tisnik/elisp-examples/blob/master/elisp-3/09_cl_loop_for_from_dowto.el
10 10_cl_loop_for_in.el procházení seznamem https://github.com/tisnik/elisp-examples/blob/master/elisp-3/10_cl_loop_for_in.el
11 11_cl_loop_for_on.el procházení seznamem https://github.com/tisnik/elisp-examples/blob/master/elisp-3/11_cl_loop_for_on.el
12 12_cl_loop_for_in_ref.el získání reference na procházené prvky https://github.com/tisnik/elisp-examples/blob/master/elisp-3/12_cl_loop_for_in_ref.el
13 13_cl_loop_append.el klauzule append https://github.com/tisnik/elisp-examples/blob/master/elisp-3/13_cl_loop_append.el
14 14_cl_loop_sum.el výpočet sumy prvků https://github.com/tisnik/elisp-examples/blob/master/elisp-3/14_cl_loop_sum.el
15 15_cl_loop_max_min_count_sum.el statistické informace o seznamu https://github.com/tisnik/elisp-examples/blob/master/elisp-3/15_cl_loop_max_min_count_sum.el
16 16_rnd_stats.el statistické informace o seznamu https://github.com/tisnik/elisp-examples/blob/master/elisp-3/16_rnd_stats.el
17 17_cl_loop_local_vars.el lokální proměnné smyčky https://github.com/tisnik/elisp-examples/blob/master/elisp-3/17_cl_loop_local_vars.el
18 18_dash_basics.el použití -map https://github.com/tisnik/elisp-examples/blob/master/elisp-3/18_dash_basics.el
19 19_dash_filters.el použití -filter https://github.com/tisnik/elisp-examples/blob/master/elisp-3/19_dash_filters.el
20 20_threading_macro.el threading makro https://github.com/tisnik/elisp-examples/blob/master/elisp-3/20_threading_macro.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
Našli jste v článku chybu?