Common Lisp

A Wikipédiából, a szabad enciklopédiából
Common Lisp
Paradigma Multi-paradigma: procedurális, funkcionális, objektumorientált, meta, reflektív, generikus
Jellemző kiterjesztés .lisp
Megjelent 1984, 1994 for ANSI Common Lisp
Fejlesztő ANSI X3J13 bizottság
Típusosság dinamikus, erős
Dialektusok CLtL1, CLtL2, ANSI Common Lisp
Megvalósítások Allegro CL, ABCL, CLISP, Clozure CL, CMUCL, Corman Common Lisp, ECL, GCL, LispWorks, Movitz, Scieneer CL, SBCL, Symbolics Common Lisp
Hatással volt rá Lisp, Lisp Machine Lisp, MacLisp, Scheme, InterLisp
Befolyásolt nyelvek Clojure, Dylan, Emacs Lisp, EuLisp, ISLISP, R, SKILL, SubL
Operációs rendszer multi-platform
Weboldal

A Common Lisp a Lisp programozási nyelv egyik dialektusa, amit az "ANSI INCITS 226-1994 (R2004), (korábban X3.226-1994 (R1999))" szabványban publikáltak.[1] Ebből az ANSI Common Lispből származik a Common Lisp HyperSpec webes használatra.[2] A Lisp szabványosításának érdekében alkották meg, hogy összefogja a különböző, főként a MacLisp variánsokat, ezért sokkal inkább specifikáció, mint megvalósítás. Léteznek szabad és kereskedelmi implementációi is.

A Common Lisp általános célú multiparadigmás nyelv, ami támogatja a funkcionális, a procedurális és az objektumorientált paradigmákat. Dinamikus volta és az iteratív fordítás lehetősége segíti az inkrementális és evolúciós fejlesztést.

Támogatja az opcionális típusannotációkat és a konverziókat, amelyek szükség szerint hozzávehetők a profilozó és az optimalizációs fázisokban. Például a fixnum tartalmazhat egy egész számot a hardver és az implementáció által támogatott tartományban, amitől hatékonyabbá válnak a nagy számokon vagy a tetszőleges pontosságú számokon végzett műveletek. Hasonlóan, a fordítóval optimize deklarációkkal modulonként és függvényenként közölhető az alkalmazott biztonsági szint.

A Common Lisp tartalmazza a CLOSt, egy objektumrendszert, ami támogatja a multimetódusokat és a metódusok kombinálását. A nyelv bővítésére erős eszközt adnak a makrók, amelyekkel a program fordítási időben átírhatja magát, és a C makrókhoz hasonlóan működő olvasó makrók, amelyek speciális jelentést adnak egyes karaktersorozatoknak.

Habár a Common Lisp nem annyira népszerű, mint egyes nem Lisp nyelvek, több eszközét átvették más, népszerűbb nyelvek.

Szintaxis[szerkesztés | forrásszöveg szerkesztése]

A Common Lisp egy Lisp nyelvjárás; S-kifejezéseket (azaz beágyazott listákat) használ mind az adatok, mind a rajtuk végzett műveletek leírására. A függvény- és a makróhívásokat listába kell tenni, ahogy a következő példák mutatják:

 (+ 2 2)           ; összeadja a 2-t és a 2-t, az eredmény 4.
 (defvar *x*)      ; Kimondja, hogy *x* létezik,
                   ; értékadás nélkül. A csillagok is a névhez tartoznak.
                   ; Innen kezdve az *x* következő kötései dinamikusak,
                   ; és nem lexikálisak.
 (setf *x* 42.1)   ; az *x* értéke a 42.1 lebegőpontos szám
 ;; Egy négyzetre emelő függvény definíciója:
 (defun square (x)
   (* x x))
 ;; a függvény végrehajtása:
 (square 3)        ;  9
 ;; a 'let' szerkezet hatókört vezet be a lokális változók számára. 
 ;; Itt az 'a' változóhoz a 6, és a 'b' változóhoz a 4 értéket kapcsoljuk.
 ;; A 'let' belseje a 'let' törzse, ami az utoljára kiszámított értéket adja vissza.
 ;; Itt az eredmény a és b összege, ezt adja vissza a 'let' kifejezés.
 ;; Az a és b változók hatóköre lexikális, kivéve ha
 ;; speciális változónak deklaráltuk őket (például egy korábbi DEFVARral).
 (let ((a 6)
       (b 4))
   (+ a b))        ; returns 10

Adattípusok[szerkesztés | forrásszöveg szerkesztése]

A Common Lispben sok adattípus van; több, mint más nyelvekben.

Skalár típusok[szerkesztés | forrásszöveg szerkesztése]

A számtípusok lehetnek egészek, lebegőpontos, tört, és komplex számok.[3] A bignum tetszőleges nagyságú vagy pontosságú számot tárolhatnak. A törtek számlálóval és nevezővel vannak reprezentálva, ami sok más nyelvben nem létezik. A Common Lisp ezeket a számtípusokat automatikusan konvertálja.

A character nem korlátozódik az ASCII-re. A legtöbb implementáció Unicode-ot használ.[4]

A Lisp nyelvjárások ismerik a szimbólum típust, de más nyelvek alig. A szimbólum egy egyedi, elnevezett adatobjektum több résszel: tartalmaz nevet, értéket, függvényt, tulajdonságlistát és csomagot. Ezek közül az érték- és a függvénycellák a legfontosabbak. A Lispben a szimbólumokat általában úgy használják, mint a többi nyelvben az azonosítókat, de vannak más felhasználási módok is. A szimbólum kiértékelésekor a benne foglalt adatot kapjuk vissza. Egyes szimbólumok önmagukat értékelik ki, például a keywords csomag tagjai. A logikai értékeket is önkiértékelő szimbólumok reprezentálják, a t és a nil. A Common Lispben a szimbólumok névterekhez, csomagokhoz tartoznak.

A számértékek kerekítésére számos függvény áll rendelkezésre. A round a legközelebbi egészre kerekít. A szokásos kerekítéstől eltérően azonban a feles értékek helyett a két lehetőség közül a páros egészet veszi. A floor az alsó, a ceiling a felső egészrészt jelenti; a truncate csonkol. A törtrész a második visszatérési érték. Például (floor -2.5) értéke -3, 0.5; (ceiling -2.5) értéke -2, -0.5; (round 2.5) értéke 2, 0.5; és (round 3.5) értéke 4, -0.5.

Adatszerkezetek[szerkesztés | forrásszöveg szerkesztése]

A listaszerű adatszerkezetek közé tartoznak a listák, a vektorok, a bitvektorok és a stringek. Mindegyikhez sok művelet tartozik.

Akárcsak a legtöbb Lisp nyelvjárásban, a listák Common Lispben is cons cellákból épülnek fel. A cons egy két slotú adatszerkezet, a lista fejét reprezentáló car, és a lista farkának megfelelő cdr. A listák cons cellák láncolatai. A car a lista egy elemére mutat, a cdr egy másik cons cellára hivatkozik, kivéve az utolsót, aminek értéke nil. A cons cellákból más adatszerkezetek, fák és körkörös hivatkozásokat tartalmazó szerkezetek építhetők. Ehelyett azonban ajánlott osztályokat és struktúrákat használni.

A Common Lisp támogatja a többdimenziós tömböket, amelyek dinamikusan átméretezhetők, és amelyekkel mátrixszámítások is végezhetők. Az egydimenziós tömbök vektorok. Egy tömb tartalmazhat különböző típusú elemeket, de specializálhatók egy típus tárolására, például egészek vektora. Egyes implementációk ennek ismeretében optimalizálják a feldolgozó algoritmusaikat. A szabvány tartalmaz két típusspecializált vektort: a stringet és a bitvektort.

A hash táblák kulcs-érték párokat tartalmaznak, amelyek bármilyen típusúak lehetnek.

A csomagok szimbólumokat tartalmaznak, és főként névterek kialakítására szolgálnak. Az export interfész elemeiként feltünteti a kívülről is látható szimbólumokat. A Struktúrának a nyelv rekord típusait nevezik. A C structjaira és a Pascal recordjaira hasonlítanak. Összetett adattípusok, amelyek akárhány mezőt tartalmazhatnak, ahol az érték típusa az összes típus közül választható. A mezőket slotoknak nevezik. Egyszeres öröklődést tesznek lehetővé.

Az osztályok a struktúrákra hasonlítanak, de több dinamikus lehetőséget és többszörös öröklődést biztosítanak. Az osztály objektumai példányok. A generikus függvények speciális esetek: egyszerre függvények és példányok. csomagok más csomagokat is tartalmazhatnak.

Függvények[szerkesztés | forrásszöveg szerkesztése]

A Common Lisp támogatja a funkcionális paradigmát, azaz a függvények más objektumokhoz hasonlóan kezelhetők. Léteznek magasabb rendű függvények, amelyeknek argumentumai vagy visszatérési értékei függvények. Lehet névtelen és beágyazott függvényeket definiálni, amelyek külső változókhoz is hozzáférnek; lezártakat átadni, sőt, változóba függvényt helyettesíteni, és függvények egyenlőségét vizsgálni.

A magasabb rendű függvények a nyelv könyvtáraiban is fontosak, például a sort argumentuma egy összeghasonlító operátor és egy kulcsfüggvény, mint opcionális kulcsszó argumentum. Nemcsak skalár adatok rendezhetők, hanem összetett adatstruktúrák is egy adott kulcs szerint:

 ;; Listák rendezése a > és a < függvényekkel, mint összehasonlító operátorokkal:
 (sort (list 5 2 6 3 1 4) #'>)   ; Returns (6 5 4 3 2 1)
 (sort (list 5 2 6 3 1 4) #'<)   ; Returns (1 2 3 4 5 6)
 ;; Lista rendezése az allisták első eleme szerint :
 (sort (list '(9 A) '(3 B) '(4 C)) #'< :key #'first)   ; Returns ((3 B) (4 C) (9 A))

A függvények kiértékelésének modellje egyszerű. Ha a kiértékelő lát egy (F A1 A2...)sorozatot, akkor feltételezi, hogy F a következők egyike:

  1. Speciális operátor (rögzített lista)
  2. Makró (előzőleg definiált)
  3. Függvénynév, ami lehet szimbólum, vagy lehet lambda.

Hogyha F függvénynév, akkor előbb az A1, A2, ..., An argumentumok értékelődnek ki balról jobbra, majd az F függvény meghívódik ezekkel a paraméterekkel. Funkcionális szempontból tekintve a nyelv mohó.

Függvények definiálása[szerkesztés | forrásszöveg szerkesztése]

Függvények a defun makróval definiálhatók. A definíció megadja a függvény és paraméterei nevét, meg a függvénytörzset:

 (defun square (x)
   (* x x))

A definíciók tartalmazhatnak deklarációkat is, amelyek informálják a fordítót az optimalizációról és tartalmazzák a paraméterek típusát. Az itt megadott dokumentáció interaktívan előhívható:

 (defun square (x)
   "Kiszámítja a single-float x négyzetét."
   (declare (single-float x) (optimize (speed 3) (debug 0) (safety 1)))
   (the single-float (* x x)))

Névtelen függvények lambda-kifejezésekkel adhatók meg, például (lambda (x) (* x x)). A Lisp programozási stílusára jellemző magasabb rendű függvényeknek sokszor érdemes névtelen függvényeket paraméterként átadni.

Lokális függvények definiálhatók a flet és a labels szavakkal:

 (flet ((square (x)
          (* x x)))
   (square 3))

A függvények kezelését számos más operátor segíti, például a compile operátor, aminek hatására a függvény újrafordul.

Generikus függvények és metódusok[szerkesztés | forrásszöveg szerkesztése]

A defgeneric generikus függvényeket, a defmethod metódusokat definiál. A generikus függvények metódusok gyűjteményei. A metódusok osztályokra vagy objektumokra specializálhatók. A generikus függvény hívásakor választódik ki a típusnak megfelelő függvény.

 (defgeneric add (a b))
 (defmethod add ((a number) (b number))
    (+ a b))
 (defmethod add ((a vector) (b number))
    (map 'vector (lambda (n) (+ n b)) a))
 (defmethod add ((a vector) (b vector))
    (map 'vector #'+ a b))
 (add 2 3)                   ;  5
 (add #(1 2 3 4) 7)          ;  #(8 9 10 11)
 (add #(1 2 3 4) #(4 3 2 1)) ;  #(5 5 5 5)

A generikus függvények is elsőfajú adattípusok. Még sok eszköz létezik a generikus függvények kezelésére.

A függvények névtere[szerkesztés | forrásszöveg szerkesztése]

A függvények névtere különbözik a többi adatétól; ez kulcsfontosságú különbség a Common Lisp és a Scheme között. A Common Lispben a defun, flet, labels, defmethod és a defgeneric operátorok hoznak létre függvény névtérbeli adatokat.

A függvények név szerinti paraméterátadásakor a function speciális operátort kell használni, ami rövidíthető a #' jellel. Erre példa a fent már szerepelt függvényátadás a sort operátornak. Az így átadott függvény a funcall operátorral hívható.

A Scheme kiértékelési módszere egyszerűbb, mivel csak egy névtér van, és minden kiértékelődik, nemcsak az argumentumok. Így az egyik nyelvjárásban írt kód nehezen értelmezhető a másik nyelvjárásban gyakorlott programozók számára. Például a Common Lispben szokás leíró neveket adni a változóknak, mint list vagy string. Ez a gyakorlat a Scheme-ben fontos függvényneveket takarhat el.

A Lisp programozók nem értenek egyet abban, hogy jó ötlet-e a két névtér. Erre Lisp-1 vs. Lisp-2 vitaként utalnak, ahol a számok a névterek számát jelölik. Ezek a nevek Richard P. Gabriel és Kent Pitman egy 1988-as cikkéből származnak, ahol alaposan kifejtik a témát.[5]

Más adattípusok[szerkesztés | forrásszöveg szerkesztése]

A fontosabb többi adattípus:

  • A fájlokat és könyvtárakat reprezentáló nevek. A legtöbb operációs rendszernél általánosabb, ami támogatja hordozható Lisp programok írását.
  • Az input és az output streamek szöveges vagy bináris adatok forrásait vagy céljait reprezentálják, mint a terminálok és a megnyitott fájlok.
  • A Common Lisp rendelkezésre bocsát egy beépített álvéletlen számgenerátort. A véletlen állapotobjektumok az álvéletlen számok újrahasznosítható forrásai, ami lehetővé teszi magok megadását, vagy adott álvéletlen számsorozatok visszajátszását.
  • Az állapotok hibákat, kivételeket és más fontos, reakcióra váró eseményeket reprezentálnak.
  • Az osztályok első osztályú objektumok, és a metaobjektum osztályok (röviden metaosztályok) példányai.
  • Az olvasótáblák ellenőrzik a Common Lisp fordítását. A nyelv szintaxisa megváltoztatható vagy kiterjeszthető azzal, hogy a programozó olvasótáblát változtat.

Hatókör[szerkesztés | forrásszöveg szerkesztése]

Ahogy más nyelvek, a Common Lisp is névvel hivatkozik a változókra, a függvényekre és más objektumokra. A nevesített referenciáknak hatókörük van.

A név és az általa megnevezett objektum közötti kapcsolat a kötés. A hatókör azokat a körülményeket jelenti, ahol ez a kötés fennáll.

A hatókör meghatározói[szerkesztés | forrásszöveg szerkesztése]

A következők segítenek a hatókör meghatározásában:

  • A referencia helye a kifejezésben. Ha egy összetett kifejezésben balról az első, akkor egy speciális operátorra, makróra vagy függvényre vonatkozik, különben változóról vagy valami másról van szó.
  • A kifejezés fajtája. Például a (GO X) átadja a vezérlést az X címkének, míg a (PRINT X) az X változó kiíratására szolgál. A két utasítás szerepelhet ugyanabban a környezetben, mivel a címkék egy másik névtérhez tartoznak. A kifejezések szintaktikailag leellenőrzik az ott szereplő szimbólumok jelentését. Például a (defclass x (a b) ()) osztályt definiáló kifejezésben (a b) az ősosztályok listája, ezért ezeknek osztályneveknek kell lenniük, és x nem vonatkozhat már meglevő kötésre, hanem az új, a-ból és b-ből származó osztály neve.
  • A referencia helye a program szövegében. Ha például az X változó kötése egy olyan környezetben van definiálva, amit LET, vagy más lokális konstrukció vezet be, akkor a hatóköre ez a lokális kifejezés.
  • A speciálisan deklarált változóreferenciák definíciója meghatározza, hogy lexikális, vagy dinamikus környezetben használható-e a referencia.
  • A környezet fajtája, ahol a program a referenciát visszaköveti. A környezet egy futás idejű szótár, ami tartalmazza a szimbólumok és jelentésük közötti kapcsolatot. Például a lexikális referenciákat lexikális, a dinamikusokat dinamikus környezetben lehet visszahivatkozni. Ugyanahhoz a változóhoz több referencia is tartozhat. A rekurzió és a többszálúság miatt egy függvény egy időben több példányban is meghívódhat. Ezekhez az aktivációkhoz ugyanaz a programszöveg tartozik, de mindegyiküknek saját lexikális környezete van.

A Common Lispben programozónak tudnia kell, hogy milyen referenciát fejez ki, milyen hatókörű (lexikális vagy dinamikus), és a futásidejű helyzetet is ismernie kell, hogy tudja, hogy mire vonatkozik az adott szimbólum.

A környezetek típusai[szerkesztés | forrásszöveg szerkesztése]

Globális környezet[szerkesztés | forrásszöveg szerkesztése]

Egyes környezetek globálisak a Lispben. Például, ha létrehozunk egy típust, az onnantól kezdve mindenütt elérhető. Az ilyen típusú referenciák erre a globális térre hivatkoznak.

Dinamikus környezet[szerkesztés | forrásszöveg szerkesztése]

Ebben a környezetben dinamikus kötések hozhatók létre, ami azt jelenti, hogy a kötés egy kifejezésen belül, például egy LET blokkban jön létre, és eltűnik a kifejezés kiértékelése után. A kötés látható azokban a függvényekben is, amelyek a blokkban hívódnak meg, ezért ezt a típusú hatókört határozatlannak is nevezik. A dinamikus kiterjesztésű és a határozatlan hatókörű kötések dinamikus hatókörűek.

A Common Lisp támogatja a dinamikus hatókörű változókat, amelyeket speciális változóknak is neveznek. Bizonyos más típusú kötések is szükségszerűen dinamikusak, mint a restarts és a catch tagek. A függvénykötések nem tehetők dinamikussá a FLET használatával, de függvényobjektumok LET környezetben dinamikusan köthetők változókhoz. Ezek a függvények a FUNCALL vagy az APPLY operátorokkal hívhatók.

A dinamikus környezet haszna a globális környezettel szemben az, hogy biztosítja a referenciák visszakövethetőségét. A számítástudományban a globális változókat potenciális hibaforrásnak tekintik, mivel nem zárja ki az esetleges, ellenőrizetlen kommunikációt a modulok között, ami nehezen felderíthető hibákat okozhat.

A Common Lispben, ha egy speciális változónak csak a legfelsőbb szinten vannak kötései, akkor úgy viselkedik, mint más nyelvekben a globális változók. Az új kapcsolat egyszerűen helyettesíti a régit. A globális érték ellenőrizetlen felülírása okozza a globális változók használatából eredő legtöbb hibát. Azonban egy speciális változó egy kifejezésben új, helyi kapcsolatot kaphat, amire a változó újrakötéseként utalnak.

A dinamikus hatókörű változók kötésével egy új memóriadarab foglalódik le a változó számára, és a név összekapcsolódik a hellyel. Amíg ez a kötés fennáll, addig a változóra mutató összes referencia erre a kötésre hivatkozik. Az új kötés nem írja felül, hanem csak elrejti a régit. Amint a kötést létrehozó kifejezés kiértékelődik, a lefoglalt hely felszabadul, és a régi kötés újra láthatóvá válik.

A többszálúságot támogató implementációkban a dinamikus hatókörök szálspecifikusak, ezért a speciális változók a szálspecifikus tár absztrakciói. Ha az egyik szál egy új kötést hoz létre a változóval, akkor az nem érinti a többi szálat. Az itt tárolt érték csak a létrehozó szál számára érhető el. Ha *X* speciális változó, akkor az ezt újrakötő szálakban szálspecifikus tárként érhető el, míg a többi szál számára globális marad, ami egy egységes értékre hivatkozik.

Dinamikus változók használatával a végrehajtási környezet további végrehajtási információkkal egészíthető ki, ami implicit módon adódik át egyik függvénytől a másikig, anélkül, hogy újabb paraméterként jelenne meg. Ez hasznos, ha a vezérlésnek több, nem kapcsolódó kód rétegén kell átjutnia, amit nem lehet követhetően új paraméterekkel kiegészíteni. Ez többnyire egy globális változót igényel, amit el kell menteni, és szükség esetén visszaállítani, hogy össze ne akadjon a rekurziókkal. Ez a dinamikus kötésű változókkal automatikusan megtörténik. Emellett a dinamikus kötésű változók szálspecifikusak is, így nem kell globális mutexet használni, ami szálbiztossá teszi a sémát.

A Common Lisp könyvtáraiban sok szabványos speciális változó található. Ilyenek például a szabványos I/O streamek, amelyeket speciális változók legfelsőbb szintű kötései tárolnak. A szabványos kimenetet a *standard-output* tárolja.

Egy standard kimenetre író függvény:

  (defun foo ()
    (format t "Hello, world"))

Ennek a kimenete megjelenhet egy stringben is, ha a *standard-output*ot egy stringhez kapcsoljuk:

  (with-output-to-string (*standard-output*)
    (foo))

A "Hello, world!" egy stringben jelenik meg.

Lexikális környezet[szerkesztés | forrásszöveg szerkesztése]

A lexikális környezetben létrehozott kötések hatóköre speciális, és a névtér típusától függően lehet dinamikus vagy általános. A lexikális hatókör azt jelenti, hogy a hatókör fizikailag arra a környezetre korlátozódik, ahol a hatókör létrejött. A blokkra nem szövegesen, azaz lexikálisan hivatkozó referenciák nem látják ezeket a változókat.

Példa:

  (defvar *stashed*) ;; ebbe függvényt helyettesítünk
 
  (tagbody
    (setf *stashed* (lambda () (go some-label)))
    (go end-label) ;; átugorja a (print "Hello")-t
   some-label
    (print "Hello")
   end-label)
  -> NIL

Itt a TAGBODY hatóköre lexikális, ezért a (GO X) hibás, ha a TAGBODY nem tartalmaz X címkét. Mivel a címkekőtések dinamikusak, ezért eltűnnek, mihelyt a TAGBODY terminál. Ha egy lexikális lezárt újra meghívja ezt a kódrészletet, akkor annak törzse nem adhatja át a vezérlést GO-val.

A TAGBODY végrehajtásakor először a setf értékelődik ki, ami egy függvényt tárol egy speciális *stashed* változóban. Ezután a (go end-label) a (print "Hello")-öt átugorva az end-labelre lép. Mivel ez a TAGBODY végén van, ezért annak végrehajtása befejeződik, és NIL-t ad vissza. Ha most hívjuk a *stashed* függvényt, akkor hiba keletkezik:

  (funcall *stashed*) ;; Hiba!

A helyzetre az egyik implementáció hibaállapottal válaszol, és ezt az üzenetet adja: "GO: tagbody for tag SOME-LABEL has already been left", azaz a GO: a vezérlés elhagyta a SOME-LABELt tartalmazó tagbodyt. A függvényt megpróbálta kiértékelni a (go some-label)-t, ami a tagbodyba van ágyazva, hogy megtalálja a SOME-LABEL címkét, azonban a tagbody már nem hajtható végre, így a vezérlés átadása elmarad.

A Lispben a helyi függvénykötések lexikális természetűek, és alapértelmezetten a változók is azok. A GO címkével szemben élettartamuk tartós. A kötés mindaddig létezik, ameddig hivatkozható; méág azután is, hogy az őket létrehozó kifejezés kiértékelődött. Ezek a hivatkozásopk a lexikális lezártaknak köszönhetően létezhetnek.

A Lisp alapértelmezett kötéstípusa lexikális, de ez megváltoztatható akár lokális, akár globális deklarációval. A DEFVAR vagy a DEFPARAMETER ezt implicit megteszi. Hogy ez ne fordulhasson elő, egy elterjedt konvenció szerint a dinamikus változók nevét csillaggal kezdik és fejezik be. Ez egy külön névteret hoz létre a dinamikus változók számára.

A lexikális környezet több okból is hasznos:

Először: hatékonyan fordítható, mivel a futás idejű lexikális környezet szerkezete egyszerű; sokszor vermen tárolják, így a megnyitás és bezárás gyorsan adminisztrálható. Még a teljes lezártak környezetéhez való hozzáférés is hatékony, mivel a változók kötései vektorban tárolhatók, így a hivatkozás is egyszerűbb.

Másodszor: a lexikális környezetnek köszönhetően léteznek a lexikális lezártak, amelyek lehetővé teszik a függvények első osztályú objektumként való kezelését, amire a funkcionális paradigma épül.

Végül, de nem utolsósorban, a lexikális környezetek elkülönítik egymástól a program különböző részeit, ami megvédi az egyes modulokat a nemkívánatos interakcióktól. Korlátozott láthatóságuk miatt ezek a változók privátak. Ha az A modul köti az X változót, és a B modul az X változóra hivatkozik, akkor nem fogja az A modulbeli kötést látni. B egyszerűen nem fér hozzá az A modulbeli X-hez. A kommunikáció céljaira a Common Lisp a speciális változókat nyújtja. Ha X speciális változó, akkor a B modul láthatja. Ez a szabályozási lehetőség egy nagy előny.

Makrók[szerkesztés | forrásszöveg szerkesztése]

A makrók a Lispben felszínesen a függvényekre emlékeztetnek. Azonban egy szerkezet kiértékelése helyett a kódot alakítják át. Argumentumként az őt tartalmazó programot köti a paraméterekhez, és kiszámítja az új forrásszöveget. Ebben az új szövegben újra lehetnek makrók. A makrók kiértékelése addig folyik, amíg a programszövegben vannak makrók. A végső, makrókat nem tartalmazó kód hajtódik végre futásidőben.

A Lisp makrók tipikus használati köre:

  • új vezérlési szerkezetek bevezetése
  • környezetek és kötések definiálása
  • az összetett és ismétlődő kódszakaszok lerövidítése
  • adatvezérelt programozás
  • beágyazott specifikus nyelvek (SQL, HTML, Prolog)

Sok szabványos Common Lisp eszköz makróként van implementálva:

  • a SETF absztrakció az értékadó és hozzáférési operátorok fordításidejű szabályozására
  • a WITH-ACCESSORS, WITH-SLOTS, és a WITH-OPEN-FILE és a többi WITH makró
  • implementációtól függően az IF vagy a COND makróként van definiálva a másik használatával
  • a LOOP nyelv

Makrók a defmacro segítségével hozhatók létre. A macrolet használatával lokális, lexikális hatókörű makrók definiálhatók. Lehetséges a define-symbol-macro és a symbol-macrolet alkalmazásával is makrókat bevezetni a szimbólumokhoz.

Paul Graham az On Lisp című könyvében részletesen foglalkozik a makrókkal.

Példa új vezérlési szerkezet bevezetésére[szerkesztés | forrásszöveg szerkesztése]

A makrók lehetővé teszik a Lisp programozóknak, hogy bővítsék a nyelvet. Ennek egy tipőikus módja új vezérlési szerkezetek bevezetése. A példa egy until ciklust vezet be. Használata:

(until test form*)

A makró definíciója:

(defmacro until (test &body body)
  (let ((start-tag (gensym "START"))
        (end-tag   (gensym "END")))
    `(tagbody ,start-tag
              (when ,test (go ,end-tag))
              (progn ,@body)
              (go ,start-tag)
              ,end-tag)))

A tagbody egy primitív Common Lisp speciális operátor, ami tagek megnevezésére ad lehetőséget, amikre a go, mint címkékre ugorhat. A ` jel kódsémákat kínál, ahova az előtte álló, vessző után következő kifejezések értékei töltődnek be. A tagbody teszteli a végfeltételt. Ha ez igaz, akkor a vég tagre ugrik, különben végrehajtódik a törzs, és a start tagre ugrik.

Példa a kód használatára:

An example form using above until macro:

(until (= (random 10) 0)
  (write-line "Hello"))

A makró kifejthető a macroexpand-1 függvénnyel. Példánk kifejtése a következő:

(TAGBODY
 #:START1136
 (IF (ZEROP (RANDOM 10))
     (PROGN (GO #:END1137))
   NIL)
 (PROGN (WRITE-LINE "hello"))
 (GO #:START1136))
 #:END1137)

A fordító az összes makrót kifejti a forráskódban fordítás vagy kiértékelés előtt. A makrók absztrakt szintaxisfákat fogadó és absztrakt szintaxisfákat visszaadó függvényeknek tekinthetők, amelyek meghívódnak a végleges kód előállítása előtt. A makrók a közönséges Common Lispben íródnak, és bármilyen Common Lisp operátort használhatnak.

Változók átvétele és takarás[szerkesztés | forrásszöveg szerkesztése]

A Common Lisp makrók képesek változók átvételére, ha a törzsben szereplő szimbólumok némelyike egyezik bizonyos, a feldolgozandó kódban található szimbólumokkal. Ez lehetővé teszi, hogy a programozók speciális jelentést adjanak a szimbólumoknak. A paraméterátadás szó félrevezető, mert bármely névtérből akaratlanul is illeszkedhetnek szimbólumok, legyenek azok függvények, tagbody címkék, catch tagek, állapotkezelők vagy újraindító névterek.

A változók szabályozatlan átvétele programhibákat okozhat. Ez a következő módokon lehetséges:

Először is, a makró kifejtése figyelmeztetés nélkül szimbolikus referenciát hoz létre, amit a makró írója a globális névtérben kíván feloldani, de ahol a makró kifejtődik, ott a kód helyi árnyékoló definíciója ellopja ezt a referenciát. Erre úgy utalnak, mint 1. típusú átvételre.

Másodszor, a 2. típusú átvétel ennek éppen az ellenkezője: a makró egyes argumentumai a makró hívója által átadott kódrészletek, amely kódrészletek úgy vannak megírva, hogy referenciát adjanak a környező kötésekre. Azonban a makró ezeket a kódrészleteket oda szúrja be, ahol a saját kötései vannak definiálva, és így ezeket a referenciákat veszi át.

A Scheme nyelvjárás makróíró rendszere referenciális átlátszóságot kínál, ami mindkét problémát kiküszöböli. Ezt a makrórendszert higiénikusnak nevezik, ellentétben az unhigiénikus makrórendszerekkel, amelyek nem oldják meg automatikusan ezt a problémát.

A Common Lisp két megoldást kínál a makró higiéniára. Az egyik módszer a gensym használata, ami egyszeri szimbólumokat hoz létre, a másik a csomagok rendszere, ami azonban nem oldja meg teljesen az első problémát.

A gensym garantáltan olyan szimbólumokat hoz létre, amelyek nem fordulnak elő máshol, ezzel kizárja a véletlen egyezéseket. A gensym csak egy eszköz, ami nem automatikusan működik, hanem használata a programozón múlik. A gensymek használata újabb makrók készítésével egyszerűsíthető. Következetes alkalmazásuk megoldja az 2-es problémát, de az 1-es megoldása már nem ilyen egyszerű, mivel a makró kifejtése nem nevezheti át a környező kódban található interferáló szimbólumokat azzal, hogy átveszi a referenciáikat. A gensymek használhatók a szükséges globális szimbólumok stabil aliasaiként. A makró kifejtése sokkal inkább ezeket használja, mint a jól ismert neveket, így azok felüldefiniálása nem okoz mellékhatást a makrókban.

Ha a makrót egy csomagban definiáljuk, akkor használhatja a csomag belső szimbólumait a kifejtésben, ezzel mindkét problémára megoldást nyújt. Azonban az 1-es problémát ez a módszer nem oldja meg teljesen. Ennek az az oka, hogy a csomagon alapuló megoldás a privát, nem exportált belső szimbólumok használatán múlik. A könyvtári csomagok azonban publikusak, és gyakran kell importálni őket a felhasználói csomagokba.

Példa egy nemkívánatos felüldefiniálásra:

 ;; az UNTIL kifejtése szabaddá teszi a DO használatát
 (defmacro until (expression &body body)
   `(do () (,expression) ,@body))
 
 ;; a macrolet lexikális operátor kötést hoz létre a DO-hoz
 (macrolet ((do (...) ... valami ...))
   (until (= (random 10) 0) (write-line "Hello")))

Kifejtése után az UNTIL makró a DO operátort hívja, ami a szabványos DO operátort akarja jelenteni. Ezzel szemben a DO itt lehet, hogy valami egészen mást jelent, így a makró nem fog úgy működni, ahogy az írója akarta.

A Common Lisp ezt azzal oldja meg, hogy megtiltja a szabványos operátorok és függvények felüldefiniálását. Mivel a példa felüldefiniálja a DO szabványos operátort, ezért a példa nem hozható létre szabványos Common Lisp implementációban.

Állapotrendszer[szerkesztés | forrásszöveg szerkesztése]

A kivételkezelést az állapotok végzik a Common Lispben. Elemei ezek: condition, handler, restart. A conditionök állapotok, amelyek jelezhetnek kivételeket, vagy más fontos eseményeket. Ha létrejött egy állapot, akkor a rendszer keres hozzá egy handlert, és meghívja, ami elvégzi az ilyenkor szükséges teendőket. A handler kereshet restartokat, amelyek egyikével megoldhatja a helyzetet, kijavíthatja a hibát, reagálhat az eseményre. A felhasználói interfész részeként ezeket a restartokat a felhasználó elé tárhatja, aki kiválaszthatja, és meghívhatja valamelyiket. Mivel a kezelő a hiba környezetében hívódik meg, ezért minden információ rendelkezésre áll a hiba kijavításához a más nyelvekben szokásos visszagöngyölítéssel és a rutin leállításával szemben. A debugger a *DEBUGGER-HOOK* dinamikus változó segítségével konfigurálható vagy helyettesíthető.

A következő példában a felhasználó meg akar nyitni egy fájlt a Read-Eval-Print-LOOP (REPL) által meghívott test függvényben. A program ekkor négy lehetőséget tár a felhasználó elé, aki ezek közül a Retry OPEN using a different pathname (újrapróbálás egy másik elérési úttal) alternatívát választva a lispm-int.lisp helyett a lispm-init.lisp fájl megnyitásával próbálkozik. A felhasználó nem egy hibakezelési kódot adott meg, hanem a hibakezelést és az újraindítási lehetőséget a Lisp rendszer nyújtja, ami képes kezelni a hibát a program leállítása nélkül.

Command: (test ">zippy>lispm-int.lisp")
 
Hiba: A fájl nem található.
 
LMFS:OPEN-LOCAL-LMFS-1
   Arg 0: #P"lispm:>zippy>lispm-int.lisp.newest"
 
s-A, <Resume>: Újra megpróbálja megnyitni ezt a fájlt: 
               lispm:>zippy>lispm-int.lisp.newest
s-B:           Egy elérési úttal adott másik fájl megnyitása
s-C, <Abort>:  Visszatérés a Lisp Top Levelre a TELNET szerveren
s-D:           A folyamat újraindítása TELNET terminálból
 
-> Egy elérési úttal adott másik fájl megnyitása
Kérem a fájl elérési útját: [alapértelmezett lispm:>zippy>lispm-int.lisp.newest]:
   lispm:>zippy>lispm-init.lisp.newest
 
...a program folytatódik

Common Lisp Object System (CLOS)[szerkesztés | forrásszöveg szerkesztése]

A Common Lispben a Common Lisp Object System (CLOS) egy eszközkészlet az objektumorientált programozásra, ami az egyik legnagyobb kifejezőerővel bír a programnyelvek között. Peter Norvig példái alapján sok tervminta implementálását leegyszerűsítik a CLOS lehetőségei, mint a többszörös öröklődés, a mixinek, a metaosztályok, a multimetódusok, a metóduskombinációk és a többi hasznos tulajdonság.[6] Az objektumorientáltság megvalósítására számos javaslat érkezett a szabványhoz, de végül a CLOS mellett döntöttek.

A CLOS egy dinamikus objektumorientált rendszer multimetódusokkal és többszörös öröklődéssel. Lehetőségei nagy mértékben különböznek a statikus objektumorientált nyelvekétől, mint a Java vagy a C++. Ez a dinamizmus lehetővé teszi az osztályok és a metódusok módosítását. Metódusok adhatók az osztályokhoz és távolíthatók el belőlük, osztályok vehetők hozzá a rendszerhez és törölhetők, az objektumok hozzáigazíthatók a megváltozott osztályokhoz, vagy akár osztályt is változtathatnak.

Az ANSI Common Lisp beépítve tartalmazza a CLOSt. A generikus függvények úgy viselkednek, mint más függvények, és szintén első osztályú objektumok. A CLOS osztályok betagozódnak a Common Lisp típusai közé. Több Common Lisp típusnak van megfelelője az osztályok között. A CLOSnak még több potenciális haszna van a Common Lispben. A specifikációban nem szerepel az állapotok és a CLOs kapcsolata. Az elérési utak és a bájtfolyamok implementálhatók a CLOS segítségével. Ezeket a további felhasználási lehetőségeket a szabvány nem tartalmazza. Az implementációk CLOS használatával implementálják az elérési utakat, a bájtfolyamokat, az input-outputot, az állapotokat, sőt, magát a CLOSt is így implementálják.

Fordító és értelmező[szerkesztés | forrásszöveg szerkesztése]

A korábbi Lisp nyelvjárások egyes implementációit fordító és értelmező is támogatta. Gyakran viszont különbözött a szematika. Ezek a nyelvjárások lexikális hatókört használtak a fordítóban és dinamikust az értelmezőben. A Common Lisp szabványa mindkettőben lexikális hatókört ír elő alapértelmezettként. A fordító a compiler utasítással hívható egyes függvények és compile-file függvénnyel egyes fájlok lefordítására. A Common Lisp megengedi, hogy a típusdeklarációk hassanak a lefordított kód generálására. A következő opciók különböző szempontú optimalizációt tesznek lehetővé: speed sebesség, space tárhely, safety biztonság, debug debugolás és compilation-speed fordítási sebesség.

Az eval függvény a Lisp kód kiértékelésére szolgál. Az eval a kódot feldolgozott S-kifejezésként tekinti, és nem stringként, mint sok más nyelv. Így a kódban használhatók Lisp kifejezések, konstruálhatók listák, alkothatók szimbólumok. Egyes Common Lisp implementációk, mint a Clozure CL és az SBCL ezt a fordító meghívásával implementálják. Így a kód lefordul, miközben az eval kiértékeli.

A fájlfordító a compile-file függvénnyel hívható. A fordítással kapott fájlok kiterjesztése fasl, ami a fast load (gyors betöltés) rövidítése. A futó Common Lisp rendszerbe a load függvénnyel tölthetők be a le nem fordított kódokkal együtt. Az implementációtól függően bájtkódra (Java JVM kódra), C kódra (ami C fordítóval fordítható tovább), vagy natív kódra képes fordítani.

A Common Lisp implementációk interaktívan használhatók, még akkor is, ha az egész kód le van fordítva. Ezért nem felel meg a szkriptnyelv ideáljának.

A nyelv megkülönbözteti az olvasási időt, a fordítási időt, a betöltési időt és a futási időt, és lehetővé teszi, hogy a kód egyes részei a kívánt feldolgozási lépésben hajtódjanak végre.

Az interaktív fejlesztést egyes operátorok is támogatják. A DEFVAR csak akkor hoz létre új kötést, ha az még nem kötődik semmihez, szemben a DEFPARAMETER-rel, ami mindig létrehozza. Ez a különbségtétel hasznos az interaktív kiértékelésben, fordításban és kódbetöltésben.

Egyes eszközök támogatják értelmezők és fordítók írását. A szimbólumok első szintű objektumokból állnak, és direkt manipulálhatók kóddal. A PROGV speciális operátor lehetővé teszi, hogy a program lexikális kötéseket hozzon létre. Maga a Lisp fordító is elérhető futásidőben, ami egyszerűvé teszi fordítók és értelmezők írását más nyelvekhez.

Példakódok[szerkesztés | forrásszöveg szerkesztése]

Születésnapparadoxon[szerkesztés | forrásszöveg szerkesztése]

Ez a kód azt számolja ki, hogy hány személynek kell egy szobában lennie, hogy 50%-nál alacsonyabb legyen annak a valószínűsége, hogy ne legyen két ember, akinek ugyanazon a napon van a születésnapja.

(defconstant +ev-meret+ 365)
 
(defun szuletesnap-paradoxon (valoszinuseg emberek-szama)
  (let ((uj-valoszinuseg (* (/ (- +ev-meret+ emberek-szama)
                               +ev-meret+)
                            valoszinuseg)))
    (if (< uj-valoszinuseg 0.5)
        (1+ emberek-szama)
        (szuletesnap-paradox uj-valoszinuseg (1+ emberek-szama)))))

A függvényt REPL-lel (Read Eval Print Loop) meghívva:

CL-USER > (szuletesnap-paradoxon 1.0 1)
23

Személyek rendezése[szerkesztés | forrásszöveg szerkesztése]

Definiálunk egy SZEMELY osztályt, és egy metódust, ami kiírja a személy nevét és korát. Ezután definiálunk egy személyeket tartalmazó listát, végül végigiterálunk a rendezett listán.

(defclass szemely ()
  ((nev :initarg :nev :accessor szemely-nev)
   (kor  :initarg :kor  :accessor szemely-kor))
  (:documentation "PERSON osztály, NEV és KOR tagokkal."))
 
(defmethod display ((object szemely) stream)
  "SZEMELY objektum kiírása kimenő karakterfolyamra."
  (with-slots (nev kor) object
    (format stream "~a (~a)" nev kor)))
 
(defparameter *csoport*
  (list (make-instance 'szemely :nev "Bob"   :kor 33)
        (make-instance 'szemely :nev "Chris" :kor 16)
        (make-instance 'szemely :nev "Ash"   :kor 23))
  "SZEMELY objektumok listája.")
 
(dolist (person (sort (copy-list *csoport*)
                      #'>
                      :key #'szemely-kor))
  (display szemely *standard-output*)
  (terpri))

Három név jelenik meg, kor szerint csökkenő sorrendben.

Bob (33)
Ash (23)
Chris (16)

Gyors hatványozás[szerkesztés | forrásszöveg szerkesztése]

A LOOP makró használatának bemutatására:

(defun hatvany (x n)
  (loop with eredmeny = 1
        while (plusp n)
        when (oddp n) do (setf eredmeny (* eredmeny x))
        do (setf x (* x x)
                 n (truncate n 2))
        finally (return eredmeny)))

Példa használat:

CL-USER > (hatvany 2 200)
1606938044258990275541962092341162602522202993782792835301376

Összehasonlítás a beépített hatványozással:

CL-USER > (= (expt 2 200) (power 2 200))
T

Az elérhető héjak listája[szerkesztés | forrásszöveg szerkesztése]

A WITH-OPEN-FILE megnyit egy fájt, és szolgáltat egy karakterfolyamot. Ha a kifejezés visszatér, akkor a fájl automatikusan bezáródik. A FUNCALL függvényobjektumot hív. A LOOP összegyűjti a predikátumra illeszkedő sorokat:

(defun illeszkedo-sorok (fajl predikatum)
  "Egy FAJL sorainak szűrése a PREDIKATUM-ra. 
   Kiválogatja azokat a sorokat, 
   ahol a PREDIKATUM T-t ad vissza."
  (with-open-file (folyam fajl)
    (loop for line = (read-line folyam nil nil)
          while line
          when (funcall predikatum line)
          collect it)))

Az ELERHETO-HEJAK függvény meghívja a fenti ILLESZKEDO-SOROK függvényt egy elérési úttal, és egy névtelen függvénnyel, mint predikátummal. A predikátum egy héj elérési útjával, vagy NIL-lel tér vissza.

(defun elerheto-hejak (&optional (fajl #p"/etc/shells"))
  (illeszkedo-sorok
   fajl
   (lambda (sor)
     (and (plusp (length sor))
          (char= (char sor 0) #\/)
          (pathname
           (string-right-trim '(#\space #\tab) line))))))

Példa futás Mac OS X 10.6-on:

CL-USER > (elerheto-hejak)
(#P"/bin/bash" #P"/bin/csh" #P"/bin/ksh" #P"/bin/sh" #P"/bin/tcsh" #P"/bin/zsh")

Összehasonlítás más Lisp nyelvjárásokkal[szerkesztés | forrásszöveg szerkesztése]

A Common Lispet legtöbbször a Scheme-mel hasonlítják össze, mivel ez a két legnépszerűbb dialektus. A Scheme régibb, és később ugyanazok tervezték a Common Lispet, mint akik megalkották a Scheme-et. A Common Lisp bizottság elnöke Guy L. Steele volt, aki Gerald Jay Sussmannel együtt alkotta a Scheme-et.

A Common Lisp egy általános célú programozási nyelv, szemben például az Emacs Lisppel és az AutoLISP-pel, amelyek bizonyos termékek beágyazott nyelvei.

A legtöbb Lisp nyelvjárás, amelyen a Common Lisp alapul, mint például a ZetaLisp és a Franz Lisp, dinamikus hatókörű változókat használt az értelmezőben és lexikálisakat a fordítóban. A Scheme bevezette a lexikális változók egyedüli használatát; ez az ALGOL 68 alapján jó ötletnek tűnt. A Common Lisp a dinamikus hatókörű változókat is támogatja, de ezeket deklarálni kell. Az ANSI Common Lisp értelmező és fordító nem különbözhet a hatókörben.

A Common Lispet néha nevezik Lisp-2-nek, míg a Scheme-et Lisp-1-nek, mivel a Common Lispben külön névterük van a függvényeknek és a változóknak, míg a Scheme-ben egy névtérbe tartoznak. Valójában a Common Lispben még több névtér van, például a GO címkék, blokk nevek, és LOOP kulcsszavak. A Scheme-ben a változóknak olyan nevet kell adni, ami nem ütközik egy függvény nevével sem, ezért a lista típusú argumentumokat gyakran nevezik lis, lst, vagy lyst, hogy elkerüljék az összeütközést a list függvénnyel. A Common Lispben viszont explicit kell hivatkozni a függvény névtérre, amikor a függvényt egy magasabb rendű függvénynek adjuk paraméterül, amire a cikkben a sort szolgál példaként.

A két nyelvjárás a logikai változókat is másként kezeli. A Scheme igazságértékei a #t (igaz) és az #f (hamis). A Common Lisp igazságértékei: T az igaz, és NIL a hamis, ahol NIL az üres lista neve is. Ezzel a korábbi Lisp konvenciókat követi. A Common Lispben valójában mindennek igaz az igazságértéke, ami nem NIL. A Scheme mindent igaznak tekint, kivéve az #f-et. Mindez megengedi, hogy predikátumként használjanak egy függvényt, ami egy olyan értéket ad vissza, ami további számításokhoz felhasználható.

Végül a Scheme szabványa előírja a végrekurzív optimalizációt, míg a Common Lisp ezt nem követeli meg. Ennek ellenére a legtöbb implementáció tartalmazza, de csak akkor alkalmazza, ha a felhasználó optimalizált fordítást kér. A Common Lisp programozási stílusa kevésbé is támogatja ezt az optimalizációt; amit Scheme-ben végrekurzióval fejeznek ki, azt Common Lispben többnyire ciklussal oldják meg.

Implementációk[szerkesztés | forrásszöveg szerkesztése]

A Common Lispet specifikációként határozzák meg, mint az Adát és a C-t, és nem konkrét implementációt jelent. Több, szabványos és a szabványtól némileg eltérő implementációja van, amelyek egymástól lényegesen különböznek.

Ehhez járulnak az implementációkhoz adott könyvtári csomagok, amelyek nem mindig felelnek meg a szabványnak, vagy éppenséggel kiterjesztik további nem szabványos funkciókkal. Szabad és nyílt forrású termékek segítik hordozható programok írását. Ezek legtöbbje megtalálható a Common-Lisp.net vagy a Common Lisp Open Code Collection projekteknél.

Az implementációk fordíthatnak natív kódra, bájtkódra, vagy használhatnak fordítás helyett értelmezést. A nyelvet arra tervezték, hogy támogassa az inkrementális fordítást, a fájlok és a blokkok fordítását. A fordítás optimalizálására a specifikáció szabványos deklarációkat javasol. A legtöbb implementáció natív gépi kódra fordít. Egyes implementációkkal optimalizált stand-alone alkalmazások készíthetők. Mások bájtkódra fordítanak, ami lassabb, de jobban támogatja a hordozhatóságot. Vannak fordítók, amelyek C-re fordítanak. Mivel a Lisp környezetek interaktív promptot adnak, és egy-az-egyben fordítanak, ezért elterjedt az a tévhit, hogy a Common Lisp egy értelmezett szkript nyelv. Gyakran kihasználják az inkrementális fordítás lehetőségét.

Egyes Unixra készült implementációk, mint a CLISP és az SBCL, inkább a szkript nyelvként való használatot támogatja; így a Perlhez vagy a Bourne héjhoz hasonlóan hívható.

Implementációk listája[szerkesztés | forrásszöveg szerkesztése]

Kereskedelmi implementációk[szerkesztés | forrásszöveg szerkesztése]

Allegro Common Lisp
Microsoft Windows, FreeBSD, Linux, Apple Mac OS X és más UNIX variánsokra. Az Allegro CL fejlesztőkörnyezetet is biztosít Windows é Linux alá, ami az alap fejlesztőkörnyezetekhez képest további eszközökkel támogatja az alkalmazások fejlesztését.
Corman Common Lisp
Microsoft Windows.
Liquid Common Lisp
korábban Lucid Common Lisp. Most már csak karbantartják, és nem adnak ki új verziókat.
LispWorks
Microsoft Windows, FreeBSD, Linux, Apple Mac OS X és UNIX változatok. A LispWorks fejlesztőkörnyezetet is biztosít minden operációs rendszerre, ami az alap fejlesztőkörnyezetekhez képest további eszközökkel támogatja az alkalmazások fejlesztését.
mocl
iOS és Android.
Open Genera
DEC Alpha.
Scieneer Common Lisp
tudományos számításokra.

Szabad terjesztésű implementációk[szerkesztés | forrásszöveg szerkesztése]

Armed Bear Common Lisp 
Egy Java virtuális gépen futó implementáció.[7] Fordítója Java bájtkódra fordít, és támogatja a hozzáférést a Java könyvtárakhoz. Korábban az Armed Bear J Editor része volt.
CLISP
Bájtkódra fordító implementáció. Hordozható, és Unix-szerű operációs rendszereken (Macintosh, Mac OS X, Linux, Bsd, Solaris, Darwin) és Windowson és még több más operációs rendszeren is működik.
Clozure CL (CCL) 
Eredetileg a Macintosh Common Lisp szabad leágazása, és a Macintosh alá készült,de ma már számos operációs rendszeren fut, elérhető Mac OS X, FreeBSD, Linux, Solaris és Windows alatt is. Minden platformon támogat 32 és 64 bit x86 portot. Emellett támogatja a Power PC portokat Mac OS és Linux alatt. Régi neve OpenMCL, de ezt már nem használják, hogy össze ne tévesszék a Macintosh Common Lisp szabad verziójával.
CMUCL
Eredetileg a Carnegie Mellon University fejlesztette, de mára már önkéntesek által fejlesztett, nyílt forráskódú és ingyenes program. A CMUCL fordítója natív kódra fordít. Elérhető Linux és BSD operációs rendszereken Intel x86 gépekre; Linuxon Alpha gépekre; Mac OS Xen Intel x86-ra és PowerPC-re; és a Solaris, IRIX, és HP-UX operációs rendszereken natív architektúrákra.
Embeddable Common Lisp (ECL) 
Az ECL értelmezője és fordítója bájtkódra fordít, de C fordító közbeiktatásával natív kódra is képes fordítani. Ekkor először C-re fordítja a Lisp kódot,majd ezt fordítja tovább C fordítóval natív kódra. Az ECL beágyazható C programokba, és lehetővé teszi C kódok beágyazását Common Lisp programokba.
GNU Common Lisp (GCL) 
A GNU Project Lisp fordítója. Még nem egészen valósítja meg az ANSI szabványt, viszont számos nagy projekt használ GCL-t, mint a Maxima, AXIOM és használta az ACL2. A GCL elérhető Linux, Windows, Solaris, és FreeBSD alatt tizenegy különféle architektúrára.
Macintosh Common Lisp
Az 5.2-es verzió nyílt forrású, és az Apple Macintosh számítógépeken, PowerPC processzorral Mac OS X alá érhető el. A rajta alapuló RMCL az Intel alapú Apple Macintosh számítógépeken az Apple Rosetta bináris fordítóját használja.
ManKai Common Lisp (MKCL) 
Az ECL leágazása. Hangsúlyozza a megbízhatóságot és a stabilitást és a kód minőségét egy erősen átdolgozott, natívan többszálú, futásidejű rendszer által. Linuxon az MKCL a POSIXnak minden tekintetben megfelelő futás idejű rendszer.
Movitz
Operációs rendszer nélkül elérhető Lisp környezet az x86 architektúrájú számítógépekre.
Poplog
A Poplog implementálja a Common Lisp egy verzióját a POP-11-gyel, és opcionálisan támogatja a Prologot, és a Standard ML (SML)-t, ami lehetővé teszi a kevert nyelvű programozást. Mindegyik a POP-11 nyelven van implementálva amit inkrementálisan fordítottak. Beépített Emacs-szerű szerkesztője kommunikál a fordítóval.
Steel Bank Common Lisp (SBCL) 
A CMUCL leágazása. Az SBCL leginkább a karbantarthatóság hangsúlyozásában különbözik a CMU CL-től,[8] SBCL runs on the platforms CMUCL does, except HP/UX; in addition, it runs on Linux for AMD64, PowerPC, SPARC, MIPS, Windows x86[9] és kísérleti jelleggel működik a a Windows AMD64-en. Az SBCL alapértelmezetten nem használ értelmezőt; minden kifejezés natív kódra fordul, kivéve, ha a felhasználó bekapcsolja az értelmezőt. Fordítója gyors natív kódot állít elő.[10]
Ufasoft Common Lisp
A CLISP portja a Windowsra, aminek magja C++-ban készült.

Régebbi implementációk[szerkesztés | forrásszöveg szerkesztése]

Austin Kyoto Common Lisp 
a Kyoto Common Lisp egy átdolgozása
Butterfly Common Lisp 
egy Scheme-ben írt implementáció a BBN Butterfly multiprocesszoros számítógépre
CLICC 
egy fordító, ami a Common Lispet C-re fordítja
CLOE 
PC-kre készült Common Lisp a Symbolicstől
Codemist Common Lisp 
az Axiom komputeralgebra rendszer kereskedelmi verziójához használt implementációt
ExperCommon Lisp 
az ExperTelligence által készített korai implementáció az Apple Macintosh számára
Golden Common Lisp
a GoldHill Inc implementációja PC-re.
Ibuki Common Lisp 
a Kyoto Common Lisp kereskedelmi verziója
Golden Common Lisp
GoldHill Inc. által készített implementáció PC-re,
Kyoto Common Lisp
az első Common Lisp fordító, aminek a C volt a célnyelve. Belőle származik a GCL, az ECL és az MKCL,
egy kis Common Lisp a beágyazott rendszerek számára. Ezt eredetileg az IS Robotics fejlesztette, ami ma iRobot,
Lisp Machine (a Symbolics, a TI és a Xerox közreműködésével)
a Common Lisp és a saját natív Lisp nyelvjárás (Lisp Machine Lisp vagy InterLisp) implementációja. A CLOS is elérhető volt. A Symbolics Common Lisp verziója az ANSI Common Lispen alapul.
Procyon Common Lisp 
implementáció Windows és Mac OS alá, amit a Franz Lisp használt az Allegro CL támogatására Windowson.
Star Sapphire Common LISP 
egy implementáció PC-re
SubL
a Common Lisp változata, amin a Cyc tudás alapú rendszerét implementálták
Top Level Common Lisp 
korai implementáció a párhuzamos végrehajtás támogatására
WCL 
megosztott könyvtáras implementáció
Vax Common Lisp
a Digital Equipment Corporationáltal fejlesztett implementáció VAX rendszerekre VMS vagy ULTRIX alá
XLISP David Betztől

Felhasználása[szerkesztés | forrásszöveg szerkesztése]

A Common Lispet használják tudományos célú fejlesztésre, például mesterséges intelligencia céljára, prototípus alapú fejlesztésre és általános célú alkalmazások fejlesztésére is.

A Common Lisp a kereskedelmi alkalmazásokban is jelen van, például a Yahoo! Store oldalban, amit Paul Graham tervezett, és később újraírtak Perlben és C++-ban. További nevezetes példák:

  • az ACT-R kognitív architektúra, amit sok kutatási projekt használ.
  • az Authorizer's Assistant,[11][12] egy nagy szabály alapú rendszer, amit az American Express használ a hitelkérelmek elemzésére.
  • a Cyc, egy hosszú távú projekt, ami a józan észt próbálja tudás alapú rendszerbe implementálni
  • a Dynamic Analysis and Replanning Tool (DART), amit a DARPA harminc éves mesterséges intelligencia projektjei közül egyedüliként folytattak 1991 és 1995 között.
  • G2 from Gensym, egy valós idejű üzleti szabály alapú motor (BRE)[13]
  • a Jak and Daxter videojáték fejlesztőkörnyezete, a Naughty Dog fejlesztésében.
  • az ITA Software keresője, amelyet olyan utazási oldalak használnak, mint az Orbitz, a Kayak.com valamint légitársaságok, mint az American Airlines, Continental Airlines és US Airways.
  • Mirai, egy 3d grafikai eszköz. A Gyűrűk Ura: A két torony filmváltozatában Gollam arcának animálásához használták.
  • Prototype Verification System (PVS), automatizált környezet a formális specifikáció és verifikáció számára.
  • a PWGL egy vizuális programozási környezet, ami a Common Lispen alapul. Használják a számítógéppel segített kompozícióhoz és a hangszintézishez.[14]
  • SPIKE, órarendkészítő a föld és az űr alapú obszervatóriumok és műholdak számára. Nevezetes alkalmazása a Hubble Space Telescope-ra.[15]

Néhány nevezetes alkalmazás a szabad szoftverek világából:

  • ACL2,automatizált tételbizonyító a Common Lisp applikatív változata számára.
  • Axiom.
  • Maxima, egy másik komputeralgebra rendszer.
  • OpenMusic, egy Common Lispen alapuló objektumorientált vizuális programozási környezet a számítógéppel segített komponálás számára.
  • Stumpwm, X11 ablakkezelő, ami teljesen Common Lispben készült.

Jegyzetek[szerkesztés | forrásszöveg szerkesztése]

  1. Quoted from cover of cited standard. ANSI INCITS 226-1994 (R2004), for sale on standard's document page.
  2. Authorship of the Common Lisp HyperSpec
  3. Reddy, Abhishek: Features of Common Lisp, 2008. augusztus 22
  4. Unicode support. The Common Lisp Wiki. (Hozzáférés: 2008. augusztus 21.)
  5. (1988. június 1.) „Technical Issues of Separation in Function Cells and Value Cells”. Lisp and Symbolic Computation 1 (1), 81–101. o.  
  6. Peter Norvig, Design Patterns in Dynamic Programming
  7. Armed Bear Common Lisp
  8. History and Copyright. Steel Bank Common Lisp
  9. Platform Table. Steel Bank Common Lisp
  10. SBCL ranks above other dynamic language implementations in the 'Computer Language Benchmark Game'
  11. Authorizer's Assistant
  12. American Express Authorizer's Assistant
  13. Real-time Application Development. Gensym. Retrieved on 2013-07-17.
  14. PWGL - Home. . Retrieved on 2013-07-17.
  15. Spike Planning and Scheduling System. Stsci.edu. Retrieved on 2013-07-17.

Források[szerkesztés | forrásszöveg szerkesztése]

Fordítás[szerkesztés | forrásszöveg szerkesztése]

Ez a szócikk részben vagy egészben a Common Lisp című angol Wikipédia-szócikk ezen változatának fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel.