Lisp: Porovnání verzí

Přidáno 306 bajtů ,  před 10 měsíci
m
Robot: -zastaralá značka HTML
m (Robot: -zastaralá značka HTML)
 
Základním zápisem v Lispu je seznam. Zapisuje se tímto způsobem:
<sourcesyntaxhighlight lang="lisp">
(1 2 "ahoj" 13.2)
</syntaxhighlight>
</source>
 
Tento seznam obsahuje čtyři prvky:
 
Například sčítání je realizováno příkazem '''+'''. Odpovídající konstrukce v jazyce vypadá takto:
<sourcesyntaxhighlight lang="lisp">
(+ 1 2 3)
</syntaxhighlight>
</source>
 
Interpret odpoví '''6'''.
 
Program [[hello world]] lze zapsat několika způsoby. Nejjednodušší vypadá takto:
<sourcesyntaxhighlight lang="lisp">
(format t "Hello, World!")
</syntaxhighlight>
</source>
 
Funkce se v Lispu definují klíčovým slovem ''defun'':
<sourcesyntaxhighlight lang="lisp">
(defun hello ()
(format t "~&Hello, World!~%"))
(hello)
</syntaxhighlight>
</source>
 
Na prvních dvou řádcích je definice funkce ''hello'', na třetím řádku je tato funkce svým jménem zavolána.
 
Funkcím lze předávat i argumenty. V následujícím příkladu je ukázka funkce ''fact'', která vypočítá [[faktoriál]] zadaného čísla:
<sourcesyntaxhighlight lang="lisp">
(defun fact (n)
(if (zerop n)
1
(* n (fact (- n 1)))))
</syntaxhighlight>
</source>
 
Pro výpočet faktoriálu čísla 6 předáme tuto hodnotu jako argument funkci fact:
<sourcesyntaxhighlight lang="lisp">
(fact 6)
</syntaxhighlight>
</source>
 
Návratovou hodnotou funkce bude hodnota 720.
Abychom mohli makra vůbec používat, musíme mít nějaké nástroje k transformaci kódu. Běžně se používá speciální operátor <code>quote</code>, který vrátí následný výraz tak jak mu ho předáme — žádnou část nevyhodnotí. Jako syntaktickou zkratku můžeme použít apostrof <code>'</code>.
 
<sourcesyntaxhighlight lang="lisp">
;; Mohlo by se zdát, že quote není potřebný operátor, když máme list,
;; ale jak je vidět, je mezi nimi zásadní rozdíl — funkce list vyhodnocuje
> '(1 2 3)
(1 2 3)
</syntaxhighlight>
</source>
 
Abychom mohli i kvotované části nechat něco vyhodnotit, musíme mít mechanismus, kterým zrušíme ono kvotování a vrátíme se zpět k vyhodnocování. K tomu slouží speciální operátory <code>unquote</code> a <code>quasiquote</code>. Quasiquote se chová stejně jako quote, pouze s tím rozdílem, že ve svém těle umožňuje použít unquote, který vyhodnotí daný výraz. Syntaktická zkratka pro unquote je čárka <code>,</code> a pro quasiquote zpětný apostrof <code>`</code>.
 
<sourcesyntaxhighlight lang="lisp">
> `(1 2 ,(+ 3 4))
(1 2 7)
> `('a 'b ,(list (+ 1 2) (+ 3 4)) c d)
((QUOTE A) (QUOTE B) (3 7) C D)
</syntaxhighlight>
</source>
 
=== Základní práce s makry ===
Makra se vytvářejí pomocí speciálního operátoru <code>defmacro</code>. Nejjednodušší příklad může být definice vlastní podmínky, vlastního ifu. Pomocí makra by to vypadalo následovně:
 
<sourcesyntaxhighlight lang="lisp">
(defmacro my-if (cond true false)
`(if ,cond
,true
,false))
</syntaxhighlight>
</source>
 
Makro se chová stejně jako běžný if:
 
<sourcesyntaxhighlight lang="lisp">
> (my-if 1 2 3)
2
2
2
</syntaxhighlight>
</source>
 
Při definici „vlastního“ ifu musíme použít makro, protože nevyhodnocuje své argumenty. Kdybychom nadefinovali if jako funkci, nechovalo by se to stejně, protože argumenty už by se vyhodnotily při volání funkce a tím pádem by se vždy vyhodnotily obě větve podmínky.
 
<sourcesyntaxhighlight lang="lisp">
(defun my-bad-if (cond true false)
(if cond
6
5
</syntaxhighlight>
</source>
 
=== Problémy spojené s makry ===
Při používání maker si musíme dávat pozor na dva klasické problémy — '''dvojí vyhodnocení''' a '''symbol capture'''. Představme si if, který v true větvi automaticky vrátí výsledek podmínky a ve false větvi vrátí předaný argument. Ukázka, jak by to mělo fungovat:
 
<sourcesyntaxhighlight lang="lisp">
; 1 je true, tak vrátí 1
> (if-false 1 2)
> (if-false (member nil '(1 2 3 4 5)) 'nic)
NIC
</syntaxhighlight>
</source>
 
Naivní implementace by mohla vypadat takto:
 
<sourcesyntaxhighlight lang="lisp">
(defmacro if-false-1 (cond false)
`(if ,cond
,cond
,false))
</syntaxhighlight>
</source>
 
Toto makro zdánlivě funguje. Ovšem do doby, než na něj pustíme kód s vedlejším efektem:
 
<sourcesyntaxhighlight lang="lisp">
;; Funguje jak má
> (if-false-1 (member 2 '(1 2 3 4 5)) 'nic)
(if-false-1 (incf a) 'nic))
3
</syntaxhighlight>
</source>
 
Kód <code>(incf a)</code> se v těle makra vyhodnotil dvakrát, proto nám to vrátí trojku. Kód po makroexpanzi vypadá takto:
 
<sourcesyntaxhighlight lang="lisp">
(LET ((A 1))
(IF (INCF A)
(INCF A)
'NIC))
</syntaxhighlight>
</source>
 
Řešením je navázat vyhodnocenou podmínku na nějaký symbol:
 
<sourcesyntaxhighlight lang="lisp">
(defmacro if-false-2 (cond false)
`(let ((cond-help ,cond))
cond-help
,false)))
</syntaxhighlight>
</source>
 
Teď už se výraz vyhodnotí pouze jednou:
 
<sourcesyntaxhighlight lang="lisp">
> (let ((a 1))
(if-false-2 (incf a) 'nic))
2
</syntaxhighlight>
</source>
 
== Externí odkazy ==
413 536

editací