destructuring bind
destruturing-bind
je blok, v ktorom sa priradí
premenným v prvom argumente hodnota z druhého argumentu (presne ako
názov naznačuje, rozloží zoznam na samostatné prvky). Tretím
argumentom je telo, v ktorom môžeme narábať s týmito premennými.
CL-USER> (destructuring-bind (a b c s) `(1 2 3 ,(+ 1 2 3)) `(,a + ,b + ,c = ,s)) (1 + 2 + 3 = 6)
values a multiple value bind
Ďalšou špecialitou je možnosť viacerých návratových hodnôt (pokiaľ túto možnosť chceme ignorovať, pracuje sa len s prvou vrátenou hodnotou):
CL-USER> (floor pi) 3 0.14159265358979312d0 CL-USER> (- 3 (floor pi)) 0 CL-USER> (multiple-value-bind (a b) (floor (sqrt 2)) (format nil "~A ~A" a b)) "1 0.41421354" CL-USER> (multiple-value-bind (x y z) (values 1 2 3) (format nil "~A ~A ~A" x y z)) "1 2 3"
Closure
CL-USER> (defun adder (n) #'(lambda (x) (+ x n))) ADDER CL-USER> (adder 5) #<CLOSURE (LAMBDA (X)) {10032C1049}> CL-USER> (funcall (adder 5) 1) 6 CL-USER> (setf (symbol-function 'add5) (adder 5)) #<CLOSURE (LAMBDA (X)) {10035CBEC9}> CL-USER> (add5 1) 6
V ukážke funkcia vytvorí lexikálny uzáver (closure),
ktoré aj mimo svojho kontextu môže pristupovať k hodnote
n
.
Techniku currying je možné
pomocou closure
implementovať.
CL-USER> (defun curry (function &rest args) (lambda (&rest more-args) (apply function (append args more-args)))) CURRY CL-USER> (curry #'+ 5) #<CLOSURE (LAMBDA (&REST MORE-ARGS)) {1002853ED9}> CL-USER> (funcall (curry #'+ 5) 1) 6 CL-USER> (setf (symbol-function 'expt10) (curry #'expt 10)) #<CLOSURE (LAMBDA (&REST MORE-ARGS)) {1002994E79}> CL-USER> (expt10 2) 100 CL-USER> (setf (symbol-function 'list-1-2) (curry #'list 1 2)) #<CLOSURE (LAMBDA (&REST MORE-ARGS)) {10034484E9}> CL-USER> (list-1-2) (1 2) CL-USER> (list-1-2 3 4) (1 2 3 4)
V ukážkach sme vytvorili volanie roznych funkcií s neúplným zoznamom argumentov (v prípade vytvorenia zoznamu je úplný, no môžeme ho rozšíriť aj o ďalšie hodnoty).
Optimalizovať vykonávanie funkcie curry
je možné týmto
nastavením:
(declaim (ftype (function (function &rest t) function) curry) (inline curry))
ASDF
asdf
(Another System Definition Facility) je knižnica,
ktorá sa používa na automatizáciu kompilácie a načítania tzv. systémov
(programy alebo knižnice pre Common Lisp, majú vlastný namespace)
podľa definovaných závislostí.
Jeho použitie si ukážeme na jednoduchom príklade – zobrazíme
body pravidelného n-uholníka (budeme potrebovať balíček
cl-sdl
). Vytvoríme si zdrojový a popisný súbor:
polygon.lisp
(okrem štandardnej Common Lisp knižnice
používame knižnicu cl-sdl
, exportujeme funkciu
start
)
(defpackage #:polygon (:use #:cl #:cl-sdl) (:export #:start)) (in-package #:polygon) ;; tu začína kód
polygon.asd
:
(asdf:defsystem #:polygon :depends-on (#:sdl) :components ((:file "polygon")))
Načítame náš projekt cez SLIME REPL, príkaz začneme zadávať
čiarkou, zadáme load-system
a následne názov nášho
projektu (možno bude nutné predtým zmeniť aktuálny adresár príkazom
cd
). Príkazy a ďalšie informácie je aj tu možné nechať
automaticky dopĺňať tabulátorom. V tomto bode môžeme začať písať
kód, globálne premenné (zvyknú sa pomenovávať s hviezdičkami):
;; globálne premenné ovplyvňujúce inicializáciu SDL a obraz (defvar *zoom* 20) (defvar *width* 200) (defvar *height* 200) (defvar *bpp* 24) (defvar *flags* (logior sdl:+swsurface+)) (defun init-sdl () (sdl:init (logior sdl:+init-video+)) (let ((surface (sdl:set-video-mode *width* *height* *bpp* *flags*))) (when (sgum:null-pointer-p surface) (error "Unable to set video mode")) (sdl:wm-set-caption "CL-SDL" nil) surface))
Hlavná programová slučka, kde prijímame udalosti z knižnice SDL (pri stlačení krížiku na okne alebo pri aktivite klávesy 'q' slučku ukončíme):
(defun run-sdl-event-loop (surface points) (plot surface points) (sdl:event-loop (:key-down (scan-code key mod unicode) (cond ((= key (char-code #\q)) (return)))) (:key-up (scan-code key mod unicode) (cond ((= key (char-code #\q)) (return)))) (:quit () (return)) (:idle () (sleep 0.05))))
Ďalšia je funkcia na výpočet bodov na jednotkovej kružnici a funkcia na ich vykreslenie:
(defun points (n) (mapcar #'(lambda (x) (list (* 2 (cos x)) (* 2 (sin x)))) ;; uhly na ktorych lezia body (loop :for i :from 0 :below n :collect (/ (* 2 pi i) n)))) (defun plot (surface points) (dolist (p points) (let ((x (floor (+ (/ *width* 2) (* *zoom* (+ (first p) 1/2))))) (y (floor (+ (/ *height* 2) (* *zoom* (+ (second p) 1/2)))))) (cl-sdl:draw-pixel surface x y 255 0 0 :update-p nil :clipping-p nil))) (cl-sdl:update-screen surface))
Obe obsahujú len veľmi jednoduché výpočty bodov a pozíciu pixelov. Teraz si ich všetky pospájame dokopy:
(defun start (n) (unwind-protect (progn (let ((surface (init-sdl))) (run-sdl-event-loop surface (points n)))) (sdl:quit)))
unwind-protect
zabezpečí aby sa vždy zavolala funkcia
quit
v knižnici SDL. Teraz stačí skompilovať pomocou
C-c C-k
a môžeme to vyskúšať. Pokiaľ spúšťame program z
REPL, používame celý názov polygon:start
.
Na precvičenie môže byť vhodné doplniť interaktívny zoom alebo
pospájanie bodov úsečkami pomocou funkcie
cl-sdl:draw-line
:
(defun draw-line (surface x1 y1 x2 y2 r g b &key (check-lock-p t) (update-p t) (clipping-p t)))
Toto bolo len také zahrievacie kolo na oboznámenie so SDL, nabudúce si skúsime niečo trošku zložitejšie – nakreslíme si Mandelbrotovu množinu.
Pre pridávanie komentárov sa musíte prihlásiť.