Common lisp


Tahle skolicka je pro ty co se rozhodli zkusit v lispu neco udelat. Je to spis jenom seznam funkci. Vsechny zajimave konstrukce rozeberu ve specialnich skolickach. K jejimu pochopeni je nutna skolicka o zakladech lispu


Obsah:


Uvod

Common lisp je nejrozsirenejsi dialekt lispu. Vetsina ostatnich je snim docela kompatibilni. Je zcela univerzalni takze v nem jde udelat opravdu hodne. Ja osobne ho nepovazuju za nejlepsi podle me je moc komplikovanej. Jeho uplna specifikace je v:

Guy L. Steele Jr. _Common LISP: the Language_. Digital Press. 1984.

Prvni vydani melo 500 stran a druhe uz 1000. To ukazuje jak tenhle standart je slozity. Proto jen naproste zaklady:

(Budu se drzet tutorialnu dodavanym z clispem-soucast slakwaru take ho muzete s polu z jinymi najit na CMU)

Symboly

Common lisp vsechny symboly dava upper case(jako pascal).

Promene

nastavovani promenych se dela pomoci setq. Lokalni promene se delaji pomoci let:

> (setq a 5)            ;ulozi cislo jako hodnotu symbolu
5
> a                     ;a je tam :)
5
> (let ((a 6)) a)       ;docasny nastaveni acka na 6
6
> a                     ;a je zase spatky petka
5
> b                     ;nenastaveny symbol
Error: Attempt to take the value of the unbound symbol B

Self evaluating symboly

To jsou symboly co se vyhodnocuji samy do sebe-jejich obsach je zase tensymbol samozdrejme ze muzem takovy symbol udelat:
(setq a 'a)
Ale je tu nekolik specialnich: t a nil (true,false) jejich funkci jsem uz popsal

Ale jsou tu jeste keywordy-to jsou taky self evaluating a zacinaji na : priklad:

> :this-is-a-keyword
:THIS-IS-A-KEYWORD
> :me-too
:ME-TOO

Cisla

integer
muze zacinat + - a nesmi obsahovat . dulezite je to, ze integer muze byt libovolne dlouhy-tedy pouziva uz popsanou knihovnu gmp.
real
jako integer ale musi mit desetinou tecku taky jde vedecky zapis
rational
zlomky-dva integery a / mezi
complex
zapis: #c(r i) r i muze byt libovolny predchazejici typ
a vsechny jsou zamozdrejme typu number a atomic priklady:
        5
        -34
        +6
        3.1415
        1.722e-15
        #c(1.722e-15 0.75)
Ma funkce na beznou aritmetiku:+, -, *, /, floor,ceiling, mod, sin, cos, tan, sqrt, exp, expt atd.. Vsechno samozdrejme dela prislusne prevody nahoru-jako cecko:
> (+ 3 3/4)             ;prevod nahoru
15/4
> (exp 1)               ;e
2.7182817
> (+ 5 6 7 (* 8 9 10))  ;fce +-/* berou vic argumentu

Cons

To uz bylo popsano tedy dve casti car a cdr(podle "contents of address register" a "contents of decrement register") zapis: (car.cdr) jsou tu tri funkce na praci:cons car a cdr. Funkce je asi jasna:
> (cons 4 5)            ;vytvori cons. Nastavi 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

Jak uz jsem psal specialni forma consu, specialni format na tisknuti:
(4 5 6)
jde vytvorit funkci:
> (list 4 5 6)
(4 5 6)
nebo:
> '(4 5 6)
(4 5 6)
A samozdrejme pomoci funkce cons:
> (cons 4 nil)
(4)
> (cons 4 (cons 5 6))
(4 5 . 6)
> (cons 4 (cons 5 (cons 6 nil)))
(4 5 6)
List je pouzivan take jako zasobnik:
> (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
(Takoveho zasobniku vyuziva treba funkce let nebo funcall)

Funkce

Ty jsou tu opravdu dobre udleane. Nekolik prikladu:
> (+ 3 4 5 6)                   ;libovolny pocet parametru
18
> (+ (+ 3 4) (+ (+ 4 5) 6))     ;matematika je tu celkem srandovni...
22
> (defun foo (x y) (+ x y 5))   ;nova funkce
FOO
> (foo 5 0)                     ;a uz ji volame..
10
> (defun fact (x)               ;bezna rekurze-zkuste treba (fact 1995) to je
teprve sranda!
    (if (> x 0); specialni forma if a jeji prvni parametr typu
boolean
      (* x (fact (- x 1)));true vetev ifu
      1;false vetev
  ) )
FACT
> (fact 5);a pocitame!
120
> (defun a (x) (if (= x 0) t (b (- x))))        ;navzajem vnorene funkce 
slapou 
taky
A
> (defun b (x) (if (> x 0) (a (- x 1)) (a (+ x 1))))
B
> (a 5)
T
> (defun bar (x)                ;funkce z vice formama vraci hodnotu posledni 
    (setq x (* x 3))            ;formy
    (setq x (/ x 2))            ;takze tyhle
    (+ x 4)
  )
BAR
> (bar 6)
13
no takhle jsem si zablbnul naposled v basicu na sharpovi :) Kdyz nadefinujete foo z dvema parametrama-jako je v prikladu tak volani chce opravdu jen dva parametry-jinak to rve(to neni tak uplne bezne v lispech) Pri volani se samozdrejme letem udelaji dve promene-x a y a prvni parametr se vycisli do x a druhy do y takze volani foo se provede nejak takhle:
(let ((x ) (y ))
     (+ x y 5)
)
to presne udela funkce funcall-tady vidite ze i volani funkce jde v lispu nejak opsat. Common lisp dela navic lexikal scoping to znamena ze schova lokalni promene funkce pred volanim dalsi funkce-tedy funkce + uz nemuze zapisovat do promene x a y ktere pouziva funkce foo Jak jsem uz rikal lisp podporuje atributy parametru. Zakladni je & optional:
> (defun bar (x & optional y) (if y x 0)); volitebny prarametr
BAR
> (defun baaz (& optional (x 3) (z 10)) (+ x z));tady se nastavuje default 
hodnota na neco jinyho nez standartni nil
BAAZ
> (bar 5)
0
> (bar 5 t)
5
> (baaz 5)
15
> (baaz 5 6)
11
> (baaz)
13
asi neni co dodat...
Dalsi je & rest ktery se udava jen u posledniho parametru a spusobi ze dalsi parametry se nechaji v listu a predaji funkci-to pouziva treba funkce +
> (defun foo (x & rest y) y)
FOO
> (foo 3)
NIL
> (foo 4 5 6)
(5 6)
No a posledni & key ktery vylepsuje nastavoavni promenych pri volani:
> (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)
a samozdrejme default hodnota:
> (defun foo (& key (x 5)) x)
FOO
> (foo :x 7)
7
> (foo)
5
Jsou tu jeste makra-funkce co nespracovavaji svoje parametry. To co je defun a funcall pro funkce je defmacro a exmand pro makra. Je nutne si hlidat tluceni lokalnich promenych a tak jejich psani je o neco slozitejsi.

Vypisovani

Zaklad jsou funkce print a format. Nebudu vic rozebirat jen priklady:
> (print 3)
3
3
> (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
pro tisteni ~ je ~~

Forms and the Top-Level Loop

Clisp podporu historii na tri zpet:
> 3
3
> 4
4
> 5
5
> ***
3
> ***
4
> ***
5
> **
4
> *
4

Specialni formy

Uz jsem popsal jsou to tedy neco jako makra psana v cecku zaklad je quote co vraci svuj parametr a jde taky napsat jako '
> (setq a 3)
3
> a
3
> (quote a)
A
> 'a                    ;'a is an abbreviation for (quote a)
A
Jina specialni forma je function. priklady:
> (setq + 3)
3
> +
3
> '+
+
> (function +)
#
> #'+                   ;#'+ is an abbreviation for (function +)
#

Binding

K tomu jsou tu dve spec. formy let a let* volani:
        (let ((var1 val1)
              (var2 val2)
              ...
             )
          body
        )
let udela lokalni promeni var1,var2... a da jim hodnoty val1,val2 atd... Poprovedeni body se promene opet zrusi priklady:
> (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
val1...nemuzou se odkazovat na var1...:
> (let ((x 1)
        (y (+ x 1)))
    y
  )
Error: Attempt to take the value of the unbound symbol X
to dela potize pri:
> (setq x 7)
7
> (let ((x 1)
        (y (+ x 1)))
    y
  )
8
to umoznuje forma let*:
> (setq x 7)
7
> (let* ((x 1)
         (y (+ x 1)))
    y
  )
2
ta vlastne dela:
        (let* ((x a)
               (y b))
          ...
        )
je stejne jako:
        (let ((x a))
          (let ((y b))
            ...
        ) )
pokud je hodnota nil da se nahradit (var1 nil) varem primo

Dynamic Scoping

Let ma stejne pojeti lokalnich promenych jako v cecku tedy pri volani funkce se schovaji-to je lexical scoping. Dynamic scoping to dela stejne jako v basicu tedy neschova je

Lexical Scoping

> (setq regular 5)
5
> (defun check-regular () regular)
CHECK-REGULAR
> (check-regular)
5
> (let ((regular 6)) (check-regular))
5
Promene co zustavaji se jmenuji specialni promene a vetsinou se pojmenuvavaji z * na zacatku a konci: *special* takove promene se delaji pomoci makra defvar:
> (defvar *special* 5)
*SPECIAL*
> (defun check-special () *special*)
CHECK-SPECIAL
> (check-special)
5
> (let ((*special* 6)) (check-special))
6

Pole

To je dalsi clispovej typ. Vytvari se funkci make-array a funkci aref se pristupuje. Podporuji se i vicerozmerne pole:
> (make-array '(3 3))
#2a((NIL NIL NIL) (NIL NIL NIL) (NIL NIL NIL))
> (aref * 1 1)
NIL
> (make-array 4)        ;1D arrays don't need the extra parens
#(NIL NIL NIL NIL)
Cisluje se vzdy od 0

Stringy

Je pole charu-dalsi typ. Zapis je uplne stejny jako v cecku. Vsechny funkce na pole pracuji i z stringy-specialni pripad pole. Je tu hodne stringovych funkci. Podporuje backshlasovou konevkci-\\ \" \n apod. priklady:
> (concatenate 'string "abcd" "efg")
"abcdefg"
> (char "abc" 1)
#\b                     ;LISP pise chary z #\
> (aref "abc" 1)
#\b                     ;stringy jsou pole..
vic zmatku:
concentrate funguje z libovolnou sentensi-sentense je list,string nebo pole preste neco z prvky. Maji spolecne funkce pro praci s nimi-jako aref:
> (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

Jsou vlastne listy popisu v zvlast v jine skolicce

setf

To je vlastne prace z ukazatelama-toho se casto vyuziva treba pri praci ze strukturama. setf je tedy vlastne neco jako *a=hodnota. Takze setf vyhodnoti svuj prni ukazatel, veme ho jako ukazatel a da tam svuj druhy vyhodnoceny parametr. Vpodstate kazdy objekt v lispu je ukazatel. Takze kdyz aref vraci nty prvek pole vraci vlastne ukazatel do toho pole kdyz nastavime promenou na jeho hodnotu objekt se rozdvoji. Ale kdyz zmenime hodnotu objektu na tom ukazatelu zmenime prvek pole:
> (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 
Takze to je jedina cesta na meneni hdonot prvku pole apod. vic prikladu:
> (setf a (make-array 1))       ;setf na promenou je setq
#(NIL)
> (push 5 (aref a 1))           ;push muze fungovat jako setf-ono to vlastne 
setf je-zamyslete se
(5)
> (pop (aref a 1))              ;a samozdrejme muzem popovat
5
> (setf (aref a 1) 5)
5
> (incf (aref a 1))             ;incf je jako setf ale zvetsi o jedna
6
> (aref a 1)
6

Boolean

nil je false vsechno jiny je true bezne se pro true pouziva t priklady:
> (if t 5 6)
5
> (if nil 5 6)
6
> (if 4 5 6)
5
if je napsano pomoci unless:
> (when t 3)
3
> (when nil 3)
NIL
> (unless t 3)
NIL
> (unless nil 3)
3
when a unless narozdil od if berou vic forem v body casti:
> (when t
    (setq a 5)
    (+ a 6)
  )
11
je tu ale treba i cond co funguje jako if..else if....fi konstrukce:
> (setq a 3)
3
> (cond
   ((evenp a) a)        ;if a jenom return a
   ((>  a 7) (/ a 2))    ;else if a je vetsi nez 7 return a/2
   ((<  a 5) (- a 1))    ;else if a je mensi nez 5 return a-1
   (t 17)               ;else return 17
  )
2
pokud chybi akce cond vrati hodnotu podinky:
> (cond ((+ 3 4)))
7
a mame tu i case:
> (setq x 'b)
B
> (case x
   (a 5)
   ((d e) 7)
   ((b f) 3)
   (otherwise 9)
  )
3

Smycky a spol

Zaklad vseho je loop-ta pouzti body furt dokola dokud nevlitne na return- return je tu neco jako break v cecku:
> (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
Vidite a to je _ZCELA_ univerzalni smycka jednoducha umi vsechno Pak tu mame dolist nastavuje promenou na jednotlivy prvky listu a provadi body:
> (dolist (x '(a b c)) (print x))
A
B
C
NIL
dolist vzdycky vrati nil. Takze to nil na konci neni z vypisu printem ale navratova hodnota.

Jsou tu ale i slozitejsi treba do:

> (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
Neco jako ceckarsky for: prvni cast jsou promeny a jaky hodnoty jim dat a jak je menit. Dalsi cast je podminka a navratova hodnota a nakonec je body. Opakuje se dokud je podminka false. je tu taky do* co funguje jako let*

Non local exits

To je forma return-ta ukonci provadeni body a nastavi navratovou hodnotu:
> (defun foo (x)
    (return-from foo 3)
    x
  )
FOO
> (foo 17)
3
Muze se vratit z libovolnyho bloku. Clisp ma na to konstrukci named block coz je skoro jako funkce. Toho vyuzivaji interne vsechny funkce co berou body:
> (block foo
    (return-from foo 7)
    3
  )
7
Temporary bloky jako smycky se vetsinou jmenujou nil. No a podobna forma je error:
> (error "This is an error")
Error: This is an error
Ta vsechno prerusi a vybombi.

Funcall, Apply, a Mapcar

To jsou funkce na volani funkci. Funcall uz znate. priklady:
> (funcall #'+ 3 4)
7
> (apply #'+ 3 4 '(3 4))
14
> (mapcar #'not '(t nil t nil t nil))
(NIL T NIL T NIL T)

Apply je jako funcall ale posledni parametr muze byt list ktery se potom prida na konec parametru volane funkce tedy priklad zavola:
(+ 3 4 3 4)
Mapcar zavola funkci co bere jako fvuj prvni parametr postupne na vsechny prvky listu co bere jako svuj druhy parametr.

Lambda

clisp umi docasne funkce:
> #'(lambda (x) (+ x 3))
(LAMBDA (X) (+ X 3))
> (funcall * 5)
8
Daji se tim delat smycky:
> (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)

Trideni

jen priklad:
> (sort '(2 1 5 4 6) #'<)
(1 2 4 5 6)
> (sort '(2 1 5 4 6) #'>)
(6 5 4 2 1)

Shodnost

V clispu je nekolik schodnosti:
ciselna funkci =
eq na symboly-dva symboly jsou eq kdyz jsou identicky takze dve kopije listu nejsou eq
eql je jako eq pro symboly a - pro cisla
a equal je jako eql ale pro consy opravdu porovnava tedy dve kopije listu jsou equal:
> (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

Prace z listy

Takovych funkci je moc tedy jen priklady:
> (append '(1 2 3) '(4 5 6))    ;concatenate lists
(1 2 3 4 5 6)
> (reverse '(1 2 3))            ;reverse the elements of a list
(3 2 1)
> (member 'a '(b d a c))        ;set membership -- returns the first tail
(A C)                           ;whose car is the desired element
> (find 'a '(b d a c))          ;another way to do set membership
A
> (find '(a b) '((a d) (a d e) (a b d e) ()) :test #'subsetp)
(A B D E)                       ;find is more flexible though
> (subsetp '(a b) '(a d e))     ;set containment
NIL
> (intersection '(a b c) '(b))  ;set intersection
(B)
> (union '(a) '(b))             ;set union
(A B)
> (set-difference '(a b) '(a))  ;set difference
(B)
No a ted jenom spustit emacs nastavit interpretr:
ln -s /usr/local/bin/clisp ~/bin/lisp
napsat M-x lisp-mode
a programovat!

M-x run-lisp spusti lisp.


Tento soubor je soucasti rozsahle sbirky skolicek na http://www.ucw.cz/~hubicka/skolicky

Take si muzete prohlidnout jeji puvodni textovou podobu

Nebo mi mailnout na hubicka@ucw.cz

Copyright (C) Jan Hubicka 1995