Siirry sisältöön

Common Lisp/Def

Wikikirjastosta

Common Lispissä on noin seitsemäntoista def -alkuista komentoa. Niistä ei ole välttämätöntä tietää kaikkea voidakseen ohjelmoida. Alkuun riittää osata käyttää komentoja DEFUN ja DEFMACRO. Saat toki enemmän aikaan ja ymmärrät paremmin Lisp -ohjelmia, jos ymmärrät useampia komentoja.

DEFUNilla voidaan luoda uusia funktioita. Funktio koostuu parametri-esittelystä, kommentista ja sarjasta käskyjä. Tässä on esimerkki:

(defun eka-funktio (x y z)
  "kommentti, jos haluat sellaisen kirjoittaa"
  (+ (* x y z) x y z))

kokeile näitä:
(eka-funktio 1 2 3)
(documentation 'eka-funktio 'function)

Kommentin voi lukea documentation -komennolla. Funktioon liitettyä kommenttia kutsutaan nimellä 'docstring'. Jos voit korvata tavallisen kommentin docstringillä, tee se! Hyviin tapoihin kuuluu, että selittää melko tarkasti ja ytimekkäästi funktioista docstringiin, mutta se ei korvaa dokumentaatiota pelkällä docstringillä.

Tähän tarvittaisiin asiaa &optional ja &rest -parametreistä.

Koska Lisp on pitkälti funktionaalinen kieli, funktioilla siinä voi tehdä monenlaisia temppuja.

DEFMACRO on yksi Lispin kiinnostavista ominaisuuksista. Sillä voi muuttaa Lispin perinteistä tapaa käsitellä listoja ja luoda täysin uusia rakenteita. Makroilla Lispiin voi lisätä esimerkiksi uusia kontrollirakenteita tai def- muotoja, vaikkapa oman tilanteeseen sopivan define-luokka rakenteen joka luo uusia luokkia ja niihin metodeja ohjelmallisesti. Historiallisesti makroja on käytetty myös koodin optimointiin mutta nykyään sitä pidetään niiden väärinkäyttönä ja tähän tarkoitukseen on parempiakin tapoja (esim. inline-funktiot).

Hyvien makrojen kirjoittaminen on monimutkaista ja siihen täytyy harjaantua. Makrot ovat hyvin tehokas väline ja monissa tapauksissa ainoa mielekäs tapa saavuttaa haluttu toiminallisuus. Seuraava nyrkkisääntö on silti hyvä muistaa: jos asian voi tehdä normaalilla funktiolla, makroja ei pidä käyttää.

Makro on siis funktio joka suoritetaan silloin kun ohjelma käännetään. Jotta ymmärtäisi makroja paremmin, vilkaisepa seuraavaa esimerkkiä:

(defmacro with-basso (name &rest body)
  `(progn (open-basso ,name) ,(cons 'progn body) (close-basso ,name)))

(macroexpand '(with-basso foo (bar) (baaz)))

Toisena esimerkkinä käytämme when-makroa. Kun kääntäjä näkee when-makron, se ottaa sen parametrit ja antaa ne listana makrolle, joka rakentaa listasta oikean listakomennon. Kääntäjä asettaa saadun listakomennon when-makron paikalle.

Eli siis listasta:
(when T (print "ruisraikka") (print "apokkalypsy"))
Tuleekin lista:
(if T (progn (print "ruisraikka") (print "apokkalypsy"))
joka suoritetaan makron sijasta!

On tärkeää ymmärtää että lispin makrot eivät perustu pelkkään mallin mukaan korvaamiseen vaan ne voivat käyttää aivan normaalia lispiä tuotetun koodin rakentamiseen. Pohjimmiltaan makrot ovat siis käännön aikana ajettuja funktioita jotka tuottavat syötettyjen listojen (koodia) perusteella toisia listoja (koodia). Em. esimerkissä käytetty backquote-notaatio on itseasiassa vain syntaktista sokeria normaaleilla listanmuodostusfunktioille (list, append jne.). Backquoten käyttö tämänkaltaisissa makroissa auttaa selkiyttämään koodia ja tuo makron idean paremmin esille. (Ja kyllä, pienen harjoittelun jälkeen edellisen kaltaiset makrot ovat täysin selkeitä.)