Provided by:
manpages-pl_20051117-1_all 
NAZWA
lisp-tut - Wskazówki dla Common LISP
OPIS
Uwaga: to wprowadzenie do Common Lisp zostało napisane dla środowiska
CMU, więc niektóre fragmenty pracy z lispem mogą się trochę różnić w
różnych miejscach.
Inne źródła informacji
Najlepsza książka o LISP, którą znam, to
Guy L. Steele Jr. _Common LISP: the Language_. Digital Press. 1984.
Pierwsza edycja jest łatwiejsza do czytania; druga opisuje trochę
nowszy standard. (Różnice między standardami nie powinny sprawiać
różnic zwykłym programistom).
Polecano mi również książkę Dave'a Touretsky'ego, choć nie czytałem jej
i nie mogę o niej nic powiedzieć.
Symbole
Symbol to łańcuch znaków. Istnieją ograniczenia co do tego, co można
wstawić do symbolu i co może być pierwszym znakiem, lecz tak długo jak
używasz prostych liter, cyfr i myślników, nic się nie stanie. (Poza
tym, że jeśli używasz tylko cyfr i prawdopodobnie początkowego
myślnika, to LISP może pomyśleć, że to liczba całkowita, a nie symbol.)
Przykłady symboli:
a
b
c1
foo
bar
baaz-quux-garply
Dalej przedstawiamy kilka rzeczy, które można zrobić z symbolami.
(Rzeczy po znaku zachęty ">" są tym, co należy wstukać do interpretera
LISP, podczas gdy inne rzeczy sÄ… odpowiedziami, drukowanymi przez LISP.
Znak ";" jest LISP-owym znakiem komentarza: wszystko od ";" do końca
linii jest ignorowane.
> (setq a 5) ;zachowaj liczbę jako wartość symbolu
5
> a ;pobierz wartość symbolu
5
> (let ((a 6)) a) ;przywiąż tymczasowo wartość symbolu do 6
6
> a ;po zakończeniu let, wartość wraca do 5
5
> (+ a 6) ;użyj wartości symbolu jako argumentu funkcji
11
> b ;spróbuj pobrać wartość symbolu, który nie ma wartości
Error: Attempt to take the value of the unbound symbol B
Istnieją dwa symbole specjalne, `t' i `nil'. Wartość `t' jest
zdefiniowana zawsze jako `t', a wartość `nil' jest zdefiniowana zawsze
jako `nil'. LISP używa `t' i `nil' do reprezentowania prawdy i fałszu.
Przykładowe użycie, opisane dokładniej dalej:
> (if t 5 6)
5
> (if nil 5 6)
6
> (if 4 5 6)
5
Ostatni przykład jest cudaczny, lecz prawidłowy: `nil' oznacza fałsz, a
wszystko inne oznacza prawdę. (O ile nie mamy potrzeby robić inaczej,
używamy t do oznaczania prawdy, tak dla czystej prostoty.)
Symbole takie jak `t' i `nil' są nazywane samorozwijającymi, ponieważ
rozwijają się same na siebie. Istnieje cała klasa samorozwijających się
symboli, zwanych słowami kluczowymi; dowolny symbol, którego nazwa
rozpoczyna się od dwukropka jest słowem kluczowym. (Zobacz niżej
przykłady zastosowań słów kluczowych.) Przykłady:
> :this-is-a-keyword
:THIS-IS-A-KEYWORD
> :so-is-this
:SO-IS-THIS
> :me-too
:ME-TOO
Liczby
Liczba całkowita jest łańcuchem cyfr, opcjonalnie poprzedzonych + lub
-. Liczba rzeczywista wygląda jak liczba całkowita, lecz ma kropkę
dziesiętną i może być zapisana w notacji naukowej. Liczba wymierna
wygląda jak dwie liczby całkowite z kreską ułamkową / między nimi. LISP
obsługuje też liczbyzespolone, które są zapisywane jako #c(r i) (gdzie
r jest częścią rzeczywistą, a i jest częścią urojoną). Liczbą jest
dowolny zapis z powyższych. Przykłady:
5
17
-34
+6
3.1415
1.722e-15
#c(1.722e-15 0.75)
Standardowe funkcje arytmetyczne są wciąż dostępne: +, -, *, /, floor,
ceiling, mod, sin, cos, tan, sqrt, exp, expt, itd. Wszystkie one
przyjmujÄ… dowolny rodzaj argumentu. +, -, * i / zwracajÄ… liczbÄ™ zgodnie
z następującymi zasadami: liczba całkowita plus wymierna daje wymierną,
wymierna plus rzeczywistÄ… daje rzeczywistÄ…, a rzeczywista plus
zespolona daje zespoloną. Oto przykłady:
> (+ 3 3/4) ;determinacja typu wynikowego
15/4
> (exp 1) ;e
2.7182817
> (exp 3) ;e*e*e
20.085537
> (expt 3 4.2) ;potęga o podstawie innej niż e
100.90418
> (+ 5 6 7 (* 8 9 10)) ;funkcje +-*/ przyjmujÄ… argumenty wielokrotne
Nie ma ograniczeń w absolutnej wartości liczby całkowitej, poza
rozmiarem pamięci komputera. Ostrzegam jednak, że obliczenia na dużych
liczbach mogą być wolne. (Podobnie jak obliczenia na liczbach
wymiernych, szczególnie w porównaniu do obliczeń na małych liczbach
całkowitych lub rzeczywistych.)
Wagoniki (ang. conses)
Wagonik jest zwykłym dwuelementowym rekordem. Z przyczyn historycznych
jego pola są nazywane "car" i "cdr". (Na pierwszej maszynie, na której
zaimplementowano LISP istniały dwie instrukcje, CAR i CDR, które
oznaczały "contents of address register" i "contents of decrement
register". Wagoniki były zaimplementowane przy użyciu tych rejestrów.)
Wagoniki są łatwe w użyciu:
> (cons 4 5) ;Zaalokuj wagonik. Ustaw car na 4 a cdr na 5.
(4 . 5)
> (cons (cons 4 5) 6)
((4 . 5) . 6)
> (car (cons 4 5))
4
> (cdr (cons 4 5))
5
Listy
Z wagoników można budować wiele struktur. Pewnie najprostszą jest lista
powiązana: car każdego wagonika wskazuje na jeden z elementów listy, a
cdr albo na następny element, albo na nil. Możesz stworzyć taką
powiązaną listę przy użyciu funkcji list:
> (list 4 5 6)
(4 5 6)
Zauważ, że LISP drukuje powiązane listy w szczególny sposób: pomija
niektóre kropki i nawiasy. Zasada jest taka: jeśli cdr wagonika to nil,
LISP nie drukuje kropki, ani nil; jeśli cdr wagonika A to wagonik B, to
nie drukuje kropki dla wagonika A, ani nawiasów dla wagonika B. Więc:
> (cons 4 nil)
(4)
> (cons 4 (cons 5 6))
(4 5 . 6)
> (cons 4 (cons 5 (cons 6 nil)))
(4 5 6)
Ostatni przykład jest równoważny dokładnie wywołaniu (list 4 5 6).
Zauważ, że nil oznacza teraz listę bez elementów: cdr listy 2
elementowej (a b) to (b), lista o jednym elemencie; cdr 1 elementowego
(b) to nil, który jest listą bez elementów.
Pusty (nil) car i cdr jest definiowany jako `nil'.
Jeśli zapisujesz swoją listę w zmiennej, możesz spowodować, że będzie
działać jak stos:
> (setq a nil)
NIL
> (push 4 a)
(4)
> (push 5 a)
(5 4)
> (pop a)
5
> a
(4)
> (pop a)
4
> (pop a)
NIL
> a
NIL
Funkcje
Powyżej był przykład funkcji. Oto kilka innych:
> (+ 3 4 5 6) ;ta funkcja pobiera dowolną liczbę argumentów
18
> (+ (+ 3 4) (+ (+ 4 5) 6)) ;czy notacja przedrostkowa nie jest super?
22
> (defun foo (x y) (+ x y 5)) ;definiowanie funkcji
FOO
> (foo 5 0) ;wołanie funkcji
10
> (defun fact (x) ;funkcja rekursywna
(if (> x 0)
(* x (fact (- x 1)))
1
) )
FACT
> (fact 5)
120
> (defun a (x) (if (= x 0) t (b (- x)))) ;funkcje wzajemnie rekurencyjne
A
> (defun b (x) (if (> x 0) (a (- x 1)) (a (+ x 1))))
B
> (a 5)
T
> (defun bar (x) ;funkcja o wielu instrukcjach w swoim
(setq x (* x 3)) ;ciele -- zwróci wartość, zwracaną przez
(setq x (/ x 2)) ;jej ostatniÄ… instrukcjÄ™
(+ x 4)
)
BAR
> (bar 6)
13
Gdy zdefiniowaliśmy `foo', nadaliśmy mu dwa argumenty, `x' i `y'.
Teraz, gdy wołamy `foo', musimy podać dokładnie dwa argumenty: pierwszy
stanie się na czas wywołania wartością `x', a drugi stanie się
wartością `y'. W lispie większość zmiennych jest zawężona leksykalnie;
to znaczy jeśli `foo' woła `bar', a `bar' próbuje odnieść się do `x',
to nie jest to możliwe.
Proces przyznawania symbolowi wartości na czas zawężeń leksykalnych
jest nazywany wiÄ…zaniem (binding).
Dla swoich funkcji możesz podawać argumenty dodatkowe. Każdy argument
po symbolu &optional jest opcjonalny:
> (defun bar (x &optional y) (if y x 0))
BAR
> (defun baaz (&optional (x 3) (z 10)) (+ x z))
BAAZ
> (bar 5)
0
> (bar 5 t)
5
> (baaz 5)
15
> (baaz 5 6)
11
> (baaz)
13
Funkcję `bar' można wywołać z jednym lub dwoma argumentami. Po
wywołaniu z jednym argumentem, wartość `x' jest ustawiana normalnie,
natomiast `y' jest ustawiane na `nil'. Po wywołaniu z dwoma
argumentami, wartość `x' i `y' zostanie odpowiednio nimi
zainicjalizowana.
Funkcja `baaz' ma dwa argumenty opcjonalne. Dla każdego z nich daje
wartość domyślną: jeśli wołający poda tylko jeden argument, `z'
zostanie związane nie z `nil', lecz z 10, a gdy wołający w ogóle nie
poda argumentów, `x' zostanie powiązany z 3, a `z' z 10.
Możesz spowodować, że funkcja przyjmuje dowolną liczbę argumentów,
kończąc jej argument parametrem &rest. LISP wtedy zbierze wszystkie
argumenty do listy i przywiąże ją do parametru &rest.
> (defun foo (x &rest y) y)
FOO
> (foo 3)
NIL
> (foo 4 5 6)
(5 6)
W końcu, możesz podać swojej funkcji dowolny rodzaj argumentu
opcjonalnego, zwanego argumentem słowa kluczowego. Wołający może
podawać takie argumenty w dowolnej kolejności--są one etykietowane
słowami kluczowymi.
> (defun foo (&key x y) (cons x y))
FOO
> (foo :x 5 :y 3)
(5 . 3)
> (foo :y 3 :x 5)
(5 . 3)
> (foo :y 3)
(NIL . 3)
> (foo)
(NIL)
Parametr &key może mieć również wartość domyślną:
> (defun foo (&key (x 5)) x)
FOO
> (foo :x 7)
7
> (foo)
5
Drukowanie
Niektóre funkcje mogą przesyłać dane na wyjście. Najprostszą jest
print, która drukuje swój argument i zwraca go.
> (print 3)
3
3
Pierwsze 3 zostało wydrukowane, drugie zwrócone.
Jeśli oczekujesz bardziej złożonego wyjścia, musisz użyć formatu. Oto
przykład:
> (format t "An atom: ~S~%and a list: ~S~%and an integer: ~D~%"
nil (list 5) 6)
An atom: NIL
and a list: (5)
and an integer: 6
Pierwszy argument instrukcji format to `t', `nil' lub strumień. `T'
wskazuje na wyjście na terminal. `Nil' oznacza, że niczego nie można
drukować, a należy zamiast tego powrócić do łańcucha zawierającego
wyjście. Strumienie są ogólnymi miejscami przepływu wyjścia: wskazują
na pliki, terminale lub inne programy. Ten podręcznik nie będzie się
dokładniej zajmował strumieniami.
Kolejnym argumentem jest matryca formatująca, która jest łańcuchem
opcjonalnie zawierajÄ…cym dyrektywy formatujÄ…ce.
Wszystkie pozostałe argumenty mogą być używane przez dyrektywy
formatujÄ…ce. LISP zamieni dyrektywy na odpowiednie znaki, w oparciu
argumenty, do których (dyrektywy) się one odnoszą. Następnie łańcuch
zostanie wydrukowany.
Format zawsze zwraca `nil', chyba że jego pierwszym argumentem jest
`nil'--wtedy nic nie drukuje i zwraca łańcuch.
W powyższym przykładzie istnieją trzy różne dyrektywy: ~S, ~D i ~%.
Pierwsza przyjmuje dowolny obiekt LISP i jest zamieniana drukowalnÄ…
reprezentacjÄ… tego obiektu (takÄ… samÄ…, jak produkowana przez print).
Druga przyjmuje tylko liczby całkowite. Ostatnia nie odnosi się do
argumentu. Jest zawsze zamieniana na powrót karetki.
Inną przydatną dyrektywą jest ~~, która jest zamieniana na pojedynczą
`~'.
Zajrzyj do podręcznika LISP, są tam różne inne dyrektywy formatujące.
Formy i pętla główna
Wszystko, co wpisujesz do interpretera LISP określane jest mianem form;
interpreter odczytuje takÄ… formÄ™, analizuje jÄ… i drukuje wynik.
Procedura ta jest nazywana pętlą odczytu-analizy-drukowania.
Niektóre formy mogą dawać błędy. Po błędzie, LISP przerzuci cię do
debuggera, abyś mógł znaleźć błąd. Każdy debugger LISP jest inny;
jednak większość z nich odpowiada na komendę "help" lub ":help".
Ogólnie, formą jest zarówno atom (np. symbol, liczba całkowita,
łańcuch) jak lista. Jeśli forma jest atomem, LISP analizuje ją
natychmiast. Jeśli jest listą, to jej pierwszy element jest traktowany
jak nazwa funkcji; pozostałe elementy analizowane są rekurencyjnie i
wywołuje się tę funkcję z wartościami pozostałych elementów jako
argumentami.
Na przykład, jeśli LISP widzi formę (+ 3 4), traktuje `+' jako nazwę
funkcji. Potem analizuje 3 aby pobrać 3 i 4 aby pobrać 4; w końcu woła
`+' z argumentami 3 i 4. Funkcja `+' zwraca 7, co jest drukowane jako
wynik.
Pętla główna daje inne udogodnienia; szczególnie wygodną rzeczą jest
możliwość mówienia o wynikach poprzednio wpisanych form. LISP zawsze
udostępnia swoje trzy najświeższe wyniki; kryją się one pod symbolami
*, ** i ***. Na przykład:
> 3
3
> 4
4
> 5
5
> ***
3
> ***
4
> ***
5
> **
4
> *
4
Formy specjalne
Istnieje pewna liczba form specjalnych, które wyglądają jak wywołania
funkcji, lecz nimi nie sÄ…. ZaliczajÄ… siÄ™ do nich konstrukcje sterujÄ…ce,
takie jak instrukcje if i pętle do; przypisania takie jak setq, setf,
push i pop; definicje takie, jak defunidefstruct; a także konstrukcje
wiążące, takie jak let. (Nie wszystkie te formy zostały już wymienione.
Patrz niżej.)
JednÄ… przydatnÄ… formÄ… specjalnÄ… jest forma cytowania (quote): chroni
ona argument przed zanalizowaniem. Na przykład:
> (setq a 3)
3
> a
3
> (quote a)
A
> 'a ;'a jest skrótem dla (quote a)
A
Inną prostą formą specjalną jest forma funkcji (function): powoduje, że
jej argument jest interpretowany jako funkcja:
> (setq + 3)
3
> +
3
> '+
+
> (function +)
#<Function + @ #x-fbef9de>
> #'+ ;#'+ jest skrótem dla (function +)
#<Function + @ #x-fbef9de>
Ta forma specjalna jest przydatna gdy chcesz przekazać funkcję jako
argument do innej funkcji. Niżej są przykłady funkcji, biorących
argumenty funkcyjne.
WiÄ…zanie
Wiązanie to zawężone leksykalnie przypisanie. Zachodzi na zmiennych w
liście parametrów funkcji za każdym wywołaniem funkcji: formalne
parametry są wiązane do rzeczywistych parametrów na czas wywołania
funkcji. Zmienne można wiązać w dowolnym miejscu programu, używając
specjalnej formy let, która wygląda tak:
(let ((var1 val1)
(var2 val2)
...
)
body
)
`Let' wiąże `var1' do `val1' i `var2' do `val2' (i tak dalej); potem
wykonuje instrukcje swojego ciała. Ciało `let' podpada pod dokładnie te
same reguły, co ciało funkcji. Przykłady:
> (let ((a 3)) (+ a 1))
4
> (let ((a 2)
(b 3)
(c 0))
(setq c (+ a b))
c
)
5
> (setq c 4)
4
> (let ((c 5)) c)
5
> c
4
Zamiast (let ((a nil) (b nil)) ...), możesz napisać (let (a b) ...).
`Val1', `val2', itd. wewnątrz `let' nie mogą odnosić się do zmiennych
`var1', `var2', itd., które są wiązane. Na przykład
> (let ((x 1)
(y (+ x 1)))
y
)
Error: Attempt to take the value of the unbound symbol X
Jeśli symbol `x' już ma wartość globalną, wynikiem będą dziwne
zdarzenia:
> (setq x 7)
7
> (let ((x 1)
(y (+ x 1)))
y
)
8
Forma specjalna let* jest podobna do `let', lecz zezwala wartościom na
wskazywanie na zmienne, zdefiniowane w `let' wcześniej. Na przykład:
> (setq x 7)
7
> (let* ((x 1)
(y (+ x 1)))
y
)
2
Forma
(let* ((x a)
(y b))
...
)
jest równoważna
(let ((x a))
(let ((y b))
...
) )
Zakresy dynamiczne
Formy `let' i `let*' dają zawężanie leksykalne, które jest rzeczą, do
której mogłeś się przyzwyczaić w C lub Pascalu. Zakresy (zawężanie)
dynamiczne są popularne w BASIC-u: jeśli przypiszesz wartość zmiennej
dynamicznej, każde wspomnienie tej zmiennej zwraca nadaną wartość, aż
nie przyznasz nowej.
W LISP-ie, zmienne dynamicznego zakresu sÄ… nazywane zmiennymi
specjalnymi. Możesz je deklarować w formie defvar. Oto przykłady
zmiennych dynamicznie i leksykalnie zawężanych.
W tym przykładzie, funkcja `check-regular' odnosi się do zmiennej
`regular' (np. leksykalnej). Ponieważ `check-regular' jest leksykalnie
poza `let', który wiąże `regular', `check-regular' zwraca globalną
wartość zmiennej.
> (setq regular 5)
5
> (defun check-regular () regular)
CHECK-REGULAR
> (check-regular)
5
> (let ((regular 6)) (check-regular))
5
W tym przykładzie, funkcja `check-special' odnosi się do zmiennej
`special' (np. dynamicznie zawężonej). Ponieważ wywołanie `check-
special' jest tymczasowo wewnątrz `let', który wiąże `special', `check-
special' zwraca lokalną wartość zmiennej.
> (defvar *special* 5)
*SPECIAL*
> (defun check-special () *special*)
CHECK-SPECIAL
> (check-special)
5
> (let ((*special* 6)) (check-special))
6
Tradycyjnie, nazwa zmiennej specjalnej rozpoczyna siÄ™ od `*'. Zmienne
specjalne są używane głównie jako zmienne globalne, gdyż programiści
zazwyczaj oczekują dla zmiennych lokalnych zawężania leksykalnego, a
dla globalnych dynamicznego.
Dla dalszych informacji o różnicach między zawężeniami leksykalnymi i
dynamicznymi, zobacz Common LISP: the Language.
Tablice
Funkcja make-array tworzy tablicę. Funkcja aref daje dostęp do jej
elementów. Wszystkie elementy tablicy są początkowo ustawione na `nil'.
Na przykład:
> (make-array '(3 3))
#2a((NIL NIL NIL) (NIL NIL NIL) (NIL NIL NIL))
> (aref * 1 1)
NIL
> (make-array 4) ;tablice 1D nie wymagają dodatkowych nawiasów
#(NIL NIL NIL NIL)
Indeksy tablic zawsze rozpoczynajÄ… siÄ™ od 0.
Jak ustawiać elementy tablicy, opisano niżej.
Napisy
Napis jest sekwencją znaków w podwójnych cudzysłowach. W LISP napis
jest reprezentowany jako tablica znaków o zmiennej długości. Napis
zawierający podwójne cudzysłowy można zapisać, poprzedzając cudzysłów
znakiem lewego ukośnika; podwójny lewy ukośnik oznacza pojedynczy lewy
ukośnik. Na przykład:
"abcd" ma 4 znaki
"\"" ma 1 znak
"\\" ma 1 znak
Oto kilka funkcji zajmujÄ…cych siÄ™ napisami:
> (concatenate 'string "abcd" "efg")
"abcdefg"
> (char "abc" 1)
#\b ;LISP zapisuje znaki poprzedzone #\
> (aref "abc" 1)
#\b ;pamiętaj, napisy w rzeczywistości są tablicami
Funkcja concatenate może w rzeczywistości działać z dowolnym rodzajem
sekwencji:
> (concatenate 'string '(#\a #\b) '(#\c))
"abc"
> (concatenate 'list "abc" "de")
(#\a #\b #\c #\d #\e)
> (concatenate 'vector '#(3 3 3) '#(3 3 3))
#(3 3 3 3 3 3)
Struktury
Struktury LISP są analogiczne do struktur C lub rekordów Pascala. Oto
przykład:
> (defstruct foo
bar
baaz
quux
)
FOO
Przykład ten definiuje typ danych o nazwie `foo', który jest strukturą,
zawierającą 3 pola. Definiuje też 4 funkcje, które operują na tym typie
danych: make-foo, foo-bar, foo-baaz i foo-quux. Pierwsza tworzy nowy
obiekt typu `foo'; pozostałe dają dostęp do poszczególnych pól obiektu.
Oto jak używać tych funkcji:
> (make-foo)
#s(FOO :BAR NIL :BAAZ NIL :QUUX NIL)
> (make-foo :baaz 3)
#s(FOO :BAR NIL :BAAZ 3 :QUUX NIL)
> (foo-bar *)
NIL
> (foo-baaz **)
3
Funkcja `make-foo' może pobierać argument słowa kluczowego dla każdego
pola struktury `foo'. Funkcje dostępu do pól pobierają jeden argument,
strukturę typu `foo' i zwracają odpowiednią wartość.
Zobacz niżej jak ustawić pola struktury.
Setf
Pewne formy w LISP-ie naturalnie definiują pozycję w pamięci. Na
przykład, jeśli wartość `x' jest strukturą typu `foo', to (foo-bar x)
definiuje pole `bar' o wartości `x'. Lub jeśli wartość `y' jest
jednowymiarowÄ… tablicÄ…, (aref y 2) definiuje trzeci element `y'.
Specjalna forma setf używa swojego pierwszego argumentu do
zdefiniowania miejsca w pamięci, analizuje drugi argument i zapisuje
wynik w wynikowej pozycji pamięci. Na przykład,
> (setq a (make-array 3))
#(NIL NIL NIL)
> (aref a 1)
NIL
> (setf (aref a 1) 3)
3
> a
#(NIL 3 NIL)
> (aref a 1)
3
> (defstruct foo bar)
FOO
> (setq a (make-foo))
#s(FOO :BAR NIL)
> (foo-bar a)
NIL
> (setf (foo-bar a) 3)
3
> a
#s(FOO :BAR 3)
> (foo-bar a)
3
Setf jest jedynym sposobem na ustawianie pól struktury lub tablicy.
Oto kilka innych przykładów setf i związanych z nim funkcji.
> (setf a (make-array 1)) ;setf na zmiennej jest równoważne setq
#(NIL)
> (push 5 (aref a 1)) ;push może działać jak setf
(5)
> (pop (aref a 1)) ;podobnie pop
5
> (setf (aref a 1) 5)
5
> (incf (aref a 1)) ;incf odczytuje z danego miejsca,
6 ;inkrementuje i zapisuje z powrotem.
> (aref a 1)
6
Zmienne logiczne i warunki
LISP do oznaczania fałszu używa symbolu `nil'. Wszystko inne niż `nil'
oznacza prawdę. O ile nie mamy specjalnych powodów, dla prawdy używa
siÄ™ symbolu `t'.
LISP daje standardowy zestaw funkcji logicznych, np. and, or i not.
And i or są blisko-zwierające: and nie zanalizuje żadnych argumentów na
prawo od pierwszego zrozumianego jako nil, a or nie zanalizuje żadnych
argumentów na prawo od pierwszego, zrozumianego jako t.
LISP daje też specjalne formy dla wyrażeń warunkowych. Najprostszą jest
if. Pierwszy argument tej formy określa czy wywołać argument drugi, czy
trzeci.
> (if t 5 6)
5
> (if nil 5 6)
6
> (if 4 5 6)
5
Jeśli potrzebujesz więcej niż jednej instrukcji w klauzuli then lub
else if'a, to możesz użyć specjalnej formy progn. Progn wykonuje każdą
instrukcję swojego ciała i zwraca ostatnią wartość.
> (setq a 7)
7
> (setq b 0)
0
> (setq c 5)
5
> (if (> a 5)
(progn
(setq a (+ b 7))
(setq b (+ c 8)))
(setq b 4)
)
13
Instrukcja if, której brak klauzuli then lub else, może być zapisana
przy użyciu specjalnej formy unless:
> (when t 3)
3
> (when nil 3)
NIL
> (unless t 3)
NIL
> (unless nil 3)
3
When i unless w przeciwieństwie do if, zezwalają na dowolną liczbę
instrukcji w swoich ciałach. (Np. (when x a b c) jest równoważne (if x
(progn a b c)).
> (when t
(setq a 5)
(+ a 6)
)
11
Bardziej złożone warunki można definiując przy użyciu formy specjalnej
cond która jest równoważna konstrukcji if ... else if .. fi.
Cond składa się z symbolu `cond', za którym następują klauzule cond, z
których każda jest listą. Pierwszy element klauzuli cond jest
warunkiem; pozostałe elementy (jeśli istnieją) są akcją. Forma cond
szuka pierwszej klauzuli, której warunek jest spełniony; potem wykonuje
odpowiednią akcję i zwraca wartość wynikową. Żaden pozostały warunek
nie jest już analizowany; nie są też wykonywane inne akcje niż ta,
odpowiadająca warunkowi. Na przykład:
> (setq a 3)
3
> (cond
((evenp a) a) ;jeśli a jest parzyste, zwróć a
((> a 7) (/ a 2)) ;inaczej, jeśli a jest > niż 7, zwróć a/2
((< a 5) (- a 1)) ;inaczej, jeśli a jest < niż 5, zwróć a-1
(t 17) ;inaczej zwróć 17
)
2
Jeśli w danej klauzuli cond brakuje akcji, cond zwraca wartość, do
której został zredukowany warunek:
> (cond ((+ 3 4)))
7
Oto mała sprytna rekurencyjna funkcja, używająca cond. Możesz spróbować
udowodnić, że kończy się dla wszystkich liczb całkowitych x wielkości
przynajmniej 1. (Jeśli ci się uda, opublikuj rezultat.)
> (defun hotpo (x steps) ;hotpo oznacza Half Or Triple Plus One
(cond
((= x 1) steps)
((oddp x) (hotpo (+ 1 (* x 3)) (+ 1 steps)))
(t (hotpo (/ x 2) (+ 1 steps)))
) )
A
> (hotpo 7 0)
16
Instrukcja case w LISP jest podobna do instrukcji switch w C:
> (setq x 'b)
B
> (case x
(a 5)
((d e) 7)
((b f) 3)
(otherwise 9)
)
3
Klauzula otherwise oznacza, że jeśli x nie jest a, b, d, e lub f,
instrukcja case ma zwrócić 9.
Iteracja
Najprostszą konstrukcją iteracyjną w LISP jest pętla (loop):
konstrukcja ta kolejno wykonuje swoje ciała, aż nie natrafi na
specjalną formę return. Na przykład
> (setq a 4)
4
> (loop
(setq a (+ a 1))
(when (> a 7) (return a))
)
8
> (loop
(setq a (- a 1))
(when (< a 3) (return))
)
NIL
Kolejną najprostszą jest dolist: wiąże ona zmienną do elementów listy w
kolejności i kończy gdy trafi na koniec listy.
> (dolist (x '(a b c)) (print x))
A
B
C
NIL
Dolist zawsze zwraca `nil'. Zauważ, że wartość `x' w powyższym
przykładzie nigdy nie była `nil': NIL znajdujący się po C był wartością
zwracaną przez dolist, drukowaną przez pętlę odczytu-analizy-
drukowania.
Najbardziej skomplikowanym rodzajem iteracji jest do. Instrukcja do
wygląda następująco:
> (do ((x 1 (+ x 1))
(y 1 (* y 2)))
((> x 5) y)
(print y)
(print 'working)
)
1
WORKING
2
WORKING
4
WORKING
8
WORKING
16
WORKING
32
Pierwsza część `do' określa, które zmienne należy związać, jakie są ich
wartości początkowe i jak je odświeżać. Następna część określa warunek
zakończenia i wartość zwracaną. Ostatnią częścią jest ciało. Forma do
wiąże jak `let' swoje zmienne do wartości początkowych, a potem
sprawdza warunek zakończnia. Dopóki warunek jest fałszywy, wykonuje
ciało; gdy warunek staje się prawdziwy, zwraca wartość formy return-
value.
Forma do* jest analogiczna do `let*' w `let'.
Nielokalne wyjścia
Forma specjalna return, wspomniana w poprzedniej sekcji o iteracji jest
przykładem nielokalnego wyjścia. Innym przykładem jest forma return-
form, która zwraca wartość z otaczającej funkcji:
> (defun foo (x)
(return-from foo 3)
x
)
FOO
> (foo 17)
3
Właściwie, forma return-form może zwrócić wartość z dowolnego nazwanego
bloku--po prostu funkcje są jedynymi blokami, które są domyślnie
nazwane. Blok nazwany możesz tworzyć specjalną formą block:
> (block foo
(return-from foo 7)
3
)
7
Forma specjalna return może zwrócić wartość z dowolnego bloku o nazwie
`nil'. Domyślnie przez nil oznaczane są pętle, lecz możesz też tworzyć
własne bloki oznaczone `nil':
> (block nil
(return 7)
3
)
7
Inną formą, która powoduje nielokalne wyjście jest forma error:
> (error "This is an error")
Error: This is an error
Forma error załącza format do swoich argumentów, a potem umieszcza cię
w debuggerze.
Funcall, Apply, i Mapcar
Wcześniej obiecałem, że dam trochę funkcji, które jako argumenty
pobierajÄ… funkcje. Oto one:
> (funcall #'+ 3 4)
7
> (apply #'+ 3 4 '(3 4))
14
> (mapcar #'not '(t nil t nil t nil))
(NIL T NIL T NIL T)
Funcall woła swój pierwszy argument z pozostałymi argumentami.
Apply działa podobnie do funcall, lecz jej ostatni argument powinien
być listą; elementy tej listy są traktowane tak, jakby były dodatkowymi
argumentami funcall.
Pierwszy argument mapcar musi być funkcją jednoargumentową; mapcar
stosuje tą funkcję do każdego elementu listy i zbiera wyniki w innej
liście.
Funcall i apply są używane często gdy ich pierwszym argumentem jest
zmienna. Na przykład mechanizm przeszukiwania może brać jako parametr
funkcję heurystyczną i używać funcall lub apply do wołania jej dla
opisu stanu. Funkcje sortowania, opisane dalej używają funcall do
wołania funkcji porównawczych.
Mapcar wraz funkcjami bez nazwy (patrz niżej) może zastąpić wiele
pętli.
Lambda
Jeśli chcesz utworzyć funkcję tymczasową i nie chcesz nadawać jej
nazwy, to możesz użyć lambda.
> #'(lambda (x) (+ x 3))
(LAMBDA (X) (+ X 3))
> (funcall * 5)
8
Połączenie lambda i mapcar może zastąpić wiele pętli. Na przykład,
następujące postacie są równoważne:
> (do ((x '(1 2 3 4 5) (cdr x))
(y nil))
((null x) (reverse y))
(push (+ (car x) 2) y)
)
(3 4 5 6 7)
> (mapcar #'(lambda (x) (+ x 2)) '(1 2 3 4 5))
(3 4 5 6 7)
Sortowanie
LISP daje do sortowania dwa prymitywy: sort i stable-sort.
> (sort '(2 1 5 4 6) #'<)
(1 2 4 5 6)
> (sort '(2 1 5 4 6) #'>)
(6 5 4 2 1)
Pierwszym argumentem sort jest lista; drugim funkcja porównawcza.
Funkcja sortująca nie gwarantuje stabilności: jeśli są dwa elementy
takie, że (and (not (< a b)) (not (< b a))) , to sort może zaaranżować
je w dowolnym porzÄ…dku. Funkcja stable-sort jest taka sama jak sort,
lecz gwarantuje, że dwa równoważne elementy pojawią się w liście
posortowanej w tej samej kolejności co na liście wejściowej.
Uwaga: sort może niszczyć swój argument, więc jeśli sekwencja wejściowa
jest dla ciebie ważna, utwórz jej kopię poleceniem copy lub copy-list
czy copy-seq.
Równość
LISP ma wiele różnych wizji równości. Równość numeryczna jest oznaczana
przez =. Dwa symbole sÄ… eq tylko gdy sÄ… identyczne. Dwie kopie tej
samej listy nie są eq, choć są równe.
> (eq 'a 'a)
T
> (eq 'a 'b)
NIL
> (= 3 4)
T
> (eq '(a b c) '(a b c))
NIL
> (equal '(a b c) '(a b c))
T
> (eql 'a 'a)
T
> (eql 3 3)
T
Eql jest równoważny eq dla symboli i = dla liczb.
Equal jest równoważny eql dla symboli i liczb. Jest prawdziwy dla dwóch
wagoników tylko gdy ich car-y i cdr-y są sobie równe. Jest prawdziwy
dla dwóch struktur tylko gdy wszystkie ich pola są sobie równe.
Przydatne funkcje listowe
Oto funkcje, działające na listach.
> (append '(1 2 3) '(4 5 6)) ;łącz (konkatenuj) listy
(1 2 3 4 5 6)
> (reverse '(1 2 3)) ;odwróć elementy listy
(3 2 1)
> (member 'a '(b d a c)) ;ustaw członkowstwo--zwraca pierwszy ogon
(A C) ;w którym car jest żądanym elemetem.
> (find 'a '(b d a c)) ;inny sposób ustawiania członkowstwa
A
> (find '(a b) '((a d) (a d e) (a b d e) ()) :test #'subsetp)
(A B D E) ;find jest jednak bardziej elastyczny
> (subsetp '(a b) '(a d e)) ;zawieranie się zbiorów
NIL
> (intersection '(a b c) '(b)) ;przekrój zbiorów
(B)
> (union '(a) '(b)) ;suma zbiorów
(A B)
> (set-difference '(a b) '(a)) ;różnica zbiorów
(B)
Subsetp, intersection, union i set-difference zakładają, że żaden z
argumentów nie posiada zduplikowanych elementów. Np. (subsetp '(a a)
'(a b b)) może się nie powieść.
Find, subsetp, intersection, union i set-difference mogą brać argument
o słowie kluczowym :test; domyślnie wszystkie używają eql.
PoczÄ…tki z Emacsem
Do edycji kodu LISP możesz używać Emacsa: większość Emacsów jest
skonfigurowania na wchodzenie w tryb LISP za każdym razem gdy znajdą
plik, kończący się na .lisp. Jeśli u ciebie tak się nie dzieje, wpisz
M-x lisp-mode.
Pod Emacsem możesz też uruchamiać LISP: upewnij się, że masz w ścieżce
komendę o nazwie lisp, która uruchamia twojego ulubionego LISP-a. Na
przykład możesz wpisać
ln -s /usr/local/bin/clisp ~/bin/lisp
Potem, w Emacsie wpisz M-x run-lisp. Możesz wtedy wysłać kod LISP do
interpretera i zrobić wiele innych fajnych rzeczy; dla dalszych
informacji wpisz C-h m z dowolnego bufora w trybie LISP.
W rzeczywistości nawet nie musisz tworzyć dowiązania. Emacs ma zmienną
o nazwie inferior-lisp-program; więc jeśli dodasz linię
(setq inferior-lisp-program "/usr/local/bin/clisp")
do swojego pliku .emacs, to Emacs będzie wiedział gdzie szukać CLISP-a
dla komendy M-x run-lisp.
AUTORZY
Geoffrey J. Gordon <ggordon@cs.cmu.edu> (piÄ…tek, 5 lutego 1993).
Poprawione przez Brunona Haible'a <haible@ma2s2.mathematik.uni-
karlsruhe.de>. Przetłumaczone na język polski i przekonwertowane do
postaci man przez Przemka Borysa <pborys@dione.ids.pl>
wrzesień 1999 LISP-TUT(5)