Common Lisp (4) - argumenty funkcie, čísla, zoznamy

28.04.2008 00:00 | Články | Adam Sloboda
Minule sme sa konečne dopracovali k nejakému kódu, teraz je čas na konkrétnejšie vysvetlenie základných súčastí jazyka.

Definícia funkcie

(defun name (parameter*)
  [ documentation-string ]
  body-form*)

Okrem obyčajného zoznamu argumentov je možné použiť nepovinné pomenované "keyword" argumenty:

CL-USER> (defun fn (&key one (two 2) (three 3 three-p))
       (list one two three-p three))
FN
CL-USER> (fn :one 0 :two 1 :three 2)
(0 1 T 2)

Z výstupu je možné vyčítať, že sme nastavili všetkým nové hodnoty, pri poslednom môžeme priamo vo funkcii zistiť či bol zadaný (three-p má hodnotu T, pravdivý výraz). Ďalej máme k dispozícii nepovinné argumenty:

CL-USER> (defun fn (&optional one (two 2))
       (list one two))
FN
CL-USER> (fn 0 1)
(0 1)
CL-USER> (fn 0)
(0 2)
CL-USER> (fn)
(NIL 2)

A v neposlednom rade neurčitý počet argumentov:

CL-USER> (defun fn (one two &rest rest)
       (list one two rest))
FN
CL-USER> (fn 1 2 3)
(1 2 (3))
CL-USER> (fn 1 2 3 4)
(1 2 (3 4))

Za zoznamom argumentov ešte môže nasledovať nepovinný dokumentačný reťazec:

(defun hello ()
  "Tento retazec sa stava sucastou funkcie."
  (format t "Hello world!~%"))
CL-USER> (documentation 'hello 'function)
"Tento retazec sa stava sucastou funkcie."

Už minule sme použili funkciu format, kde sme tiež vypisovali na štandardný výstup (aby sa reťazec nevypisoval ale použil ako návratová hodnota, prvý argument musí byť nil). Táto funkcia dokáže formátovať rôznymi spôsobmi, jej formátovací reťazec je taký malý špecializovaný jazyk a rozsah dokumentácie je väčší než celý tento diel.

~~znak tilda, ~n~ je n tíld
~%nový riadok, ~n% je n nových riadkov
~F / ~E / ~Gdesatinné číslo
~nRvypíše číslo so základom n (D/B/O/X miesto nR pre desiatkovú/binárnu/osmičkovú/šestnástkovú sústavu)
~Cďalší argument v poradí je znak
~Apekný ("aesthetic") výpis akéhokoľvek argumentu
~{ a ~}iterácia nad zoznamom
CL-USER> (format nil "~{~B ~}~%" '(1 2 3 4))
"1 10 11 100 
"
CL-USER> (format t "~{~A:~7T~3B~%~}" '(:one 1 :two 2 :three 3 :four 4))
ONE:     1
TWO:    10
THREE:  11
FOUR:  100
NIL
CL-USER> (format nil "~R" 1234)
"one thousand two hundred thirty-four"
CL-USER> (format nil "~@R" 1234)
"MCCXXXIV"

Toto je len zlomok celkových možností formátovania, aj keď tie najexotickejšie sme už vyčerpali.

Zoznam

Zoznam je zatiaľ všade okolo nás, je to totiž veľmi šikovný dátový typ, vhodný na použitie ako zásobník (funkcie push, pop), pole a pod. (Pozor, Common Lisp má aj polia, hashe a iné typy, zoznam nie je zďaleka jediný.)

Základné funkcie na prácu so zoznamom: first/car, second/cadr, third/caddr, …, rest/cdr, last, nth, intersection (prienik), union (zjednotenie), append a iné.

Zoznam sa vnútorne skladá z nadväzujúcich dvojíc ("dotted pair"), ktoré dokáže skladať funkcia cons:

CL-USER> (cons 1 nil)
(1)
CL-USER> (cons nil 1)
(NIL . 1)
CL-USER> (cons 1 '(2 3))
(1 2 3)
CL-USER> (last (cons 1 (cons 2 (cons 3 4))))
(3 . 4)
CL-USER> (cons 1 (cons 2 (cons 3 (cons 4 nil))))
(1 2 3 4)
CL-USER> (last (cons 1 (cons 2 (cons 3 (cons 4 nil)))))
(4)
CL-USER> (nth 0 (cons 1 (cons 2 (cons 3 (cons 4 nil)))))
1
CL-USER> (nth 3 (cons 1 (cons 2 (cons 3 (cons 4 nil)))))
4

Zoznam tvoria zreťazené dvojice, prvá je hodnota a druhá odkaz na nasledujúcu dvojicu (a prvok poslednej dvojice musí byť nil). Toto zreťazenie je vlastne spájaný zoznam.

Špeciálnym zoznamom je tzv. "property list", ktorý obsahuje pomenované prvky:

CL-USER> (defvar prop '(1 2 :three 3 4))
PROP
CL-USER> (getf prop :three)
3
CL-USER> (setf (getf prop :three) 4)
4
CL-USER> prop
(1 2 :THREE 4 4)

Čísla

Už sme videli celé čísla aj desatinné čísla, oboje v matematike patria pod reálne. Taktiež zlomky:

CL-USER> 1/3
1/3
CL-USER> (/ 1 3)
1/3

Toto spolu s neobmedzenou veľkosťou dáva výbornú presnosť (na druhú stranu to stojí procesorový čas, preto používame aj čísla typu float). Common Lisp poskytuje aj komplexné čísla:

CL-USER> #C(2 2)
#C(2 2)
CL-USER> (+ #C(2 2) #C(2 2))
#C(4 4)
CL-USER> (* #C(2 2) #C(2 2))
#C(0 8)
CL-USER> (abs #C(2 2))
2.828427
CL-USER> (sqrt -1)
#C(0.0 1.0)

Ďalšie samozrejmé funkcie sú bitové operácie a narábanie s bitovou reprezentáciou: bit, ash, bit-not, bit-and, bit-xor, byte, ldb, dpb, …

Na záver si predstavíme rôzne operátory rovnosti (či skôr predikáty), ktoré máme k dispozícii:

=porovnáva len čísla
eqporovnáva objekty, zisťuje či sa jedná o totožný objekt (je pravdivý napr. po priradení pomocou setq)
eqlporovnáva typ a hodnotu
equalzistí či objekty "vyzerá" rovnako (pre čísla, znaky a reťazce sa správa ako eql) a nedokáže porovnať polia (okrem reťazcov)
equalpignoruje typy čísel ako =, reťazce a znaky sú porovnávané case-insensitive

Pozornejší si už mohli všimnúť -P konvenciu (dostala sa aj do Jargon file). Je to pridávanie písmena 'p' na koniec názvu predikátu (s pomlčkou alebo bez, dávame najmä ak predchádza viac slov – v Lispe sa slová zvyknú oddeľovať pomlčkami). Common Lisp obsahuje veľa predikátov a funkcií, kde sa dajú využiť ako argument (v prípade komplexnejších porovnaní je možné na mieste vytvoriť anonymnú funkciu).

Nabudúce sa pozrieme zase na nejaké teoretické témy, ale znovu si niečo naprogramujeme.

  • Zoznam ako pole 28.04.2008 | 12:31
    Nathan   Návštevník
    Len malá technická, zoznam je určite vhodný na reprezentáciu zásobníka, fronty a niektorých ďalších štruktúr, ale na pole je rozhodne lepšie použiť samotný typ pole.
    • Re: Zoznam ako pole 28.04.2008 | 12:44
      autor   Návštevník
      ano, ale je mozne pouzit aj zoznam

      dodam, ze jednorozmerne pole ma v CL aj samostatny typ vector (okrem array). okrem toho ma aj ine datove typy, ktore su optimalizovane.