Lisp - Common Lisp

Lisp
Paradigma Multi-paradigma : procedurální , funkční , objektově orientované , meta , reflexní , generické
Rodina Lisp
Navrhl Scott Fahlman , Richard P. Gabriel , David A. Moon , Kent Pitman , Guy Steele , Dan Weinreb
Vývojář Výbor ANSI X3J13
Poprvé se objevil 1984 (před 37 lety) , 1994 (před 27 lety) pro ANSI Common Lisp (1984) (1994)
Kázeň při psaní Dynamický , silný
Rozsah Lexikální, volitelně dynamický
OS Cross-platform
Rozšíření názvu souboru .lisp, .lsp, .l, .cl, .fasl
webová stránka common-lisp .net
Hlavní implementace
Allegro CL , ABCL , CLISP , Clozure CL , CMUCL , ECL , GCL , LispWorks , Scieneer CL , SBCL , Symbolics Common Lisp
Nářečí
CLtL1, CLtL2, ANSI Common Lisp
Ovlivněn
Lisp , Lisp Machine Lisp , Maclisp , Scheme , Interlisp
Ovlivněn
Clojure , Dylan , Emacs Lisp , EuLisp , ISLISP , *Lisp , AutoLisp , Julia , Moose , R , SKILL , SubL

Common Lisp ( CL ) je dialekt programovacího jazyka Lisp , publikoval v ANSI standardní dokumentu ANSI INCITS 226-1994 (S20018) (dříve X3.226-1994 (R1999) ). Common Lisp HyperSpec , hypertextovými odkazy HTML verze, byl odvozen ze standardu ANSI Common Lisp.

Jazyk Common Lisp byl vyvinut jako standardizovaný a vylepšený nástupce Maclispu . Na začátku 80. let již několik skupin pracovalo na různých nástupcích MacLispu: Lisp Machine Lisp (aka ZetaLisp), Spice Lisp , NIL a S-1 Lisp . Common Lisp se snažil sjednotit, standardizovat a rozšířit funkce těchto nářečí MacLisp. Common Lisp není implementace, ale spíše jazyková specifikace . K dispozici je několik implementací standardu Common Lisp, včetně bezplatného a otevřeného softwaru a proprietárních produktů. Common Lisp je univerzální programovací jazyk s více paradigmaty . Podporuje kombinaci procedurálních , funkčních a objektově orientovaných programovacích paradigmat. Jako dynamický programovací jazyk usnadňuje vývojový a přírůstkový vývoj softwaru s iterativní kompilací do efektivních běhových programů. Tento přírůstkový vývoj se často provádí interaktivně bez přerušení běžící aplikace.

Podporuje také volitelnou anotaci typu a přetypování, které lze podle potřeby přidat v pozdějších fázích profilování a optimalizace, aby kompilátor mohl generovat efektivnější kód. Například fixnummůže obsahovat unboxed integer v rozsahu podporovaném hardwarem a implementací, což umožňuje efektivnější aritmetiku než u velkých celých čísel nebo libovolných typů přesnosti. Podobně může být kompilátoru na základě modulů nebo funkcí sděleno, jaký typ úrovně bezpečnosti je požadován, pomocí deklarací optimalizace .

Common Lisp zahrnuje CLOS , objektový systém, který podporuje multimetody a kombinace metod. Často je implementován pomocí protokolu Metaobject .

Common Lisp je rozšiřitelný pomocí standardních funkcí, jako jsou makra Lisp (transformace kódu) a čtecí makra (vstupní analyzátory znaků).

Common Lisp poskytuje částečnou zpětnou kompatibilitu s původními Lisp Maclisp a John McCarthy . To umožňuje portování staršího softwaru Lisp do Common Lisp.

Dějiny

Práce na Common Lisp začaly v roce 1981 po iniciativě manažera ARPA Boba Engelmora vyvinout jednotný komunitní standardní dialekt Lisp. Velká část původního jazykového designu byla provedena prostřednictvím elektronické pošty. V roce 1982 Guy L. Steele Jr. podal první přehled Common Lisp na sympoziu ACM 1982 o LISP a funkčním programování.

První jazyková dokumentace byla vydána v roce 1984 jako první vydání Common Lisp the Language (známý jako CLtL1). Druhé vydání (známé jako CLtL2), vydané v roce 1990, zahrnovalo mnoho změn v jazyce, provedených během procesu normalizace ANSI Common Lisp: rozšířená syntaxe LOOP, Common Lisp Object System, Condition System pro zpracování chyb, rozhraní k krásná tiskárna a mnoho dalšího. Ale CLtL2 nepopisuje konečný standard ANSI Common Lisp, a proto není dokumentací ANSI Common Lisp. Konečný standard ANSI Common Lisp pak byl publikován v roce 1994. Od té doby nebyla publikována žádná aktualizace standardu. Implementace a knihovny poskytly různá rozšíření a vylepšení Common Lisp (příklady jsou Unicode, Concurrency, CLOS-based IO).

Syntax

Common Lisp je dialekt Lispu. Používá výrazy S k označení struktury kódu i dat. Volání funkcí, makra a speciální formuláře se zapisují jako seznamy s názvem operátora jako v těchto příkladech:

 (+ 2 2)           ; adds 2 and 2, yielding 4. The function's name is '+'. Lisp has no operators as such.
 (defvar *x*)      ; Ensures that a variable *x* exists,
                   ; without giving it a value. The asterisks are part of
                   ; the name, by convention denoting a special (global) variable. 
                   ; The symbol *x* is also hereby endowed with the property that
                   ; subsequent bindings of it are dynamic, rather than lexical.
 (setf *x* 42.1)   ; Sets the variable *x* to the floating-point value 42.1
 ;; Define a function that squares a number:
 (defun square (x)
   (* x x))
 ;; Execute the function:
 (square 3)        ; Returns 9
 ;; The 'let' construct creates a scope for local variables. Here
 ;; the variable 'a' is bound to 6 and the variable 'b' is bound
 ;; to 4. Inside the 'let' is a 'body', where the last computed value is returned.
 ;; Here the result of adding a and b is returned from the 'let' expression.
 ;; The variables a and b have lexical scope, unless the symbols have been
 ;; marked as special variables (for instance by a prior DEFVAR).
 (let ((a 6)
       (b 4))
   (+ a b))        ; returns 10

Typy dat

Common Lisp má mnoho datových typů .

Skalární typy

Číslo typy patří celá čísla , poměry , čísel s plovoucí desetinnou čárkou a komplexní čísla . Common Lisp používá bignums k reprezentaci číselných hodnot libovolné velikosti a přesnosti. Typ poměru přesně reprezentuje zlomky, zařízení není k dispozici v mnoha jazycích. Common Lisp podle potřeby automaticky vynucuje číselné hodnoty mezi těmito typy.

Typ znaku Common Lisp není omezen na znaky ASCII . Většina moderních implementací umožňuje znaky Unicode .

Typ symbolu je jazykům Lisp společný, ale mimo ně je do značné míry neznámý. Symbol je jedinečný pojmenovaný datový objekt s několika částmi: název, hodnota, funkce, seznam vlastností a balíček. Z nich jsou hodnotové buňky a funkční buňky nejdůležitější. Symboly v Lispu se často používají podobně jako identifikátory v jiných jazycích: udržovat hodnotu proměnné; existuje však mnoho dalších použití. Normálně je při vyhodnocení symbolu vrácena jeho hodnota. Některé symboly se vyhodnocují samy, například všechny symboly v balíčku klíčových slov se samy vyhodnocují. Booleovské hodnoty v Common Lisp jsou reprezentovány samovyhodnocujícími se symboly T a NIL. Common Lisp má jmenné prostory pro symboly, nazývané 'balíčky'.

K zaokrouhlování skalárních číselných hodnot různými způsoby je k dispozici řada funkcí . Funkce roundzaokrouhlí argument na nejbližší celé číslo, přičemž případy v polovině cesty se zaokrouhlí na sudé celé číslo. Funkce truncate, floora ceilingzaokrouhlení směrem k nule, dolů nebo nahoru. Všechny tyto funkce vrací vyřazenou zlomkovou část jako sekundární hodnotu. Například (floor -2.5)výnosy −3, 0,5; (ceiling -2.5)výnosy −2, −0,5; (round 2.5)výtěžky 2, 0,5; a (round 3.5)výtěžky 4, -0,5.

Datové struktury

Sekvenční typy v Common Lisp zahrnují seznamy, vektory, bitové vektory a řetězce. Existuje mnoho operací, které mohou fungovat na jakémkoli typu sekvence.

Stejně jako téměř ve všech ostatních dialektech Lisp, seznamy v Common Lisp jsou složeny z konzul , někdy nazývaných cons buňky nebo páry . Nevýhoda je datová struktura se dvěma sloty, která se nazývá auto a cdr . Seznam je propojený řetězec úspor nebo prázdný seznam. Vůz každé nevýhody odkazuje na člena seznamu (případně jiný seznam). Cdr každé nevýhody odkazuje na další nevýhody - s výjimkou posledních nevýhod v seznamu, jejichž cdr odkazuje na nilhodnotu. Conses lze také snadno použít k implementaci stromů a dalších složitých datových struktur; ačkoli se obvykle doporučuje použít místo toho instance struktury nebo třídy. Je také možné vytvářet kruhové datové struktury s konzoly.

Common Lisp podporuje vícerozměrná pole a v případě potřeby může dynamicky měnit velikost nastavitelných polí. Pro maticovou matematiku lze použít vícerozměrná pole. Vektor je jednorozměrné pole. Pole mohou nést jakýkoli typ jako členy (dokonce i smíšené typy ve stejném poli) nebo mohou být specializované tak, aby obsahovaly konkrétní typ členů, jako ve vektoru bitů. Obvykle je podporováno pouze několik typů. Mnoho implementací může optimalizovat funkce pole, když je použité pole specializované na typ. Standardní jsou dva typy polí specializované na typ: řetězec je vektor znaků, zatímco bitový vektor je vektor bitů .

Hashovací tabulky ukládají asociace mezi datovými objekty. Jako klíč nebo hodnotu lze použít jakýkoli objekt. Tabulkám hash se automaticky mění velikost podle potřeby.

Balíčky jsou kolekce symbolů, které se používají hlavně k oddělení částí programu do jmenných prostorů . Balíček může exportovat některé symboly a označit je jako součást veřejného rozhraní. Balíčky mohou používat jiné balíčky.

Struktury , podobné použití v strukturách C a Pascal , představují libovolné složité datové struktury s libovolným počtem a typem polí (nazývaných sloty ). Struktury umožňují jednoduchou dědičnost.

Třídy jsou podobné strukturám, ale nabízejí dynamičtější funkce a vícenásobnou dědičnost. (Viz ZAVŘENO ). Třídy byly do Common Lisp přidány pozdě a existuje určité koncepční překrývání se strukturami. Objekty vytvořené ze tříd se nazývají instance . Zvláštním případem jsou generické funkce. Obecné funkce jsou funkce i instance.

Funkce

Common Lisp podporuje prvotřídní funkce . Například je možné zapisovat funkce, které berou jiné funkce jako argumenty, nebo také návratové funkce. To umožňuje popsat velmi obecné operace.

Knihovna Common Lisp do značné míry spoléhá na takové funkce vyššího řádu. Například sortfunkce bere relační operátor jako argument a klíčovou funkci jako volitelný argument klíčového slova. To lze použít nejen k třídění jakéhokoli typu dat, ale také k řazení datových struktur podle klíče.

 ;; Sorts the list using the > and < function as the relational operator.
 (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)
 ;; Sorts the list according to the first element of each sub-list.
 (sort (list '(9 A) '(3 B) '(4 C)) #'< :key #'first)   ; Returns ((3 B) (4 C) (9 A))

Vyhodnocovací model funkcí je velmi jednoduchý. Když hodnotitel narazí na formulář (f a1 a2...), předpokládá, že symbol s názvem f je jedním z následujících:

  1. Speciální operátor (lze snadno porovnat s pevným seznamem)
  2. Provozovatel makra (musí být definován dříve)
  3. Název funkce (výchozí), což může být buď symbol, nebo podformulář začínající symbolem lambda.

Pokud f je název funkce, pak jsou argumenty a1, a2, ..., an vyhodnoceny v pořadí zleva doprava a funkce je nalezena a vyvolána s hodnotami zadanými jako parametry.

Definování funkcí

Makrodefun definuje funkce, kde je definice funkce dává název funkce, názvy argumentů, a těla funkce:

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

Definice funkcí mohou zahrnovat direktivy kompilátoru , známé jako deklarace , které kompilátoru poskytnou rady ohledně nastavení optimalizace nebo datových typů argumentů. Mohou také zahrnovat řetězce dokumentace (docstrings), které může systém Lisp použít k poskytování interaktivní dokumentace:

 (defun square (x)
   "Calculates the square of the single-float x."
   (declare (single-float x) (optimize (speed 3) (debug 0) (safety 1)))
   (the single-float (* x x)))

Anonymní funkce ( literály funkcí ) jsou definovány pomocí lambdavýrazů, např. (lambda (x) (* x x))Pro funkci, která umocňuje její argument. Styl programování Lisp často používá funkce vyššího řádu, pro které je užitečné poskytnout jako argumenty anonymní funkce.

Místní funkce lze definovat pomocí fleta labels.

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

S definicí a manipulací funkcí souvisí několik dalších operátorů. Například funkce může být kompilována s compileoperátorem. (Některé systémy Lisp standardně spouštějí funkce pomocí tlumočníka, pokud nedostanou pokyn ke kompilaci; jiné kompilují všechny funkce).

Definování obecných funkcí a metod

Makro defgenericdefinuje obecné funkce . Obecné funkce jsou souborem metod . Makro defmethoddefinuje metody.

Metody mohou specializovat své parametry na standardní třídy CLOS , systémové třídy , třídy struktury nebo jednotlivé objekty. Pro mnoho typů existují odpovídající systémové třídy .

Když je volána obecná funkce, vícenásobné odeslání určí efektivní metodu, kterou je třeba použít.

 (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))
(defmethod add ((a string) (b string))
  (concatenate 'string a b))
 (add 2 3)                   ; returns 5
 (add #(1 2 3 4) 7)          ; returns #(8 9 10 11)
 (add #(1 2 3 4) #(4 3 2 1)) ; returns #(5 5 5 5)
 (add "COMMON " "LISP")      ; returns "COMMON LISP"

Generické funkce jsou také prvotřídním datovým typem . Obecných funkcí a metod je mnohem více, než je popsáno výše.

Obor názvů funkcí

Obor názvů pro názvy funkcí je oddělený od oboru názvů pro datové proměnné. Toto je klíčový rozdíl mezi Common Lisp a Scheme . Pro Common Lisp, operátory, které definují jména ve funkci názvů patří defun, flet, labels, defmethoda defgeneric.

Chcete -li předat funkci podle názvu jako argument jiné funkci, musíte použít functionspeciální operátor, běžně zkráceně jako #'. První sortpříklad výše odkazuje na funkci pojmenovanou symbolem >v oboru názvů funkcí s kódem #'>. Naopak pro volání funkce předané tímto způsobem by se funcallna argument použil operátor.

Vyhodnocovací model schématu je jednodušší: existuje pouze jeden obor názvů a vyhodnocují se všechny pozice ve formuláři (v libovolném pořadí) - nejen argumenty. Kód napsaný jedním dialektem je proto někdy matoucí pro programátory zkušenější v druhém. Například mnozí programátoři Common Lisp rádi používají popisné názvy proměnných, jako je seznam nebo řetězec, což by ve Scheme mohlo způsobit problémy, protože by lokálně stínovaly názvy funkcí.

Zda je samostatný obor názvů pro funkce výhodou, je zdrojem sporu v komunitě Lisp. Obvykle se označuje jako debata Lisp-1 vs. Lisp-2 . Lisp-1 odkazuje na model Scheme a Lisp-2 odkazuje na model Common Lisp. Tato jména byla vytvořena v dokumentu z roku 1988 Richardem P. Gabrielem a Kentem Pitmanem , který tyto dva přístupy značně srovnává.

Více návratových hodnot

Common Lisp podporuje koncept více hodnot , kde jakýkoli výraz má vždy jednu primární hodnotu , ale může mít také libovolný počet sekundárních hodnot , které mohou být přijímány a kontrolovány zúčastněnými volajícími. Tento koncept je odlišný od vracení hodnoty seznamu, protože sekundární hodnoty jsou zcela volitelné a předávány prostřednictvím vyhrazeného bočního kanálu. To znamená, že volající mohou zůstat zcela nevědomí toho, že sekundární hodnoty jsou k dispozici, pokud je nepotřebují, a je vhodné používat mechanismus pro sdělování informací, který je někdy užitečný, ale ne vždy nezbytný. Například,

  • TRUNCATEFunkce zaokrouhlí dané číslo na celé číslo směrem k nule. Vrací však také zbytek jako sekundární hodnotu, takže je velmi snadné určit, jaká hodnota byla zkrácena. Podporuje také volitelný parametr dělitel, který lze použít k provádění euklidovského dělení triviálně:
(let ((x 1266778)
      (y 458))
  (multiple-value-bind (quotient remainder)
      (truncate x y)
    (format nil "~A divided by ~A is ~A remainder ~A" x y quotient remainder)))

;;;; => "1266778 divided by 458 is 2765 remainder 408"
  • GETHASHvrátí hodnotu klíče v asociativní mapě nebo výchozí hodnotu jinak a sekundární logickou hodnotu označující, zda byla hodnota nalezena. Kód, který se nestará o to, zda byla hodnota nalezena nebo poskytnuta jako výchozí, jej může jednoduše použít tak, jak je, ale když je takové rozlišení důležité, může zkontrolovat sekundární boolean a reagovat odpovídajícím způsobem. Oba případy použití jsou podporovány stejným hovorem a žádný není druhým zbytečně zatěžován ani omezován. Díky této funkci na jazykové úrovni odpadá potřeba kontrolovat existenci klíče nebo jej porovnávat s hodnotou null, jak by se to dělalo v jiných jazycích.
(defun get-answer (library)
  (gethash 'answer library 42))

(defun the-answer-1 (library)
  (format nil "The answer is ~A" (get-answer library)))
;;;; Returns "The answer is 42" if ANSWER not present in LIBRARY

(defun the-answer-2 (library)
  (multiple-value-bind (answer sure-p)
      (get-answer library)
    (if (not sure-p)
        "I don't know"
     (format nil "The answer is ~A" answer))))
;;;; Returns "I don't know" if ANSWER not present in LIBRARY

Několik standardních formulářů podporuje více hodnot, z nichž nejběžnější jsou MULTIPLE-VALUE-BINDspeciální formuláře pro přístup k sekundárním hodnotám a VALUESpro vracení více hodnot:

(defun magic-eight-ball ()
  "Return an outlook prediction, with the probability as a secondary value"
  (values "Outlook good" (random 1.0)))

;;;; => "Outlook good"
;;;; => 0.3187

Jiné typy

Mezi další datové typy v Common Lisp patří:

  • Název cesty představuje soubory a adresáře v souborovém systému . Zařízení Common Lisp pathname je obecnější než konvence pojmenování souborů většiny operačních systémů, takže přístup programů Lisp k souborům je široce přenosný napříč různými systémy.
  • Vstupní a výstupní toky představují zdroje a propady binárních nebo textových dat, například terminálu nebo otevřených souborů.
  • Common Lisp má vestavěný generátor pseudonáhodných čísel (PRNG). Objekty náhodného stavu představují opakovaně použitelné zdroje pseudonáhodných čísel, což uživateli umožňuje vložit PRNG nebo způsobit, že přehraje sekvenci.
  • Podmínky jsou typ používaný k reprezentaci chyb, výjimek a dalších „zajímavých“ událostí, na které může program reagovat.
  • Třídy jsou objekty první třídy a samy o sobě jsou instancemi tříd nazývaných třídy metaobjektů ( zkráceně metaclasses ).
  • Čtecí tabulky jsou typem objektu, který řídí, jak čtečka Common Lispu analyzuje text zdrojového kódu. Tím, že programátor ovládá, která čitelná se používá při čtení kódu, může změnit nebo rozšířit syntaxi jazyka.

Rozsah

Stejně jako programy v mnoha jiných programovacích jazycích, i programy Common Lisp využívají názvy k označení proměnných, funkcí a mnoha dalších druhů entit. Pojmenované odkazy podléhají rozsahu.

Asociace mezi jménem a entitou, na kterou název odkazuje, se nazývá vazba.

Rozsah se vztahuje na soubor okolností, za nichž se u jména určí, že má konkrétní vazbu.

Determinery rozsahu

Okolnosti, které určují rozsah v Common Lisp, zahrnují:

  • umístění odkazu ve výrazu. Pokud je to pozice úplně vlevo sloučeniny, odkazuje na speciální operátor nebo vazbu makra nebo funkce, jinak na proměnnou vazbu nebo něco jiného.
  • druh výrazu, ve kterém se reference odehrává. Například (go x)znamená řízení přenosu na označení x, zatímco (print x)odkazuje na proměnnou x. Oba obory xmohou být aktivní ve stejné oblasti textu programu, protože štítky tagbody jsou v odděleném oboru názvů od názvů proměnných. Speciální formulář nebo formulář makra má úplnou kontrolu nad významy všech symbolů v jeho syntaxi. Například v (defclass x (a b) ())definici třídy (a b)je seznam základních tříd, takže tyto názvy jsou vyhledávány v prostoru názvů tříd a xnejedná se o odkaz na existující vazbu, ale je odvozen název nové třídy od aa b. Tato fakta vyplývají čistě ze sémantiky defclass. Jediným obecným faktem o tomto výrazu je to, že defclassodkazuje na vazbu makra; všechno ostatní je na defclass.
  • umístění odkazu v textu programu. Pokud je například odkaz na proměnnou xuzavřen ve vazebném konstruktu, jako je a, letkterý definuje vazbu pro x, pak je odkaz v rozsahu vytvořeném touto vazbou.
  • pro odkaz na proměnnou, ať už byl variabilní symbol místně nebo globálně prohlášen za speciální. To určuje, zda je reference vyřešena v lexikálním prostředí, nebo v dynamickém prostředí.
  • konkrétní instance prostředí, ve kterém je odkaz vyřešen. Prostředí je slovník run-time, který mapuje symboly na vazby. Každý druh odkazu používá svůj vlastní druh prostředí. Odkazy na lexikální proměnné jsou řešeny v lexikálním prostředí atd. Ke stejné referenci může být přidruženo více než jedno prostředí. Například díky rekurzi nebo použití více vláken může existovat více aktivací stejné funkce současně. Tyto aktivace sdílejí stejný text programu, ale každá má svou vlastní instanci lexikálního prostředí.

Aby pochopil, na co symbol odkazuje, musí programátor Common Lisp vědět, jaký druh odkazu je vyjádřen, jaký rozsah používá, pokud se jedná o proměnný odkaz (dynamický versus lexikální rozsah), a také situaci za běhu: v jakém prostředí je reference vyřešena, kde byla vazba zavedena do prostředí atd.

Druhy prostředí

Globální

Některá prostředí v Lispu jsou globálně všudypřítomná. Pokud je například definován nový typ, je poté znám všude. Odkazy na tento typ vyhledejte v tomto globálním prostředí.

Dynamický

Jedním typem prostředí v Common Lisp je dynamické prostředí. Vazby vytvořené v tomto prostředí mají dynamický rozsah, což znamená, že vazba je vytvořena na začátku provádění nějakého konstruktu, například letbloku, a zmizí, když tento konstrukt dokončí provádění: jeho životnost je svázána s dynamickou aktivací a deaktivací překážka. Dynamická vazba však není v tomto bloku jen viditelná; je také viditelný pro všechny funkce vyvolané z tohoto bloku. Tento typ viditelnosti je známý jako neurčitý rozsah. Vazby, které vykazují dynamický rozsah (životnost vázaná na aktivaci a deaktivaci bloku) a neurčitý rozsah (viditelné pro všechny funkce, které jsou z tohoto bloku volány), mají údajně dynamický rozsah.

Common Lisp má podporu pro dynamicky nastavované proměnné, které se také nazývají speciální proměnné. Některé další druhy vazeb jsou také nutně dynamicky nastaveny, jako jsou restarty a tagy catch. Funkční vazby nelze dynamicky definovat pomocí flet(což poskytuje pouze lexikálně vymezené funkční vazby), ale funkční objekty (objekt první úrovně v Common Lisp) lze přiřadit dynamicky definovaným proměnným, svázaným pomocí letv dynamickém rozsahu a poté volat pomocí funcallnebo APPLY.

Dynamický rozsah je mimořádně užitečný, protože globálním proměnným dodává referenční přehlednost a disciplínu . Globální proměnné jsou v počítačové vědě odsuzovány jako potenciální zdroje chyb, protože mohou vést k ad-hoc, skrytým komunikačním kanálům mezi moduly, které vedou k nechtěným, překvapivým interakcím.

V Common Lisp se speciální proměnná, která má pouze vazbu na nejvyšší úrovni, chová stejně jako globální proměnná v jiných programovacích jazycích. Do něj lze uložit novou hodnotu a tato hodnota jednoduše nahradí to, co je ve vazbě nejvyšší úrovně. Neopatrná náhrada hodnoty globální proměnné je jádrem chyb způsobených používáním globálních proměnných. Dalším způsobem, jak pracovat se speciální proměnnou, je poskytnout jí novou místní vazbu ve výrazu. Někdy se tomu říká „rebinding“ proměnné. Vazba dynamicky vymezené proměnné dočasně vytvoří nové umístění paměti pro tuto proměnnou a přidruží název k tomuto umístění. Zatímco tato vazba je účinná, všechny odkazy na tuto proměnnou odkazují na novou vazbu; předchozí vazba je skrytá. Když provádění výrazu vazby skončí, dočasné umístění paměti je pryč a stará vazba je odhalena s původní hodnotou neporušenou. Samozřejmě lze vnořit více dynamických vazeb pro stejnou proměnnou.

V implementacích Common Lisp, které podporují multithreading, jsou dynamické obory specifické pro každé vlákno provádění. Speciální proměnné tedy slouží jako abstrakce pro lokální úložiště vláken. Pokud jedno vlákno rebinduje speciální proměnnou, toto rebinding nemá vliv na tuto proměnnou v jiných vláknech. Hodnotu uloženou ve vazbě lze načíst pouze pomocí vlákna, které tuto vazbu vytvořilo. Pokud každé vlákno váže nějakou speciální proměnnou *x*, *x*chová se jako lokální úložiště vláken. Mezi vlákny, která se nevracejí *x*, se chová jako obyčejný globál: všechna tato vlákna odkazují na stejnou vazbu na nejvyšší úrovni *x*.

Dynamické proměnné lze použít k rozšíření kontextu provádění o další kontextové informace, které jsou implicitně předávány z funkce do funkce, aniž by se musely zobrazovat jako parametr extra funkce. To je obzvláště užitečné, když musí přenos ovládacích prvků procházet vrstvami nesouvisejícího kódu, který jednoduše nelze rozšířit o další parametry pro předávání dalších dat. Taková situace obvykle vyžaduje globální proměnnou. Že globální proměnná musí být uložena a obnovena, aby se schéma při rekurzi nerozbilo: dynamické proměnné rebinding se o to postarají. A tato proměnná musí být vytvořena lokálně (nebo musí být použit velký mutex), aby se schéma nerozbilo pod vlákny: implementace dynamického rozsahu se o to také mohou postarat.

V knihovně Common Lisp existuje mnoho standardních speciálních proměnných. Například všechny standardní I/O toky jsou uloženy ve vazbách nejvyšší úrovně známých speciálních proměnných. Standardní výstupní proud je uložen v *standardním výstupu *.

Předpokládejme, že funkce foo zapisuje na standardní výstup:

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

Chcete-li zachytit jeho výstup v řetězci znaků, * standard-output * lze svázat s proudem řetězců a volat:

  (with-output-to-string (*standard-output*)
    (foo))
 -> "Hello, world" ; gathered output returned as a string

Lexikální

Common Lisp podporuje lexikální prostředí. Formálně mají vazby v lexikálním prostředí lexikální rozsah a mohou mít buď neurčitý rozsah, nebo dynamický rozsah, v závislosti na typu oboru názvů. Lexikální rozsah znamená, že viditelnost je fyzicky omezena na blok, ve kterém je navázána vazba. Reference, které nejsou v tomto bloku vloženy textově (tj. Lexikálně), tuto vazbu jednoduše nevidí.

Značky v TAGBODY mají lexikální rozsah. Výraz (GO X) je chybný, pokud není vložen do TAGBODY, který obsahuje štítek X. Vazby štítků však zmizí, když TAGBODY ukončí provádění, protože mají dynamický rozsah. Pokud je tento blok kódu znovu zadán vyvoláním lexikálního uzávěru , je neplatné, aby se tělo tohoto uzávěru pokusilo přenést řízení na značku prostřednictvím GO:

  (defvar *stashed*) ;; will hold a function

  (tagbody
    (setf *stashed* (lambda () (go some-label)))
    (go end-label) ;; skip the (print "Hello")
   some-label
    (print "Hello")
   end-label)
  -> NIL

Když je TAGBODY spuštěn, nejprve vyhodnotí formulář setf, který ukládá funkci do speciální proměnné *stashed *. Potom (go end-label) přenese kontrolu na end-label, přeskočí kód (vytiskněte „Hello“). Protože end-label je na konci tagbody, tagbody skončí a získá NIL. Předpokládejme, že dříve zapamatovaná funkce se nyní nazývá:

  (funcall *stashed*) ;; Error!

Tato situace je chybná. Jednou odpovědí implementace je chybový stav obsahující zprávu „GO: tagbody pro tag SOME-LABEL již byl ponechán“. Funkce se pokusila vyhodnotit (go some-label), která je lexikálně vložena do tagbody a vyřeší se na label. Tagbody se však nespouští (jeho rozsah skončil), a proto nemůže dojít k přenosu kontroly.

Vazby místních funkcí v Lispu mají lexikální rozsah a variabilní vazby mají ve výchozím nastavení také lexikální rozsah. Na rozdíl od štítků GO mají oba neurčitý rozsah. Když je navázána lexikální funkce nebo variabilní vazba, tato vazba nadále existuje tak dlouho, dokud jsou na ni možné odkazy, a to i poté, co byl ukončen konstrukt, který tuto vazbu stanovil. Odkazy na lexikální proměnné a funkce po ukončení jejich ustavujícího konstruktu jsou možné díky lexikálním uzávěrům .

Lexikální vazba je výchozí režim vazby pro proměnné Common Lisp. U individuálního symbolu lze přepnout na dynamický rozsah, a to buď lokální deklarací, globální deklarací. K posledně uvedenému může dojít implicitně použitím konstruktu jako DEFVAR nebo DEFPARAMETER. Je to důležitá konvence v Common Lisp programování, že zvláštní (tj dynamicky rozsahem) proměnné mají názvy, které začínají a končí hvězdičkou sigil * v tom, co se nazývá „ Earmuff konvence“. Pokud se tato konvence dodrží, efektivně vytvoří samostatný obor názvů pro speciální proměnné, takže proměnné určené jako lexikální nebudou náhodně ozvláštněny.

Lexikální rozsah je užitečný z několika důvodů.

Za prvé, odkazy na proměnné a funkce lze kompilovat do efektivního strojového kódu, protože struktura prostředí za běhu je relativně jednoduchá. V mnoha případech může být optimalizováno pro ukládání do zásobníku, takže otevírání a zavírání lexikálních rozsahů má minimální režii. I v případech, kdy musí být generovány úplné uzávěry, je přístup do prostředí uzavření stále účinný; typicky se každá proměnná stane ofsetem do vektoru vazeb, a tak se reference proměnné stane jednoduchou instrukcí načítání nebo ukládání v režimu adresování základna plus offset .

Za druhé, lexikální rozsah (v kombinaci s neurčitým rozsahem) vede k lexikálnímu uzavření , což zase vytváří celé paradigma programování soustředěné kolem používání funkcí, které jsou prvotřídními objekty, což je základem funkčního programování.

Za třetí, možná nejdůležitější, i když nejsou využívány lexikální uzávěry, použití lexikálního rozsahu izoluje moduly programu od nežádoucích interakcí. Kvůli omezené viditelnosti jsou lexikální proměnné soukromé. Pokud jeden modul A váže lexikální proměnnou X a volá jiný modul B, odkazy na X v B se náhodně nevyřeší na X vázaný v A. B jednoduše nemá přístup k X. V situacích, ve kterých jsou disciplinované interakce prostřednictvím proměnné žádoucí, Common Lisp poskytuje speciální proměnné. Speciální proměnné umožňují modulu A nastavit vazbu pro proměnnou X, která je viditelná pro jiný modul B, nazývaný od A. Umět to udělat je výhoda a možnost zabránit tomu, aby se to stalo, je také výhodou; v důsledku toho Common Lisp podporuje lexikální i dynamický rozsah .

Makra

Makro v Lispu povrchně se podobá funkci použití. Avšak místo toho, aby představoval vyhodnocovaný výraz, představuje transformaci zdrojového kódu programu. Makro získá zdroj, který obklopuje, jako argumenty, spojí je se svými parametry a vypočítá nový zdrojový formulář. Tento nový formulář může také používat makro. Rozbalení makra se opakuje, dokud nový zdrojový formulář nepoužívá makro. Konečný vypočítaný formulář je zdrojový kód spuštěný za běhu.

Typická použití maker v Lispu:

  • nové řídicí struktury (příklad: smyčkové konstrukce, rozvětvené konstrukce)
  • rozsahové a vazebné konstrukce
  • zjednodušená syntaxe pro složitý a opakovaný zdrojový kód
  • definující formuláře na nejvyšší úrovni s vedlejšími efekty při kompilaci
  • datově řízené programování
  • jazyky specifické pro vloženou doménu (příklady: SQL , HTML , Prolog )
  • implicitní finalizační formuláře

Různé standardní funkce Common Lisp je také nutné implementovat jako makra, například:

  • standardní setfabstrakce, umožňující vlastní rozšíření času kompilace operátorů přiřazení/přístupu
  • with-accessors, with-slots, with-open-fileA další podobné WITHmakra
  • V závislosti na implementaci, ifnebo condje makro postavené na druhém, speciální operátor; whena unlessskládají se z maker
  • Výkonný loopjazyk specifický pro doménu

Makra jsou definována makrem defmacro . Maketa speciálního operátoru umožňuje definici lokálních (lexikálně vymezených) maker. Je také možné definovat makra pro symboly pomocí define-symbol-macro a symbol-macrolet .

Kniha Paula Grahama On Lisp popisuje použití maker v Common Lisp podrobně. Doug Hoyte ‚s kniha Let Over Lambda rozšiřuje diskusi o makra, prohlašovat‚Makra jsou tou největší výhodou, že lisp má jako programovací jazyk a tou největší výhodou libovolném programovacím jazyce.‘ Hoyte poskytuje několik příkladů iterativního vývoje maker.

Příklad použití makra k definování nové řídicí struktury

Makra umožňují programátorům Lisp vytvářet nové syntaktické formy v jazyce. Jedním z typických použití je vytvoření nových řídicích struktur. Ukázkové makro poskytuje untilsmyčkovou konstrukci. Syntaxe je:

(until test form*)

Makro definice , dokud :

(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)))

tagbody je primitivní speciální operátor Common Lisp, který poskytuje možnost pojmenovávat tagy a pomocí formuláře go na ně přecházet . Backquote ` poskytuje notaci, která poskytuje šablony kódu, kde se vyplňuje hodnota formulářů před čárkou. Formuláře předcházející čárkou a zavináčem jsou spojeny . Formulář tagbody testuje koncovou podmínku. Pokud je podmínka pravdivá, přeskočí na koncovou značku. V opačném případě se provede zadaný kód těla a poté přeskočí na počáteční značku.

Příkladem použití výše do makro:

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

Kód lze rozšířit pomocí funkce macroexpand-1 . Rozšíření pro výše uvedený příklad vypadá takto:

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

Během makroexpanze hodnota proměnné testu je (= (náhodné 10), 0) a hodnota proměnné tělesa je ((write-line „Dobrý den,“)) . Tělo je seznam forem.

Symboly se obvykle automaticky upskatují. Rozšíření používá TAGBODY se dvěma štítky. Symboly pro tyto štítky jsou vypočítány společností GENSYM a nejsou vloženy do žádného balíčku. Dvě značky go použijte tyto značky k přeskočení. Protože tagbody je primitivní operátor v Common Lisp (a ne makro), nebude rozšířen na něco jiného. Rozbalený formulář používá makro when , které se také rozbalí. Plné rozbalení zdrojového formuláře se nazývá procházení kódu .

V plně rozbalené ( procházené ) formě, když je forma nahrazena primitivem, pokud :

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

Všechna makra musí být rozbalena, než lze zdrojový kód, který je obsahuje, vyhodnotit nebo zkompilovat normálně. Makra lze považovat za funkce, které přijímají a vracejí výrazy S- podobné abstraktním stromům syntaxe , ale neomezují se na ně. Tyto funkce jsou vyvolány před vyhodnocovačem nebo kompilátorem k vytvoření konečného zdrojového kódu. Makra jsou psána běžným jazykem Common Lisp a mohou používat libovolného dostupného operátora Common Lisp (nebo jiného poskytovatele).

Variabilní zachycení a stínování

Běžná makra Lisp jsou schopna toho, čemu se běžně říká variabilní sběr , kde se symboly v těle makra rozšiřují shodně se symboly v kontextu volání, což umožňuje programátorovi vytvářet makra, kde různé symboly mají zvláštní význam. Pojem zachycení proměnné je poněkud zavádějící, protože všechny obory názvů jsou náchylné k nežádoucímu zachycení, včetně oboru názvů operátorů a funkcí, jmenného prostoru štítků tagbody, tagu catch, obsluhy podmínek a jmenných prostorů restartu.

Proměnné snímání může způsobit softwarové vady. K tomu dochází jedním z následujících dvou způsobů:

  • Prvním způsobem může rozšíření makra nechtěně vytvořit symbolický odkaz, který zapisovatel makra předpokládá, že vyřeší v globálním oboru názvů, ale kód, kde je makro rozbaleno, poskytuje lokální definici stínování, která tento odkaz ukradne. Toto lze označit jako zachycení typu 1.
  • Druhý způsob, zachycení typu 2, je pravý opak: některé argumenty makra jsou části kódu dodané volajícím makra a tyto části kódu jsou napsány tak, aby odkazovaly na okolní vazby. Makro však vloží tyto části kódu do rozšíření, které definuje jeho vlastní vazby, které omylem zachytí některé z těchto odkazů.

Schéma dialektu Lispu poskytuje systém pro psaní maker, který poskytuje referenční transparentnost, která eliminuje oba typy problémů se zachycením. Tento typ makro systému se někdy nazývá „hygienický“, zejména svými zastánci (kteří považují makro systémy, které tento problém automaticky neřeší, za nehygienické).

V Common Lisp je makro hygiena zajištěna jedním ze dvou různých způsobů.

Jedním z přístupů je použití gensymů : zaručeně jedinečných symbolů, které lze použít při makroexpanzi bez hrozby zachycení. Použití gensymů v definici makra je ruční práce, ale lze psát makra, která zjednodušují vytváření instancí a používání gensymů. Gensymy řeší zachycení typu 2 snadno, ale nejsou použitelné na zachycení typu 1 stejným způsobem, protože rozšíření makra nemůže přejmenovat rušivé symboly v okolním kódu, které zachycují jeho odkazy. Gensymy by mohly být použity k zajištění stabilních aliasů pro globální symboly, které makro expanze potřebuje. Makro expanze by používala spíše tyto tajné aliasy než známá jména, takže předefinování známých jmen by nemělo na makro žádný špatný vliv.

Dalším přístupem je použití balíčků. Makro definované ve svém vlastním balíčku může při svém rozšíření jednoduše používat interní symboly v tomto balíčku. Použití balíčků se zabývá zachycením typu 1 a typu 2.

Balíčky však neřeší zachycení odkazů typu 1 na standardní funkce a operátory Common Lisp. Důvodem je to, že použití balíčků k řešení problémů se zachycením se točí kolem používání soukromých symbolů (symboly v jednom balíčku, do kterých se neimportují nebo jinak zviditelňují v jiných balíčcích). Zatímco symboly knihovny Common Lisp jsou externí a často se importují do nebo zviditelňují v uživatelem definovaných balíčcích.

Následuje příklad nechtěného zachycení v oboru názvů operátorů, ke kterému dochází při rozšíření makra:

 ;; expansion of UNTIL makes liberal use of DO
 (defmacro until (expression &body body)
   `(do () (,expression) ,@body))

 ;; macrolet establishes lexical operator binding for DO
 (macrolet ((do (...) ... something else ...))
   (until (= (random 10) 0) (write-line "Hello")))

untilMakro bude expandovat do formy, která požaduje do, který je určen pro označení standardní Common Lisp makro do. V tomto kontextu však domůže mít zcela jiný význam, takže untilnemusí fungovat správně.

Common Lisp řeší problém stínování standardních operátorů a funkcí tím, že zakazuje jejich předefinování. Protože předefinuje standardní operátor do, předchozí je ve skutečnosti fragmentem nevyhovujícího Common Lisp, který umožňuje implementacím diagnostikovat a odmítnout.

Systém podmínek

Systém podmínek je zodpovědný za zpracování výjimek v Common Lisp. Poskytuje podmínky , obslužné rutiny a restartuje . Podmínky s jsou objekty popisující výjimečnou situaci (například chybu). Pokud je signalizována podmínka , systém Common Lisp vyhledá obslužný program pro tento typ podmínky a zavolá obslužnou rutinu. Handler mohou nyní hledat restartů a použití jednoho z těchto restartů se automaticky opravit stávající problém s využitím informací, jako je typ podmínky a veškeré příslušné informace poskytnuté jako součást objektu stavu, a volat příslušnou funkci restart.

Tato restartování, pokud nejsou zpracována kódem, mohou být předložena uživatelům (jako součást uživatelského rozhraní, například debuggeru), takže uživatel může vybrat a vyvolat jeden z dostupných restartů. Vzhledem k tomu, že obsluha podmínek je volána v kontextu chyby (bez odvíjení zásobníku), je v mnoha případech možné úplné zotavení po chybě, kde by jiné systémy pro zpracování výjimek již ukončily aktuální rutinu. Samotný debugger lze také přizpůsobit nebo nahradit pomocí *debugger-hook*dynamické proměnné. Kód nalezený ve formulářích na ochranu před odvinutím, jako jsou finalizátory, bude také proveden podle potřeby navzdory výjimce.

V následujícím příkladu (pomocí Symbolics Genera ) se uživatel pokusí otevřít soubor v testu funkce Lisp volaného z Read-Eval-Print-LOOP ( REPL ), když soubor neexistuje. Systém Lisp představuje čtyři restarty. Uživatel vybere možnost Opakovat OPEN pomocí restartu jiné cesty a zadá jinou cestu (lispm-init.lisp místo lispm-int.lisp). Uživatelský kód neobsahuje žádný kód pro zpracování chyb. Celé zpracování chyb a restartovací kód zajišťuje systém Lisp, který dokáže zpracovat a opravit chybu bez ukončení uživatelského kódu.

Command: (test ">zippy>lispm-int.lisp")

Error: The file was not found.
       For lispm:>zippy>lispm-int.lisp.newest

LMFS:OPEN-LOCAL-LMFS-1
   Arg 0: #P"lispm:>zippy>lispm-int.lisp.newest"

s-A, <Resume>: Retry OPEN of lispm:>zippy>lispm-int.lisp.newest
s-B:           Retry OPEN using a different pathname
s-C, <Abort>:  Return to Lisp Top Level in a TELNET server
s-D:           Restart process TELNET terminal

-> Retry OPEN using a different pathname
Use what pathname instead [default lispm:>zippy>lispm-int.lisp.newest]:
   lispm:>zippy>lispm-init.lisp.newest

...the program continues

Common Lisp Object System (CLOS)

Common Lisp obsahuje sadu nástrojů pro objektově orientované programování , Common Lisp Object System nebo CLOS , což je jeden z nejvýkonnějších objektových systémů dostupných v jakémkoli jazyce. Peter Norvig například vysvětluje, kolik návrhových vzorů je jednodušší implementovat v dynamickém jazyce s funkcemi CLOS (vícenásobná dědičnost, mixiny, multimetody, metaklasy, kombinace metod atd.). Bylo navrženo několik rozšíření Common Lisp pro objektově orientované programování, které mají být zahrnuty do standardu ANSI Common Lisp, ale nakonec byl CLOS přijat jako standardní objektový systém pro Common Lisp. CLOS je dynamický objektový systém s vícenásobným odesláním a vícenásobnou dědičností a radikálně se liší od zařízení OOP nacházejících se ve statických jazycích, jako je C ++ nebo Java . CLOS jako dynamický objektový systém umožňuje za běhu změny obecných funkcí a tříd. Metody lze přidávat a odebírat, třídy lze přidávat a předefinovat, objekty lze aktualizovat pro změny tříd a třídu objektů lze měnit.

CLOS byl integrován do ANSI Common Lisp. Obecné funkce lze použít jako normální funkce a jedná se o prvotřídní datový typ. Každá třída CLOS je integrována do systému typu Common Lisp. Mnoho běžných typů Lisp má odpovídající třídu. Existuje více potenciálního využití CLOS pro Common Lisp. Specifikace neuvádí, zda jsou podmínky implementovány s CLOS. Pathnames and streams could be implemented with CLOS. Tyto další možnosti využití CLOS pro ANSI Common Lisp nejsou součástí standardu. Skutečné implementace Common Lisp používají CLOS pro názvy cest, streamy, vstup -výstup, podmínky, samotnou implementaci CLOS a další.

Překladač a tlumočník

Tlumočník Lisp přímo spouští zdrojový kód Lisp poskytovaný jako objekty Lisp (seznamy, symboly, čísla, ...) načtené z výrazů s. Kompilátor Lisp generuje bytecode nebo strojový kód ze zdrojového kódu Lisp. Common Lisp umožňuje kompilaci obou jednotlivých funkcí Lisp do paměti a kompilaci celých souborů do externě uloženého kompilovaného kódu ( soubory fasl ).

Několik implementací dřívějších dialektů Lisp poskytovalo tlumočníka i překladač. Bohužel sémantika byla často odlišná. Tyto dřívější Lisps implementovaly lexikální rozsahy v kompilátoru a dynamické rozsahy v tlumočníku. Common Lisp vyžaduje, aby tlumočník i překladač ve výchozím nastavení používali lexikální rozsah. Standard Common Lisp popisuje sémantiku tlumočníka i překladače. Kompilátor lze volat pomocí funkce compile pro jednotlivé funkce a pomocí funkce compile-file pro soubory. Common Lisp umožňuje deklarace typů a poskytuje způsoby, jak ovlivnit zásady generování kódu kompilátoru. U posledně jmenovaných mohou být dány různé kvality optimalizace mezi 0 (není důležité) a 3 (nejdůležitější): rychlost , prostor , bezpečnost , ladění a rychlost kompilace .

K dispozici je také funkce pro vyhodnocení Lisp kód: eval. evalbere kód jako předem analyzované s-výrazy a ne, jako v některých jiných jazycích, jako textové řetězce. Tímto způsobem lze kód sestavit pomocí obvyklých funkcí Lisp pro vytváření seznamů a symbolů a poté lze tento kód vyhodnotit pomocí funkce eval. Několik implementací Common Lisp (jako Clozure CL a SBCL) implementuje evalpomocí svého kompilátoru. Tímto způsobem je kód kompilován, přestože je vyhodnocen pomocí funkce eval.

Kompilátor souborů je vyvolán pomocí funkce compile-file . Vygenerovaný soubor s kompilovaným kódem se nazývá soubor fasl (z rychlého načtení ). Tyto soubory fasl a také soubory zdrojových kódů lze načíst s načtením funkce do spuštěného systému Common Lisp. V závislosti na implementaci generuje kompilátor souborů byte-code (například pro Java Virtual Machine ), kód jazyka C (který je pak kompilován pomocí kompilátoru C) nebo přímo nativní kód.

Běžné implementace Lisp lze použít interaktivně, přestože se kód plně zkompiloval. Myšlenka interpretovaného jazyka tedy neplatí pro interaktivní Common Lisp.

Jazyk rozlišuje mezi dobou čtení, časem kompilace, dobou načítání a dobou běhu a umožňuje uživatelskému kódu také toto rozlišení provést požadovaný typ zpracování v požadovaném kroku.

Někteří speciální operátoři jsou k dispozici zejména pro interaktivní vývoj; například defvarpřiřadí hodnotu své poskytnuté proměnné pouze tehdy, pokud již nebyla vázána, zatímco defparameterpřiřazení provede vždy. Toto rozlišení je užitečné při interaktivním vyhodnocování, kompilaci a načítání kódu v živém obrazu.

Některé funkce jsou také k dispozici při psaní překladačů a tlumočníků. Symboly se skládají z objektů první úrovně a lze s nimi přímo manipulovat pomocí uživatelského kódu. progvSpeciální operátor umožňuje vytvořit lexikální vazby programově, zatímco balíčky jsou také manipulovat. Kompilátor Lisp je k dispozici za běhu ke kompilaci souborů nebo jednotlivých funkcí. Díky tomu je snadné použít Lisp jako přechodný překladač nebo tlumočník pro jiný jazyk.

Příklady kódu

Narozeninový paradox

Následující program vypočítá nejmenší počet lidí v místnosti, u nichž je pravděpodobnost jedinečných narozenin menší než 50% ( narozeninový paradox , kdy u 1 osoby je pravděpodobnost zjevně 100%, u 2 364/365 atd.) ). Odpověď je 23.

Podle konvence jsou konstanty v Common Lisp uzavřeny + znaky.

(defconstant +year-size+ 365)

(defun birthday-paradox (probability number-of-people)
  (let ((new-probability (* (/ (- +year-size+ number-of-people)
                               +year-size+)
                            probability)))
    (if (< new-probability 0.5)
        (1+ number-of-people)
        (birthday-paradox new-probability (1+ number-of-people)))))

Volání ukázkové funkce pomocí REPL (Read Eval Print Loop):

CL-USER > (birthday-paradox 1.0 1)
23

Třídění seznamu objektů osob

Definujeme třídu persona způsob zobrazování jména a věku osoby. Dále definujeme skupinu osob jako seznam personobjektů. Poté iterujeme seřazený seznam.

(defclass person ()
  ((name :initarg :name :accessor person-name)
   (age  :initarg :age  :accessor person-age))
  (:documentation "The class PERSON with slots NAME and AGE."))

(defmethod display ((object person) stream)
  "Displaying a PERSON object to an output stream."
  (with-slots (name age) object
    (format stream "~a (~a)" name age)))

(defparameter *group*
  (list (make-instance 'person :name "Bob"   :age 33)
        (make-instance 'person :name "Chris" :age 16)
        (make-instance 'person :name "Ash"   :age 23))
  "A list of PERSON objects.")

(dolist (person (sort (copy-list *group*)
                      #'>
                      :key #'person-age))
  (display person *standard-output*)
  (terpri))

Vytiskne tři jména se sestupným věkem.

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

Umocňování kvadraturou

Použití makra LOOP je ukázáno:

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

Příklad použití:

CL-USER > (power 2 200)
1606938044258990275541962092341162602522202993782792835301376

Porovnat s vestavěným umocněním:

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

Najděte seznam dostupných skořápek

WITH-OPEN-FILE je makro, které otevírá soubor a poskytuje stream. Když se formulář vrací, soubor se automaticky zavře. FUNCALL volá funkční objekt. LOOP shromažďuje všechny řádky, které odpovídají predikátu.

(defun list-matching-lines (file predicate)
  "Returns a list of lines in file, for which the predicate applied to
 the line returns T."
  (with-open-file (stream file)
    (loop for line = (read-line stream nil nil)
          while line
          when (funcall predicate line)
          collect it)))

Funkce AVAILABLE-SHELLS volá nad funkcí LIST-MATCHING-LINES s názvem cesty a anonymní funkcí jako predikátem. Predikát vrací název cesty shellu nebo NIL (pokud řetězec není název souboru shellu).

(defun available-shells (&optional (file #p"/etc/shells"))
  (list-matching-lines
   file
   (lambda (line)
     (and (plusp (length line))
          (char= (char line 0) #\/)
          (pathname
           (string-right-trim '(#\space #\tab) line))))))

Příklad výsledků (v systému Mac OS X 10.6):

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

Srovnání s jinými Lisps

Common Lisp se nejčastěji porovnává se schématem a je s ním v kontrastu - pouze proto, že jsou dvěma nejpopulárnějšími dialekty Lisp. Schéma předchází CL a pochází nejen ze stejné tradice Lisp, ale také od některých stejných inženýrů - Guy L. Steele , s nímž Scheme navrhl Gerald Jay Sussman , předsedal normalizačnímu výboru pro Common Lisp.

Common Lisp je programovací jazyk pro všeobecné použití, na rozdíl od variant Lisp, jako jsou Emacs Lisp a AutoLISP, což jsou rozšiřující jazyky integrované do konkrétních produktů (GNU Emacs a AutoCAD). Na rozdíl od mnoha dřívějších Lisps používá Common Lisp (jako Scheme ) ve výchozím nastavení lexikální proměnný rozsah pro interpretovaný i kompilovaný kód.

Většina systémů Lisp, jejichž návrhy přispěly k Common Lisp - jako například ZetaLisp a Franz Lisp - používaly ve svých tlumočnicích dynamicky vymezené proměnné a ve svých překladačích lexikálně vymezené proměnné. Scheme zavedlo výhradní použití lexikálně upravených proměnných pro Lisp; inspirace z ALGOL 68 . CL podporuje také dynamicky vymezené proměnné, ale musí být výslovně deklarovány jako "speciální". Mezi překladači ANSI CL a překladači nejsou žádné rozdíly v rozsahu.

Běžný Lisp je někdy nazýván Lisp-2 a Scheme a Lisp-1 , odkazující na použití oddělených jmenných prostorů CL pro funkce a proměnné. (Ve skutečnosti má CL mnoho jmenných prostorů, například pro značky go, názvy bloků a loopklíčová slova). Mezi obhájci CL a Scheme existuje dlouhodobá kontroverze ohledně kompromisů zapojených do více jmenných prostorů. Ve Scheme je (obecně) nutné vyhnout se uvádění názvů proměnných, které jsou v rozporu s funkcemi; Funkce schématu mají často argumenty pojmenované lis, lstnebo lysttak , aby nebyly v rozporu se systémovou funkcí list. V CL je však nutné při předávání funkce jako argumentu výslovně odkazovat na obor názvů funkcí - což je také běžný výskyt, jako v sortpříkladu výše.

CL se od Scheme také liší zpracováním booleovských hodnot. Schéma používá speciální hodnoty #t a #f k vyjádření pravdy a nepravdy. CL dodržuje starší Lisp konvenci používání symbolů T a NIL, přičemž NIL znamená také prázdný seznam. V CL je jakákoli hodnota, která není NIL, považována za true pomocí podmíněných podmínek, jako například if, zatímco ve schématu jsou všechny hodnoty, které nejsou#f, považovány za pravdivé. Tyto konvence umožňují některým operátorům v obou jazycích sloužit jako predikáty (odpovědi na booleovskou otázku) i jako návrat užitečné hodnoty pro další výpočet, ale ve schématu hodnota '(), která je ekvivalentní NIL v Common Lisp, se hodnotí jako true v booleovském výrazu.

A konečně, dokumenty standardů schématu vyžadují optimalizaci koncového volání , což standard CL nevyžaduje. Většina implementací CL nabízí optimalizaci typu tail-call, i když často pouze tehdy, když programátor použije směrnici optimalizace. Nicméně, společné CL styl kódování nepodporuje všudypřítomné použití rekurze, že schéma styl preferuje, co by se programátor schéma vyjádřit koncová rekurze, uživatel CL se obvykle vyjadřují s iterační exprese v do, dolist, loop, nebo (v poslední době) s iteratebalík.

Implementace

Viz implementace kategorie Common Lisp .

Common Lisp je definován specifikací (jako Ada a C ) spíše než jednou implementací (jako Perl ). Existuje mnoho implementací a oblastí standardních podrobností, ve kterých se mohou platně lišit.

Implementace navíc obvykle přicházejí s rozšířeními, která poskytují funkce, které nejsou zahrnuty ve standardu:

  • Interaktivní nejvyšší úroveň (REPL)
  • Sběr odpadků
  • Debugger, stepper a inspektor
  • Slabé datové struktury (hashovací tabulky)
  • Rozšiřitelné sekvence
  • Rozšiřitelná Smyčka
  • Přístup k životnímu prostředí
  • Protokol Meta-Object CLOS
  • Rozšiřitelné streamy založené na CLOS
  • Systém CLOS založený na podmínkách
  • Síťové streamy
  • Trvalý CLOS
  • Podpora Unicode
  • Cizojazyčné rozhraní (často do C)
  • Rozhraní operačního systému
  • Rozhraní Java
  • Vlákna a vícenásobné zpracování
  • Doručování aplikací (aplikace, dynamické knihovny)
  • Ukládání obrázků

Pro podporu rozšíření Common Lisp přenosným způsobem byly vytvořeny bezplatné a open-source softwarové knihovny, které se nejvíce nacházejí v úložištích projektů Common-Lisp.net a CLOCC (Common Lisp Open Code Collection).

Běžné implementace Lisp mohou používat libovolnou kombinaci kompilace nativního kódu, kompilace nebo interpretace bajtového kódu. Common Lisp byl navržen tak, aby podporoval přírůstkové kompilátory , kompilátory souborů a kompilátory bloků. Standardní deklarace k optimalizaci kompilace (například vložení funkcí nebo typová specializace) jsou navrženy ve specifikaci jazyka. Většina běžných implementací Lisp kompiluje zdrojový kód do nativního strojového kódu . Některé implementace mohou vytvářet (optimalizované) samostatné aplikace. Jiní kompilují interpretovaný bytecode , který je méně účinný než nativní kód, ale usnadňuje přenos binárních kódů. Některé kompilátory kompilují kód Common Lisp do kódu C. Mylná představa, že Lisp je čistě interpretovaný jazyk, je nejpravděpodobnější, protože prostředí Lisp poskytuje interaktivní výzvu a tento kód je kompilován jeden po druhém, inkrementálně. S Common Lisp je široce používána přírůstková kompilace.

Jako skriptovací jazyk lze použít některé implementace na bázi Unixu ( CLISP , SBCL ) ; to znamená, že je systém vyvolán transparentně způsobem, jakým je překladač prostředí Perl nebo Unix .

Seznam implementací

Komerční implementace

Allegro Common Lisp
pro Microsoft Windows, FreeBSD, Linux, Apple macOS a různé varianty UNIX. Allegro CL poskytuje integrované vývojové prostředí (IDE) (pro Windows a Linux) a rozsáhlé možnosti pro doručování aplikací.
Liquid Common Lisp
dříve nazývaný Lucid Common Lisp . Pouze údržba, žádná nová vydání.
LispWorks
pro Microsoft Windows, FreeBSD, Linux, Apple macOS, iOS, Android a různé varianty UNIX. LispWorks poskytuje integrované vývojové prostředí (IDE) (dostupné pro většinu platforem, ale ne pro iOS a Android) a rozsáhlé možnosti pro doručování aplikací.
mocl
pro iOS, Android a macOS.
Otevřete Genera
pro DEC Alpha.
Scieneer Common Lisp
který je určen pro vysoce výkonné vědecké výpočty.

Volně redistribuovatelné implementace

Armed Bear Common Lisp (ABCL)
Implementace CL, která běží na virtuálním stroji Java . Obsahuje překladač bajtového kódu Java a umožňuje přístup k knihovnám Java z CL. To bylo dříve jen složkou ozbrojených Bear J editoru .
CLISP
Implementace kompilace bajtového kódu, přenosná a běží na několika unixových a unixových systémech (včetně macOS ), stejně jako na Microsoft Windows a několika dalších systémech.
Clozure CL (CCL)
Původně bezplatná a open-source vidlice Macintosh Common Lisp. Jak vyplývá z této historie, CCL byl napsán pro Macintosh, ale Clozure CL nyní běží na macOS , FreeBSD , Linux , Solaris a Windows . Na každé platformě jsou podporovány 32 a 64 bitové x86 porty. Kromě toho existují porty Power PC pro Mac OS a Linux. CCL byl dříve znám jako OpenMCL, ale tento název se již nepoužívá, aby nedošlo k záměně s open source verzí Macintosh Common Lisp.
CMUCL
Původně pochází z Carnegie Mellon University , nyní je skupinou dobrovolníků udržován jako bezplatný a open-source software . CMUCL používá rychlý překladač nativního kódu. Je k dispozici pro Linux a BSD pro Intel x86; Linux pro Alpha; macOS pro Intel x86 a PowerPC; a Solaris, IRIX a HP-UX na jejich nativních platformách.
Corman Common Lisp
pro Microsoft Windows. V lednu 2015 byl Corman Lisp publikován pod licencí MIT.
Embeddable Common Lisp (ECL)
ECL obsahuje překladač a překladač bajtových kódů. Může také kompilovat kód Lisp do strojového kódu prostřednictvím kompilátoru C. ECL poté zkomplikuje kód Lisp do C, zkomplikuje kód C pomocí kompilátoru C a poté může načíst výsledný strojový kód. Je také možné vložit ECL v C programy a C kód do programů Common Lisp.
GNU Common Lisp (GCL)
The GNU Lisp kompilátor projektu. GCL, který ještě není plně kompatibilní s ANSI, je však implementací volby pro několik velkých projektů, včetně matematických nástrojů Maxima , AXIOM a (historicky) ACL2 . GCL běží na Linuxu pod jedenácti různými architekturami a také pod Windows, Solarisem a FreeBSD .
Macintosh Common Lisp (MCL)
Verze 5.2 pro počítače Apple Macintosh s procesorem PowerPC se systémem Mac OS X je open source. RMCL (založený na MCL 5.2) běží na počítačích Apple Macintosh s procesorem Intel pomocí binárního překladače Rosetta od společnosti Apple.
ManKai Common Lisp (MKCL)
Pobočka ECL . MKCL klade důraz na spolehlivost, stabilitu a celkovou kvalitu kódu prostřednictvím silně přepracovaného, ​​nativně vícevláknového runtime systému. V Linuxu má MKCL plně runtime systém kompatibilní s POSIX.
Movitz
Implementuje prostředí Lisp pro počítače x86 bez spoléhání na jakýkoli podkladový operační systém.
Poplog
Poplog implementuje verzi CL s POP-11 a volitelně Prolog a Standard ML (SML), což umožňuje programování ve smíšeném jazyce. Pro všechny je implementačním jazykem POP-11, který je kompilován přírůstkově. Má také integrovaný editor podobný Emacsu, který komunikuje s překladačem.
Steel Bank Common Lisp (SBCL)
Pobočka od CMUCL . „Obecně řečeno, SBCL se odlišuje od CMU CL větším důrazem na udržovatelnost.“ SBCL běží na platformách CMUCL, kromě HP/UX; navíc běží na Linuxu pro AMD64, PowerPC, SPARC, MIPS, Windows x86 a má experimentální podporu pro běh na Windows AMD64. SBCL ve výchozím nastavení nepoužívá tlumočníka; všechny výrazy jsou kompilovány do nativního kódu, pokud uživatel nezapne tlumočníka. Kompilátor SBCL generuje rychlý nativní kód podle předchozí verze hry The Computer Language Benchmarks Game .
Ufasoft Common Lisp
port CLISP pro platformu Windows s jádrem napsaným v C ++.

Další implementace

Austin Kyoto Common Lisp
evoluce Kyoto Common Lisp od Billa Scheltera
Motýl obecný Lisp
implementace napsaná ve schématu pro víceprocesorový počítač BBN Butterfly
CLICC
překladač Common Lisp to C
CLOE
Společný Lisp pro PC od Symbolics
Codemist Common Lisp
používá se pro komerční verzi počítačového algebraického systému Axiom
ExperCommon Lisp
raná implementace pro Apple Macintosh od ExperTelligence
Golden Lisp
implementace pro PC od GoldHill Inc.
Ibuki Common Lisp
komercializovaná verze Kyoto Common Lisp
Kjóto Common Lisp
první kompilátor Common Lisp, který používal C jako cílový jazyk. GCL, ECL a MKCL pocházejí z této implementace Common Lisp.
L
malá verze Common Lisp pro vestavěné systémy vyvinutá společností IS Robotics, nyní iRobot
Lisp Machines (od Symbolics , TI a Xerox)
poskytoval implementace Common Lisp navíc k jejich rodnému dialektu Lisp (Lisp Machine Lisp nebo Interlisp). CLOS byl také k dispozici. Symbolics poskytuje vylepšenou verzi Common Lisp.
Procyon Common Lisp
implementace pro Windows a Mac OS, používaná Franzem pro jejich Windows port Allegro CL
Star Sapphire Common LISP
implementace pro PC
SubL
varianta Common Lisp používaná k implementaci systému založeného na znalostech Cyc
Common Lisp na nejvyšší úrovni
raná implementace pro souběžné provádění
WCL
implementace sdílené knihovny
VAX Common Lisp
Implementace Digital Equipment Corporation, která běžela na systémech VAX se systémem VMS nebo ULTRIX
XLISP
implementace napsaná Davidem Betzem

Aplikace

Common Lisp se používá k vývoji výzkumných aplikací (často v umělé inteligenci ), k rychlému vývoji prototypů nebo k nasazeným aplikacím.

Common Lisp se používá v mnoha komerčních aplikacích, včetně Yahoo! Webový obchod Store, který původně zahrnoval Paul Graham a později byl přepsán v C ++ a Perl . Mezi další pozoruhodné příklady patří:

Existují také open-source aplikace napsané v Common Lisp, jako například:

Viz také

Reference

Bibliografie

Chronologický seznam knih vydaných (nebo chystaných k vydání) o Common Lisp (jazyk) nebo o programování pomocí Common Lisp (zejména programování AI).

externí odkazy