Wspólne seplenienie - Common Lisp

Wspólne seplenienie
Paradygmat Wieloparadygmat : proceduralny , funkcjonalny , obiektowy , meta , refleksyjny , generyczny
Rodzina Seplenienie
Zaprojektowany przez Scott Fahlman , Richard P. Gabriel , David A. Moon , Kent Pitman , Guy Steele , Dan Weinreb
Deweloper Komitet ANSI X3J13
Po raz pierwszy pojawiły się 1984 (37 lat temu) , 1994 (27 lat temu) dla ANSI Common Lisp ( 1984 ) ( 1994 )
Dyscyplina pisania Dynamiczny , mocny
Zakres leksykalny, opcjonalnie dynamiczny
OS Wieloplatformowy
Rozszerzenia nazw plików .lisp, .lsp, .l, .cl, .fasl
Strona internetowa common-lisp .net
Główne wdrożenia
Allegro CL , ABCL , CLISP , Clozure CL , CMUCL , ECL , GCL , LispWorks , Scieneer CL , SBCL , Symbolics Common Lisp
Dialekty
CLtL1, CLtL2, ANSI Common Lisp
Wpływem
Lisp , Lisp Maszyna Lisp , Maclisp , Schemat , Interlisp
Pod wpływem
Clojure , Dylan , Emacs Lisp , EuLisp , ISLISP , * Lisp , AutoLisp , Julia , Moose , R , UMIEJĘTNOŚCI , subl

Common Lisp ( CL ) jest dialekt języka programowania Lisp , opublikowanym w ANSI standardowy dokument ANSI INCITS 226-1994 (S20018) (dawniej X3.226-1994 (R1999) ). Common Lisp HyperSpec , odsyłacza wersja HTML, zostały uzyskane ze standardem ANSI Common Lisp.

Język Common Lisp został opracowany jako ustandaryzowany i ulepszony następca Maclisp . Na początku lat osiemdziesiątych kilka grup pracowało już nad różnymi następcami MacLispa: Lisp Machine Lisp (znany również jako ZetaLisp), Spice Lisp , NIL i S-1 Lisp . Common Lisp dążył do ujednolicenia, standaryzacji i rozszerzenia funkcji tych dialektów MacLisp. Common Lisp nie jest implementacją, ale specyfikacją języka . Dostępnych jest kilka implementacji standardu Common Lisp, w tym oprogramowanie bezpłatne i open-source oraz produkty zastrzeżone. Common Lisp jest uniwersalnym, wieloparadygmatycznym językiem programowania . Obsługuje kombinację paradygmatów programowania proceduralnego , funkcjonalnego i obiektowego . Jako dynamiczny język programowania ułatwia ewolucyjne i przyrostowe tworzenie oprogramowania , z iteracyjną kompilacją w wydajne programy wykonawcze. Ten przyrostowy rozwój jest często wykonywany interaktywnie bez przerywania działającej aplikacji.

Obsługuje również opcjonalną adnotację typu i rzutowanie, które można dodać w razie potrzeby na późniejszych etapach profilowania i optymalizacji, aby umożliwić kompilatorowi generowanie bardziej wydajnego kodu. Na przykład fixnummoże przechowywać nieopakowaną liczbę całkowitą w zakresie obsługiwanym przez sprzęt i implementację, co pozwala na bardziej wydajną arytmetykę niż w przypadku dużych liczb całkowitych lub typów o dowolnej precyzji. Podobnie kompilator może określić na podstawie modułu lub funkcji, jaki rodzaj poziomu bezpieczeństwa jest pożądany, za pomocą deklaracji optymalizacji .

Common Lisp zawiera Clos , to układ obiektu , który obsługuje multimethods i kombinacji metod. Jest często implementowany za pomocą protokołu Metaobject Protocol.

Common Lisp jest rozszerzalny poprzez standardowe funkcje, takie jak makra Lisp (przekształcenia kodu) i makra czytnika (parsery wejściowe dla znaków).

Common Lisp zapewnia częściową kompatybilność wsteczną z maclisp i oryginalnego Johna McCarthy'ego Lisp . Pozwala to na przeniesienie starszego oprogramowania Lisp do Common Lisp.

Historia

Prace nad Common Lispem rozpoczęły się w 1981 roku po inicjatywie menedżera ARPA Boba Engelmore'a, która miała na celu opracowanie jednego standardowego dialektu lispu. Większość początkowego projektu języka została wykonana za pośrednictwem poczty elektronicznej. W 1982 r. Guy L. Steele Jr. przedstawił pierwszy przegląd Common Lisp na sympozjum ACM w 1982 r. na temat LISP i programowania funkcjonalnego.

Pierwsza dokumentacja języka została opublikowana w 1984 roku jako Common Lisp the Language (znany jako CLtL1), pierwsze wydanie. Druga edycja (znana jako CLtL2), opublikowana w 1990 roku, zawierała wiele zmian w języku, dokonanych podczas procesu standaryzacji ANSI Common Lisp: rozszerzoną składnię LOOP, Common Lisp Object System, Condition System do obsługi błędów, interfejs do ładna drukarka i wiele więcej. Ale CLtL2 nie opisuje ostatecznego standardu ANSI Common Lisp i dlatego nie jest dokumentacją ANSI Common Lisp. Ostateczny standard ANSI Common Lisp został opublikowany w 1994 roku. Od tego czasu nie opublikowano żadnej aktualizacji standardu. Różne rozszerzenia i ulepszenia Common Lisp (przykłady to Unicode, Concurrency, IO oparte na CLOS) zostały dostarczone przez implementacje i biblioteki.

Składnia

Common Lisp to dialekt Lisp. Używa wyrażeń S do oznaczenia zarówno kodu, jak i struktury danych. Wywołania funkcji, formularze makr i formularze specjalne są zapisywane jako listy, z nazwą operatora jako pierwszym, jak w poniższych przykładach:

 (+ 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 danych

Common Lisp ma wiele typów danych .

Typy skalarne

Numer typy to liczby całkowite , wskaźniki , liczby zmiennoprzecinkowe i liczby zespolone . Common Lisp używa bignum do reprezentowania wartości liczbowych o dowolnym rozmiarze i precyzji. Typ współczynnika reprezentuje dokładnie ułamki, funkcja niedostępna w wielu językach. Common Lisp automatycznie wymusza odpowiednie wartości liczbowe między tymi typami.

Typ znaku Common Lisp nie jest ograniczony do znaków ASCII . Większość nowoczesnych implementacji dopuszcza znaki Unicode .

Typ symbolu jest wspólny dla języków Lisp, ale w dużej mierze nieznany poza nimi. Symbol to unikalny, nazwany obiekt danych składający się z kilku części: nazwy, wartości, funkcji, listy właściwości i pakietu. Spośród nich najważniejsza jest komórka wartości i komórka funkcji . Symbole w Lisp są często używane podobnie do identyfikatorów w innych językach: do przechowywania wartości zmiennej; jednak istnieje wiele innych zastosowań. Normalnie, gdy symbol jest oceniany, zwracana jest jego wartość. Niektóre symbole oceniają się same, na przykład wszystkie symbole w pakiecie słów kluczowych są samoocenione. Wartości logiczne w Common Lisp są reprezentowane przez samooceny symbole T i NIL. Common Lisp posiada przestrzenie nazw dla symboli, zwane 'pakietami'.

Dostępnych jest wiele funkcji służących do zaokrąglania skalarnych wartości liczbowych na różne sposoby. Funkcja roundzaokrągla argument do najbliższej liczby całkowitej, z przypadkami połowicznymi zaokrąglonymi do parzystej liczby całkowitej. Funkcje truncate, floori ceilingzaokrąglają odpowiednio do zera, w dół lub w górę. Wszystkie te funkcje zwracają odrzuconą część ułamkową jako wartość drugorzędną. Na przykład (floor -2.5)wydajność -3, 0,5; (ceiling -2.5)wydajność -2, -0,5; (round 2.5)wydajność 2, 0,5; i (round 3.5)daje 4, -0,5.

Struktury danych

Typy sekwencji w Common Lisp obejmują listy, wektory, wektory bitowe i łańcuchy. Istnieje wiele operacji, które mogą działać na dowolnym typie sekwencji.

Jak w prawie wszystkich innych dialektach Lispu, listy w Common Lisp składają się z cons , czasami nazywanych cons cell lub pairs . Wady to struktura danych z dwoma gniazdami, zwana car i cdr . Lista jest połączonym łańcuchem wad lub pustą listą. Samochód każdego skazańca odnosi się do członka listy (prawdopodobnie innej listy). Cdr każdego wady odnosi się do następnego wady — z wyjątkiem ostatnich na liście, których cdr odnosi się do nilwartości. Conses można również łatwo wykorzystać do implementacji drzew i innych złożonych struktur danych; chociaż zwykle zaleca się użycie instancji struktur lub klas. Możliwe jest również tworzenie okrągłych struktur danych z konusami.

Common Lisp obsługuje wielowymiarowe tablice , a może dynamicznie zmieniać rozmiar regulowany tablic w razie potrzeby. Tablice wielowymiarowe mogą być używane do matematyki macierzowej. Wektor jest jednowymiarową tablicę. Tablice mogą przenosić dowolny typ jako elementy członkowskie (nawet typy mieszane w tej samej tablicy) lub mogą być wyspecjalizowane w zawieraniu określonego typu elementów członkowskich, na przykład w wektorze bitów. Zwykle obsługiwanych jest tylko kilka typów. Wiele implementacji może zoptymalizować funkcje tablicowe, gdy używana tablica jest wyspecjalizowana w typie. Standardem są dwa wyspecjalizowane typy tablic: łańcuch znaków jest wektorem znaków, a wektor bitowy jest wektorem bitów .

Tabele mieszające przechowują asocjacje między obiektami danych. Dowolny obiekt może być użyty jako klucz lub wartość. Tabele mieszania są automatycznie zmieniane w razie potrzeby.

Pakiety to zbiory symboli, używane głównie do rozdzielania części programu na przestrzenie nazw . Pakiet może eksportować niektóre symbole, oznaczając je jako część publicznego interfejsu. Pakiety mogą korzystać z innych pakietów.

Struktury , podobne w użyciu do struktur C i rekordów Pascala , reprezentują dowolne złożone struktury danych z dowolną liczbą i typem pól (zwanych slotami ). Struktury umożliwiają dziedziczenie pojedyncze.

Klasy są podobne do struktur, ale oferują bardziej dynamiczne funkcje i wielokrotne dziedziczenie. (Zobacz CLOS ). Klasy zostały dodane późno do Common Lisp i istnieje pewne koncepcyjne nakładanie się na struktury. Obiekty utworzone z klas nazywane są Instancjami . Szczególnym przypadkiem są funkcje ogólne. Funkcje ogólne to zarówno funkcje, jak i instancje.

Funkcje

Common Lisp obsługuje funkcje pierwszej klasy . Na przykład możliwe jest napisanie funkcji, które przyjmują inne funkcje jako argumenty lub również funkcje zwracające. Umożliwia to opisanie bardzo ogólnych operacji.

Biblioteka Common Lisp w dużym stopniu opiera się na takich funkcjach wyższego rzędu. Na przykład sortfunkcja przyjmuje operator relacyjny jako argument i funkcję klucza jako opcjonalny argument słowa kluczowego. Można to wykorzystać nie tylko do sortowania dowolnego typu danych, ale także do sortowania struktur danych według klucza.

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

Model oceny funkcji jest bardzo prosty. Kiedy oceniający napotyka formularz (f a1 a2...), zakłada, że ​​symbol o nazwie f jest jednym z następujących:

  1. Specjalny operator (łatwo sprawdzany na stałej liście)
  2. Operator makra (musi być wcześniej zdefiniowany)
  3. Nazwa funkcji (domyślna), która może być symbolem lub podformularzem rozpoczynającym się od symbolu lambda.

Jeśli f jest nazwą funkcji, to argumenty a1, a2, ..., an są oceniane w kolejności od lewej do prawej, a funkcja jest znajdowana i wywoływana z wartościami podanymi jako parametry.

Definiowanie funkcji

Makrodefun określa funkcje, gdzie definicja funkcji podaje nazwę funkcji, nazw żadnych argumentów i ciała funkcji:

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

Definicje funkcji mogą zawierać dyrektywy kompilatora , znane jako deklaracje , które dostarczają kompilatorowi wskazówek dotyczących ustawień optymalizacji lub typów danych argumentów. Mogą również zawierać ciągi dokumentacji (docstrings), które system Lisp może wykorzystać do dostarczenia interaktywnej dokumentacji:

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

Funkcje anonimowe ( literały funkcyjne ) definiuje się za pomocą lambdawyrażeń, np. (lambda (x) (* x x))dla funkcji podnoszącej swój argument do kwadratu. Styl programowania Lisp często używa funkcji wyższego rzędu, dla których przydatne jest dostarczanie funkcji anonimowych jako argumentów.

Funkcje lokalne można zdefiniować za pomocą fleti labels.

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

Istnieje kilka innych operatorów związanych z definicją i manipulacją funkcjami. Na przykład funkcja może być skompilowana z compileoperatorem. (Niektóre systemy Lisp domyślnie uruchamiają funkcje przy użyciu interpretera, chyba że zostanie to poinstruowane, aby skompilować; inne kompilują każdą funkcję).

Definiowanie ogólnych funkcji i metod

Makro defgenericdefiniuje funkcje ogólne . Funkcje ogólne to zbiór metod . Makro defmethoddefiniuje metody.

Metody mogą specjalizować swoje parametry przez CLOSA standardowych klas , klas systemowych , klas strukturalnych lub poszczególne obiekty. Dla wielu typów istnieją odpowiednie klasy systemowe .

Gdy wywoływana jest funkcja ogólna, wielokrotne wysyłanie określi skuteczną metodę do użycia.

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

Funkcje ogólne są również typem danych pierwszej klasy . Istnieje znacznie więcej funkcji ogólnych funkcji i metod niż opisano powyżej.

Przestrzeń nazw funkcji

Przestrzeń nazw dla nazw funkcji jest oddzielona od przestrzeni nazw dla zmiennych danych. Jest to kluczowa różnica między Common Lisp a Scheme . Dla Common Lisp, operatorów, które definiują nazwy w obszarze nazw funkcji należą defun, flet, labels, defmethodi defgeneric.

Aby przekazać funkcję według nazwy jako argument do innej funkcji, należy użyć functionoperatora specjalnego, powszechnie określanego skrótem #'. Pierwszy sortprzykład powyżej odnosi się do funkcji nazwanej symbolem >w przestrzeni nazw funkcji z kodem #'>. I odwrotnie, aby wywołać funkcję przekazaną w taki sposób, należałoby użyć funcalloperatora na argumencie.

Model oceny schematu jest prostszy: istnieje tylko jedna przestrzeń nazw, a wszystkie pozycje w formularzu są oceniane (w dowolnej kolejności) – nie tylko argumenty. Kod napisany w jednym dialekcie jest więc czasami mylący dla programistów bardziej doświadczonych w drugim. Na przykład, wielu programistów Common Lisp lubi używać opisowych nazw zmiennych, takich jak list lub string, które mogą powodować problemy w Scheme, ponieważ lokalnie ukrywają nazwy funkcji.

To, czy oddzielna przestrzeń nazw dla funkcji jest zaletą, jest źródłem kontrowersji w społeczności Lispów. Jest to zwykle określane jako debata Lisp-1 vs. Lisp-2 . Lisp-1 odnosi się do modelu Scheme, a Lisp-2 odnosi się do modelu Common Lisp. Nazwy te zostały ukute w artykule z 1988 roku autorstwa Richarda P. Gabriela i Kenta Pitmana , który obszernie porównuje te dwa podejścia.

Wiele zwracanych wartości

Common Lisp obsługuje koncepcję wielu wartości , gdzie każde wyrażenie zawsze ma jedną podstawową wartość , ale może również mieć dowolną liczbę wartości drugorzędnych , które mogą być odbierane i sprawdzane przez zainteresowane osoby wywołujące. Ta koncepcja różni się od zwracania wartości listy, ponieważ wartości drugorzędne są w pełni opcjonalne i przekazywane przez dedykowany kanał boczny. Oznacza to, że dzwoniący mogą pozostać całkowicie nieświadomi istnienia drugorzędnych wartości, jeśli ich nie potrzebują, i ułatwia korzystanie z mechanizmu przekazywania informacji, które czasami są przydatne, ale nie zawsze konieczne. Na przykład,

  • TRUNCATEFunkcja zaokrągla daną liczbę do całkowitej do zera. Jednak zwraca również resztę jako wartość drugorzędną, dzięki czemu bardzo łatwo jest określić, która wartość została obcięta. Obsługuje również opcjonalny parametr dzielnika, którego można użyć do trywialnego dzielenia euklidesowego :
(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"
  • GETHASHzwraca wartość klucza w mapie asocjacyjnej lub wartość domyślną w przeciwnym razie oraz drugorzędną wartość logiczną wskazującą, czy wartość została znaleziona. Tak więc kod, który nie dba o to, czy wartość została znaleziona lub podana jako domyślna, może po prostu użyć jej w takiej postaci, w jakiej jest, ale gdy takie rozróżnienie jest ważne, może sprawdzić drugorzędną wartość logiczną i odpowiednio zareagować. Oba przypadki użycia są obsługiwane przez to samo wywołanie i żadne z nich nie jest niepotrzebnie obciążone ani ograniczone przez drugie. Posiadanie tej funkcji na poziomie języka eliminuje potrzebę sprawdzania istnienia klucza lub porównywania go z wartością null, jak miałoby to miejsce w innych językach.
(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

Wiele wartości jest obsługiwanych przez kilka standardowych formularzy, z których najczęstsze to MULTIPLE-VALUE-BINDspecjalny formularz dostępu do wartości drugorzędnych i VALUESzwracania wielu wartości:

(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

Inne rodzaje

Inne typy danych w Common Lisp obejmują:

  • Nazwy ścieżek reprezentują pliki i katalogi w systemie plików . Funkcja nazw ścieżek Common Lisp jest bardziej ogólna niż konwencje nazewnictwa plików większości systemów operacyjnych, dzięki czemu dostęp programów Lisp do plików jest szeroko przenośny w różnych systemach.
  • Strumienie wejściowe i wyjściowe reprezentują źródła i ujścia danych binarnych lub tekstowych, takich jak terminal lub otwarte pliki.
  • Common Lisp ma wbudowany generator liczb pseudolosowych (PRNG). Obiekty stanu losowego reprezentują źródła liczb pseudolosowych wielokrotnego użytku, co pozwala użytkownikowi na zaszczepienie PRNG lub spowodowanie, że będzie on odtwarzał sekwencję.
  • Warunki to typ używany do reprezentowania błędów, wyjątków i innych „interesujących” zdarzeń, na które program może odpowiedzieć.
  • Klasyobiektami pierwszej klasy i same są instancjami klas zwanych klasami metaobiektów ( w skrócie metaklasy ).
  • Readtables to typ obiektu, który kontroluje sposób, w jaki czytnik Common Lisp analizuje tekst kodu źródłowego. Kontrolując, która tablica do odczytu jest używana podczas wczytywania kodu, programista może zmienić lub rozszerzyć składnię języka.

Zakres

Podobnie jak programy w wielu innych językach programowania, programy Common Lisp wykorzystują nazwy do odwoływania się do zmiennych, funkcji i wielu innych rodzajów jednostek. Odwołania nazwane podlegają zakresowi.

Powiązanie między nazwą a podmiotem, do którego odnosi się nazwa, nazywamy powiązaniem.

Zakres odnosi się do zbioru okoliczności, w których nazwa ma określone wiązanie.

Determinanty zakresu

Okoliczności, które określają zakres w Common Lisp obejmują:

  • lokalizacja odwołania w wyrażeniu. Jeśli jest to skrajna lewa pozycja związku, odnosi się do specjalnego operatora lub powiązania makra lub funkcji, w przeciwnym razie do powiązania zmiennej lub czegoś innego.
  • rodzaj wypowiedzi, w której ma miejsce odwołanie. Na przykład (go x)oznacza przeniesienie kontroli do etykiety x, podczas gdy (print x)odnosi się do zmiennej x. Oba zakresy xmogą być aktywne w tym samym regionie tekstu programu, ponieważ etykiety znaczników znajdują się w oddzielnej przestrzeni nazw niż nazwy zmiennych. Specjalna forma lub forma makra ma pełną kontrolę nad znaczeniem wszystkich symboli w swojej składni. Na przykład w (defclass x (a b) ())definicji klasy the (a b)jest listą klas bazowych, więc te nazwy są wyszukiwane w przestrzeni nazw klas i xnie są odwołaniem do istniejącego powiązania, ale nazwą nowej klasy, która jest pochodna z ai b. Te fakty wyłaniają się wyłącznie z semantyki defclass. Jedynym ogólnym faktem dotyczącym tego wyrażenia jest to, że defclassodnosi się do powiązania makra; wszystko inne zależy od defclass.
  • lokalizacja odniesienia w tekście programu. Na przykład, jeśli odwołanie do zmiennej xjest zawarte w konstrukcji powiązania, takiej jak a, letktóra definiuje powiązanie dla x, odwołanie znajduje się w zakresie utworzonym przez to powiązanie.
  • w przypadku odwołania do zmiennej, niezależnie od tego, czy symbol zmiennej został, lokalnie czy globalnie, zadeklarowany jako specjalny. Określa to, czy odwołanie jest rozwiązywane w środowisku leksykalnym, czy w środowisku dynamicznym.
  • konkretna instancja środowiska, w którym odwołanie jest rozwiązane. Środowisko to słownik czasu wykonywania, który odwzorowuje symbole na powiązania. Każdy rodzaj odniesienia wykorzystuje swój własny rodzaj środowiska. Odniesienia do zmiennych leksykalnych są rozwiązywane w środowisku leksykalnym i tak dalej. Z tym samym odniesieniem można powiązać więcej niż jedno środowisko. Na przykład, dzięki rekurencji lub wykorzystaniu wielu wątków, w tym samym czasie może istnieć wiele aktywacji tej samej funkcji. Te aktywacje mają ten sam tekst programu, ale każda ma własną instancję środowiska leksykalnego.

Aby zrozumieć, do czego odnosi się symbol, programista Common Lisp musi wiedzieć, jakiego rodzaju odniesienie jest wyrażane, jakiego rodzaju zakresu używa, jeśli jest to odwołanie do zmiennej (zakres dynamiczny kontra leksykalny), a także sytuację w czasie wykonywania: w jakie środowisko jest rozwiązane odniesienie, gdzie zostało wprowadzone wiązanie do środowiska i tak dalej.

Rodzaje środowiska

Światowy

Niektóre środowiska w Lisp są wszechobecne na całym świecie. Na przykład, jeśli zostanie zdefiniowany nowy typ, będzie on później znany wszędzie. Odniesienia do tego typu sprawdzają to w tym globalnym środowisku.

Dynamiczny

Jednym z typów środowiska w Common Lisp jest środowisko dynamiczne. Powiązania ustanowione w tym środowisku mają zasięg dynamiczny, co oznacza, że ​​powiązanie jest ustanawiane na początku wykonywania jakiegoś konstruktu, takiego jak letblok, i znika po zakończeniu wykonywania tego konstruktu: jego czas życia jest powiązany z dynamiczną aktywacją i dezaktywacją blok. Jednak powiązanie dynamiczne jest widoczne nie tylko w tym bloku; jest również widoczny dla wszystkich funkcji wywoływanych z tego bloku. Ten rodzaj widoczności jest znany jako zakres nieokreślony. Wiązania, które wykazują zakres dynamiczny (czas życia związany z aktywacją i dezaktywacją bloku) i zakres nieokreślony (widoczny dla wszystkich funkcji, które są wywoływane z tego bloku) mają zasięg dynamiczny.

Common Lisp obsługuje zmienne o dynamicznym zasięgu, które są również nazywane zmiennymi specjalnymi. Niektóre inne rodzaje powiązań są również z konieczności objęte zakresem dynamicznym, takie jak restarty i tagi catch. Powiązania funkcji nie mogą być objęte zakresem dynamicznym using flet(co zapewnia tylko powiązania funkcji z zakresem leksykalnym), ale obiekty funkcji (obiekt pierwszego poziomu w Common Lisp) można przypisać do zmiennych o zakresie dynamicznym, powiązać przy użyciu letw zakresie dynamicznym, a następnie wywołać using funcalllub APPLY.

Zakres dynamiczny jest niezwykle przydatny, ponieważ dodaje referencyjną przejrzystość i dyscyplinę do zmiennych globalnych . Zmienne globalne są źle postrzegane w informatyce jako potencjalne źródła błędów, ponieważ mogą powodować powstawanie ad hoc ukrytych kanałów komunikacji między modułami, które prowadzą do niechcianych, zaskakujących interakcji.

W Common Lisp specjalna zmienna, która ma tylko powiązanie najwyższego poziomu, zachowuje się jak zmienna globalna w innych językach programowania. Można w nim zapisać nową wartość, która po prostu zastępuje to, co znajduje się w powiązaniu najwyższego poziomu. Nieostrożne zastępowanie wartości zmiennej globalnej leży u podstaw błędów spowodowanych użyciem zmiennych globalnych. Jednak innym sposobem pracy ze zmienną specjalną jest nadanie jej nowego, lokalnego powiązania w wyrażeniu. Jest to czasami określane jako „ponowne wiązanie” zmiennej. Powiązanie zmiennej o zakresie dynamicznym tymczasowo tworzy nową lokalizację pamięci dla tej zmiennej i kojarzy nazwę z tą lokalizacją. Gdy to powiązanie obowiązuje, wszystkie odwołania do tej zmiennej odwołują się do nowego powiązania; poprzednie wiązanie jest ukryte. Po zakończeniu wykonywania wyrażenia wiążącego, tymczasowa lokalizacja pamięci znika, a stare wiązanie zostaje ujawnione z nienaruszoną oryginalną wartością. Oczywiście można zagnieździć wiele powiązań dynamicznych dla tej samej zmiennej.

W implementacjach Common Lisp, które obsługują wielowątkowość, zakresy dynamiczne są specyficzne dla każdego wątku wykonania. W ten sposób zmienne specjalne służą jako abstrakcja dla lokalnego przechowywania wątków. Jeśli jeden wątek ponownie wiąże specjalną zmienną, to ponowne powiązanie nie ma wpływu na tę zmienną w innych wątkach. Wartość przechowywana w powiązaniu może zostać pobrana tylko przez wątek, który utworzył to powiązanie. Jeśli każdy wątek wiąże jakąś specjalną zmienną *x*, *x*zachowuje się jak pamięć lokalna wątku. Wśród wątków, które nie zmieniają wiązania *x*, zachowuje się jak zwykły global: wszystkie te wątki odnoszą się do tego samego wiązania najwyższego poziomu *x*.

Zmiennych dynamicznych można użyć do rozszerzenia kontekstu wykonania o dodatkowe informacje kontekstowe, które są niejawnie przekazywane z funkcji do funkcji bez konieczności pojawiania się jako dodatkowy parametr funkcji. Jest to szczególnie przydatne, gdy transfer kontroli musi przejść przez warstwy niepowiązanego kodu, którego po prostu nie można rozszerzyć o dodatkowe parametry, aby przekazać dodatkowe dane. Taka sytuacja zwykle wymaga zmiennej globalnej. Ta zmienna globalna musi zostać zapisana i przywrócona, aby schemat nie załamał się w wyniku rekursji: zajmuje się tym dynamiczne ponowne wiązanie zmiennych. I ta zmienna musi być lokalna dla wątku (lub musi być użyty duży muteks), aby schemat nie załamał się pod wątkami: implementacje zakresu dynamicznego również mogą się tym zająć.

W bibliotece Common Lisp znajduje się wiele standardowych zmiennych specjalnych. Na przykład wszystkie standardowe strumienie we/wy są przechowywane w powiązaniach najwyższego poziomu dobrze znanych zmiennych specjalnych. Standardowy strumień wyjściowy jest przechowywany w *standard-output*.

Załóżmy, że funkcja foo zapisuje na standardowe wyjście:

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

Aby uchwycić jego wyjście w ciągu znaków, *standard-output* można powiązać ze strumieniem ciągu i wywołać:

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

Leksykalny

Common Lisp obsługuje środowiska leksykalne. Formalnie powiązania w środowisku leksykalnym mają zakres leksykalny i mogą mieć nieokreślony lub dynamiczny zakres, w zależności od typu przestrzeni nazw. Zakres leksykalny oznacza, że ​​widoczność jest fizycznie ograniczona do bloku, w którym ustanowiono wiązanie. Odwołania, które nie są tekstowo (tj. leksykalnie) osadzone w tym bloku, po prostu nie widzą tego wiązania.

Tagi w TAGBODY mają zakres leksykalny. Wyrażenie (GO X) jest błędne, jeśli nie jest osadzone w TAGBODY, który zawiera etykietę X. Jednak powiązania etykiet znikają, gdy TAGBODY kończy wykonywanie, ponieważ mają dynamiczny zasięg. Jeśli ten blok kodu zostanie ponownie wprowadzony przez wywołanie domknięcia leksykalnego , próba przeniesienia kontroli do tagu przez GO jest nieprawidłowa dla treści tego zamknięcia:

  (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

Kiedy TAGBODY jest wykonywany, najpierw ocenia formularz setf, który przechowuje funkcję w specjalnej zmiennej *stashed*. Następnie (go end-label) przekazuje kontrolę do etykiety końcowej, pomijając kod (drukuj "Hello"). Ponieważ etykieta końcowa znajduje się na końcu ciała znacznika, treść znacznika kończy się, dając NIL. Załóżmy, że poprzednio zapamiętana funkcja jest teraz wywoływana:

  (funcall *stashed*) ;; Error!

Ta sytuacja jest błędna. Odpowiedzią jednej z implementacji jest warunek błędu zawierający komunikat „GO: tagbody dla tagu SOME-LABEL został już pozostawiony”. Funkcja próbowała ocenić (go some-label), która jest leksykalnie osadzona w treści znacznika i rozwiązuje do etykiety. Jednak tagbody nie jest wykonywany (jego zakres się skończył), więc przeniesienie kontroli nie może nastąpić.

Powiązania funkcji lokalnych w Lispie mają zasięg leksykalny , a powiązania zmiennych mają domyślnie zasięg leksykalny. W przeciwieństwie do etykiet GO, oba mają nieokreślony zasięg. Po ustanowieniu funkcji leksykalnej lub wiązania zmiennej, to wiązanie trwa tak długo, jak możliwe są odniesienia do niego, nawet po zakończeniu konstruktu, który ustanowił to wiązanie. Odwołania do zmiennych i funkcji leksykalnych po zakończeniu ich konstrukcji ustalającej są możliwe dzięki domknięciom leksykalnym .

Wiązanie leksykalne jest domyślnym trybem wiązania dla zmiennych Common Lisp. W przypadku pojedynczego symbolu można go przełączyć na zakres dynamiczny, za pomocą deklaracji lokalnej lub deklaracji globalnej. To ostatnie może wystąpić niejawnie poprzez użycie konstrukcji takiej jak DEFVAR lub DEFPARAMETER. Ważną konwencją w programowaniu Common Lisp jest to, że specjalne (tj. dynamicznie o zakresie) zmienne mają nazwy, które zaczynają się i kończą sigilem z gwiazdką *w tak zwanej " konwencji nauszników ". Jeśli jest przestrzegana, ta konwencja skutecznie tworzy osobną przestrzeń nazw dla zmiennych specjalnych, dzięki czemu zmienne, które mają być leksykalne, nie są przypadkowo traktowane jako specjalne.

Zakres leksykalny jest przydatny z kilku powodów.

Po pierwsze, odwołania do zmiennych i funkcji można skompilować do wydajnego kodu maszynowego, ponieważ struktura środowiska wykonawczego jest stosunkowo prosta. W wielu przypadkach można go zoptymalizować do przechowywania w stosie, więc otwieranie i zamykanie zakresów leksykalnych ma minimalne obciążenie. Nawet w przypadkach, gdy konieczne jest wygenerowanie pełnych zamknięć, dostęp do środowiska zamknięcia jest nadal efektywny; zazwyczaj każda zmienna staje się przesunięciem do wektora wiązań, a więc odwołanie do zmiennej staje się prostą instrukcją ładowania lub przechowywania z trybem adresowania base-plus-offset .

Po drugie, zakres leksykalny (w połączeniu z zakresem nieokreślonym) daje początek domknięciu leksykalnemu , co z kolei tworzy cały paradygmat programowania skoncentrowany na wykorzystaniu funkcji będących obiektami pierwszej klasy, co jest podstawą programowania funkcyjnego.

Po trzecie, być może najważniejsze, nawet jeśli nie wykorzystuje się domknięć leksykalnych, użycie zakresu leksykalnego izoluje moduły programu od niepożądanych interakcji. Ze względu na ograniczoną widoczność zmienne leksykalne są prywatne. Jeśli jeden moduł A wiąże zmienną leksykalną X i wywołuje inny moduł B, odwołania do X w B nie zostaną przypadkowo rozwiązane do związanego X w A. B po prostu nie ma dostępu do X. pożądane, Common Lisp zapewnia specjalne zmienne. Zmienne specjalne pozwalają modułowi A ustawić powiązanie dla zmiennej X, która jest widoczna dla innego modułu B, wywoływanego z A. Możliwość wykonania tego jest zaletą, a możliwość zapobiegania temu jest również zaletą; w konsekwencji Common Lisp obsługuje zarówno zakres leksykalny, jak i dynamiczny .

Makra

Makro w Lisp powierzchownie przypomina funkcję w eksploatacji. Jednak zamiast reprezentować wyrażenie, które jest oceniane, reprezentuje transformację kodu źródłowego programu. Makro pobiera źródło, które otacza jako argumenty, wiąże je ze swoimi parametrami i oblicza nowy formularz źródłowy. Ten nowy formularz może również używać makra. Rozwijanie makra jest powtarzane, dopóki nowy formularz źródłowy nie będzie używał makra. Ostateczną wyliczoną formą jest kod źródłowy wykonywany w czasie wykonywania.

Typowe zastosowania makr w Lisp:

  • nowe struktury kontrolne (przykład: konstrukcje zapętlające, konstrukcje rozgałęziające)
  • scoping i wiążące konstrukcje
  • uproszczona składnia dla złożonego i powtarzalnego kodu źródłowego
  • definiowanie formularzy najwyższego poziomu z efektami ubocznymi w czasie kompilacji
  • programowanie oparte na danych
  • wbudowane języki specyficzne dla domeny (przykłady: SQL , HTML , Prolog )
  • niejawne formularze finalizacji

Różne standardowe funkcje Common Lisp muszą być również zaimplementowane jako makra, takie jak:

  • standardowa setfabstrakcja, aby umożliwić niestandardowe rozszerzenia w czasie kompilacji operatorów przypisania/dostępu
  • with-accessors, with-slots, with-open-fileI inne podobne WITHmakra
  • W zależności od implementacji, ifczyli condmakra zbudowanego na drugiej, operatora specjalnego; wheni unlessskładają się z makr
  • Potężny loopjęzyk specyficzny dla domeny

Makra są definiowane przez makro defmacro . Makrolet operatora specjalnego umożliwia definicję makr lokalnych (o zasięgu leksykalnym). Możliwe jest również zdefiniowanie makr dla symboli za pomocą funkcji define-symbol-macro i symbol-macrolet .

Książka Paula Grahama On Lisp opisuje szczegółowo użycie makr w Common Lisp. Książka Douga Hoyte'a Let Over Lambda rozszerza dyskusję na temat makr, twierdząc, że „makra są największą zaletą lisp jako języka programowania i największą zaletą każdego języka programowania”. Hoyte podaje kilka przykładów iteracyjnego tworzenia makr.

Przykład użycia makra do zdefiniowania nowej struktury sterowania

Makra pozwalają programistom Lisp na tworzenie nowych form składniowych w języku. Jednym z typowych zastosowań jest tworzenie nowych struktur kontrolnych. Przykładowe makro udostępnia untilkonstrukcję pętli. Składnia to:

(until test form*)

Definicja makra do :

(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 to prymitywny specjalny operator Common Lisp, który zapewnia możliwość nazywania tagów i używania formularza go do przeskakiwania do tych tagów. Backquote ` zawiera zapis, który dostarcza szablony kodu, gdzie wartość form poprzedzone przecinkiem są wypełnione. Formy poprzedzone przecinkiem i po-znaku są łączone w. Postać tagbody testuje stan końcowy. Jeśli warunek jest spełniony, przeskakuje do znacznika końcowego. W przeciwnym razie dostarczony kod treści jest wykonywany, a następnie przeskakuje do tagu początkowego.

Przykład użycia powyższego do makra:

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

Kod można rozszerzyć za pomocą funkcji macroexpand-1 . Rozszerzenie powyższego przykładu wygląda tak:

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

Podczas rozwijania makra wartość zmiennej test to (= (losowe 10) 0), a wartość ciała zmiennej to ((write-line "Hello")) . Ciało to lista formularzy.

Symbole są zwykle automatycznie zamieniane. Rozszerzenie wykorzystuje TAGBODY z dwiema etykietami. Symbole tych etykiet są obliczane przez GENSYM i nie są umieszczone w żadnym opakowaniu. Dwa formularze go używają tych tagów do przeskoku. Ponieważ tagbody jest pierwotnym operatorem w Common Lisp (a nie makrem), nie zostanie rozwinięty w coś innego. Formę wykorzystuje rozszerzone przy makro, które również zostaną rozszerzone. Pełne rozszerzenie formularza źródłowego nazywamy chodzeniem po kodzie .

W całkowicie rozwiniętej ( chodzącej ) formie, gdy forma jest zastępowana przez prymityw if :

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

Wszystkie makra muszą zostać rozwinięte, zanim kod źródłowy, który je zawiera, będzie mógł zostać normalnie oceniony lub skompilowany. Makra można uznać za funkcje, które akceptują i zwracają wyrażenia S – podobne do abstrakcyjnych drzew składni , ale nie ograniczają się do nich. Te funkcje są wywoływane przed oceniającym lub kompilatorem w celu utworzenia ostatecznego kodu źródłowego. Makra są napisane w normalnym Common Lisp i mogą używać dowolnego dostępnego operatora Common Lisp (lub innego operatora).

Zmienne przechwytywanie i cieniowanie

Wspólne makra Lisp są zdolne do tego, co powszechnie nazywa się przechwytywaniem zmiennych , gdzie symbole w ciele rozszerzenia makra pokrywają się z tymi w kontekście wywoływania, umożliwiając programiście tworzenie makr, w których różne symbole mają specjalne znaczenie. Termin przechwytywanie zmiennych jest nieco mylący, ponieważ wszystkie przestrzenie nazw są podatne na niechciane przechwytywanie, w tym przestrzeń nazw operatora i funkcji, przestrzeń nazw etykiety tagbody, tag catch, procedura obsługi warunków i przestrzenie nazw restartu.

Przechwytywanie zmiennych może powodować defekty oprogramowania. Dzieje się to na jeden z dwóch następujących sposobów:

  • Po pierwsze, rozwinięcie makra może nieumyślnie utworzyć symboliczne odniesienie, które, jak zakładał autor makr, zostanie rozwiązane w globalnej przestrzeni nazw, ale kod, w którym rozwijane jest makro, dostarcza lokalną, zacieniającą definicję, która kradnie to odwołanie. Niech będzie to określane jako bicie typu 1.
  • Drugi sposób, przechwytywanie typu 2, jest wręcz odwrotny: niektóre argumenty makra to fragmenty kodu dostarczone przez obiekt wywołujący makra, a te fragmenty kodu są napisane w taki sposób, że odwołują się do otaczających powiązań. Jednak makro wstawia te fragmenty kodu do rozszerzenia, które definiuje własne powiązania, które przypadkowo przechwytują niektóre z tych odwołań.

Dialekt Scheme of Lisp zapewnia system pisania makr, który zapewnia przezroczystość referencyjną, która eliminuje oba typy problemów z przechwytywaniem. Ten rodzaj makrosystemu jest czasami nazywany „higienicznym”, w szczególności przez jego zwolenników (którzy uważają makrosystemy, które nie rozwiązują automatycznie tego problemu za niehigieniczne).

W Common Lisp higiena makro jest zapewniona na dwa różne sposoby.

Jednym z podejść jest użycie gensymów : gwarantowanych unikalnych symboli, które mogą być użyte w makro-rozszerzeniu bez groźby przechwycenia. Użycie gensymów w definicji makra to ręczna robota, ale można napisać makra, które upraszczają tworzenie i używanie gensymów. Gensyms łatwo rozwiązuje przechwytywanie typu 2, ale nie można ich zastosować do przechwytywania typu 1 w ten sam sposób, ponieważ rozwinięcie makra nie może zmienić nazwy zakłócających symboli w otaczającym kodzie, który przechwytuje jego odniesienia. Gensyms można wykorzystać do zapewnienia stabilnych aliasów dla symboli globalnych, których potrzebuje makrorozszerzenie. Rozwijanie makr używałoby tych tajnych aliasów zamiast dobrze znanych nazw, więc redefinicja dobrze znanych nazw nie miałaby złego wpływu na makro.

Innym podejściem jest użycie pakietów. Makro zdefiniowane we własnym pakiecie może po prostu używać wewnętrznych symboli w tym pakiecie w swoim rozwinięciu. Wykorzystanie pakietów dotyczy przechwytywania typu 1 i typu 2.

Jednak pakiety nie rozwiązują przechwytywania typu 1 odwołań do standardowych funkcji i operatorów Common Lisp. Powodem jest to, że użycie pakietów do rozwiązywania problemów z przechwytywaniem kręci się wokół używania symboli prywatnych (symbole w jednym pakiecie, które nie są importowane lub w inny sposób widoczne w innych pakietach). Podczas gdy symbole biblioteki Common Lisp są zewnętrzne i często importowane lub uwidaczniane w pakietach zdefiniowanych przez użytkownika.

Poniżej znajduje się przykład niechcianego przechwytywania w przestrzeni nazw operatora, występującego podczas rozwijania 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 będzie rozwijać się w formie rozmowy do, które ma odnosić się do standardowego Common Lisp makra do. Jednak w tym kontekście domoże mieć zupełnie inne znaczenie, więc untilmoże nie działać poprawnie.

Common Lisp rozwiązuje problem cieniowania standardowych operatorów i funkcji, zabraniając ich redefinicji. Ponieważ redefiniuje on standardowy operator do, poprzedni jest w rzeczywistości fragmentem niezgodnego Common Lisp, który pozwala implementacjom go zdiagnozować i odrzucić.

System warunków

System warunków jest odpowiedzialny za obsługę wyjątków w Common Lisp. Zapewnia warunki , obsługę i restart . Warunki to obiekty opisujące wyjątkową sytuację (na przykład błąd). Jeśli zostanie zasygnalizowany warunek , system Common Lisp wyszukuje procedurę obsługi dla tego typu warunku i wywołuje procedurę. Program obsługi może teraz wyszukiwać restarty i użyć jednego z tych restartów, aby automatycznie naprawić bieżący problem, używając informacji takich jak typ warunku i wszelkie istotne informacje dostarczone jako część obiektu warunku, a także wywołać odpowiednią funkcję restartu.

Te restarty, jeśli nie są obsługiwane przez kod, mogą być prezentowane użytkownikom (jako część interfejsu użytkownika, na przykład debuggera), tak aby użytkownik mógł wybrać i wywołać jeden z dostępnych restartów. Ponieważ procedura obsługi warunków jest wywoływana w kontekście błędu (bez rozwijania stosu), w wielu przypadkach możliwe jest pełne odzyskanie po błędzie, gdy inne systemy obsługi wyjątków już zakończyłyby bieżącą procedurę. Sam debugger można również dostosować lub zastąpić za pomocą *debugger-hook*zmiennej dynamicznej. Kod znaleziony w formularzach unwind-protect, takich jak finalizatory, będzie również wykonywany zgodnie z potrzebami, pomimo wyjątku.

W poniższym przykładzie (używając Symbolics Genera ) użytkownik próbuje otworzyć plik w teście funkcji Lisp wywołanym z Read-Eval-Print-LOOP ( REPL ), gdy plik nie istnieje. System Lisp przedstawia cztery restarty. Użytkownik wybiera Retry OPEN przy użyciu innej ścieżki restartu i wprowadza inną ścieżkę (lispm-init.lisp zamiast lispm-int.lisp). Kod użytkownika nie zawiera żadnego kodu obsługi błędów. Całą obsługę błędów i kod restartu zapewnia system Lisp, który może obsłużyć i naprawić błąd bez przerywania kodu użytkownika.

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 zawiera zestaw narzędzi do programowania obiektowego , Common Lisp Object System lub CLOS , który jest jednym z najpotężniejszych systemów obiektowych dostępnych w dowolnym języku. Na przykład Peter Norvig wyjaśnia, jak wiele wzorców projektowych jest prostszych do zaimplementowania w dynamicznym języku dzięki funkcjom CLOS (wielokrotne dziedziczenie, domieszki, wielometody, metaklasy, kombinacje metod itp.). Zaproponowano włączenie kilku rozszerzeń Common Lisp do programowania obiektowego do standardu ANSI Common Lisp, ale ostatecznie CLOS został przyjęty jako standardowy system obiektowy dla Common Lisp. CLOS jest systemem obiektów dynamicznych z wielokrotnym wysyłaniem i wielokrotnym dziedziczeniem , i różni się radykalnie od udogodnień OOP, które można znaleźć w językach statycznych, takich jak C++ lub Java . Jako dynamiczny system obiektów, CLOS umożliwia wprowadzanie w czasie wykonywania zmian ogólnych funkcji i klas. Metody mogą być dodawane i usuwane, klasy mogą być dodawane i redefiniowane, obiekty mogą być aktualizowane w celu zmiany klasy, a klasa obiektów może być zmieniana.

CLOS został zintegrowany z ANSI Common Lisp. Funkcje ogólne mogą być używane jak normalne funkcje i są typem danych pierwszej klasy. Każda klasa CLOS jest zintegrowana z systemem typu Common Lisp. Wiele typów Common Lisp ma odpowiednią klasę. Istnieje więcej potencjalnych zastosowań CLOS dla Common Lisp. Specyfikacja nie mówi, czy warunki są zaimplementowane za pomocą CLOS. Ścieżki i strumienie można zaimplementować za pomocą CLOS. Te dalsze możliwości wykorzystania CLOS dla ANSI Common Lisp nie są częścią standardu. Rzeczywiste implementacje Common Lisp używają CLOS do nazw ścieżek, strumieni, wejścia-wyjścia, warunków, implementacji samego CLOS i nie tylko.

Kompilator i tłumacz

Interpreter Lisp bezpośrednio wykonuje kod źródłowy Lisp dostarczony jako obiekty Lisp (listy, symbole, liczby, ...) odczytane z s-wyrażeń. Kompilator Lisp generuje kod bajtowy lub kod maszynowy z kodu źródłowego Lisp. Common Lisp umożliwia kompilację zarówno pojedynczych funkcji Lisp w pamięci, jak i kompilację całych plików do zewnętrznie przechowywanego skompilowanego kodu ( pliki fasl ).

Kilka implementacji wcześniejszych dialektów Lisp dostarczało zarówno interpretera, jak i kompilatora. Niestety często semantyka była inna. Te wcześniejsze Lispy zaimplementowały zakres leksykalny w kompilatorze i dynamiczny zakres w interpreterze. Common Lisp wymaga, aby zarówno interpreter, jak i kompilator domyślnie używały zakresu leksykalnego. Standard Common Lisp opisuje zarówno semantykę interpretera, jak i kompilatora. Kompilator można wywołać przy użyciu funkcji kompilacji dla poszczególnych funkcji i przy użyciu funkcji kompilacji-pliku dla plików. Common Lisp umożliwia deklaracje typów i zapewnia sposoby wpływania na zasady generowania kodu kompilatora. W przypadku tych ostatnich różne cechy optymalizacji mogą mieć wartości od 0 (nieważne) do 3 (najważniejsze): speed , space , safety , debug i compile-speed .

Istnieje również funkcja do oceny kodu Lisp: eval. evalprzyjmuje kod jako wstępnie przetworzone wyrażenia s, a nie, jak w niektórych innych językach, jako ciągi tekstowe. W ten sposób kod może być skonstruowany za pomocą zwykłych funkcji Lisp do konstruowania list i symboli, a następnie ten kod może być oceniany za pomocą funkcji eval. Kilka implementacji Common Lisp (takich jak Clozure CL i SBCL) jest implementowanych evalza pomocą ich kompilatora. W ten sposób kod jest kompilowany, nawet jeśli jest oceniany za pomocą funkcji eval.

Kompilator plików jest wywoływany za pomocą funkcji compile-file . Wygenerowany plik ze skompilowanym kodem nazywa się plikiem fasl (od fast load ). Te pliki fasl , a także pliki kodu źródłowego mogą być ładowane z funkcją ładowania do działającego systemu Common Lisp. W zależności od implementacji kompilator plików generuje kod bajtowy (na przykład dla wirtualnej maszyny Javy ), kod języka C (który następnie jest kompilowany za pomocą kompilatora C) lub bezpośrednio kod natywny.

Implementacje Common Lisp mogą być używane interaktywnie, nawet jeśli kod zostanie w pełni skompilowany. Idea języka interpretowanego nie ma zatem zastosowania do interaktywnego Common Lisp.

Język rozróżnia czas odczytu, czas kompilacji, czas ładowania i czas wykonywania, a także umożliwia kodowi użytkownika dokonanie tego rozróżnienia w celu wykonania żądanego typu przetwarzania w żądanym kroku.

Niektóre operatory specjalne są przeznaczone specjalnie do programowania interaktywnego; na przykład, defvarprzypisze wartość do dostarczonej zmiennej tylko wtedy, gdy nie była już powiązana, podczas gdy defparameterzawsze wykona przypisanie. To rozróżnienie jest przydatne podczas interaktywnej oceny, kompilowania i ładowania kodu w obrazie na żywo.

Niektóre funkcje są również dostępne, aby pomóc w pisaniu kompilatorów i interpreterów. Symbole składają się z obiektów pierwszego poziomu i można nimi bezpośrednio manipulować za pomocą kodu użytkownika. progvSpecjalny operator umożliwia tworzenie powiązań leksykalne programowo, a pakiety są również manipulowany. Kompilator Lisp jest dostępny w czasie wykonywania do kompilowania plików lub poszczególnych funkcji. Ułatwiają one używanie Lispa jako pośredniego kompilatora lub interpretera dla innego języka.

Przykłady kodu

Urodzinowy paradoks

Poniższy program oblicza najmniejszą liczbę osób w pokoju, dla których prawdopodobieństwo unikalnych urodzin jest mniejsze niż 50% ( paradoks urodzin , gdzie dla 1 osoby prawdopodobieństwo oczywiście wynosi 100%, dla 2 to 364/365 itd. ). Odpowiedź to 23.

Zgodnie z konwencją, stałe w Common Lisp są otoczone znakami +.

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

Wywołanie przykładowej funkcji za pomocą REPL (Read Eval Print Loop):

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

Sortowanie listy obiektów osób

Definiujemy klasę personi sposób wyświetlania imienia i wieku osoby. Następnie definiujemy grupę osób jako listę personobiektów. Następnie iterujemy po posortowanej liście.

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

Wypisuje trzy imiona z malejącym wiekiem.

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

Potęgowanie przez podniesienie do kwadratu

Zademonstrowano użycie makra LOOP:

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

Przykładowe zastosowanie:

CL-USER > (power 2 200)
1606938044258990275541962092341162602522202993782792835301376

Porównaj z wbudowaną potęgą:

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

Znajdź listę dostępnych pocisków

WITH-OPEN-FILE to makro, które otwiera plik i zapewnia strumień. Po powrocie formularza plik jest automatycznie zamykany. FUNCALL wywołuje obiekt funkcji. LOOP zbiera wszystkie wiersze, które pasują do predykatu.

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

Funkcja AVAILABLE-SHELLS wywołuje powyższą funkcję LIST-MATCHING-LINES z nazwą ścieżki i funkcją anonimową jako predykatem. Predykat zwraca ścieżkę dostępu do powłoki lub NIL (jeśli łańcuch nie jest nazwą pliku powłoki).

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

Przykładowe wyniki (w systemie 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")

Porównanie z innymi ustami

Common Lisp jest najczęściej porównywany i przeciwstawiany Scheme — choćby dlatego, że są to dwa najpopularniejsze dialekty Lispu. Scheme poprzedza CL i wywodzi się nie tylko z tej samej tradycji Lispu, ale także od kilku tych samych inżynierów — Guya L. Steele'a , z którym Gerald Jay Sussman zaprojektował Scheme, przewodniczył komitetowi standaryzacyjnemu Common Lisp.

Common Lisp jest językiem programowania ogólnego przeznaczenia, w przeciwieństwie do wariantów Lisp, takich jak Emacs Lisp i AutoLISP, które są językami rozszerzeń wbudowanymi w określone produkty (odpowiednio GNU Emacs i AutoCAD). W przeciwieństwie do wielu wcześniejszych Lispów, Common Lisp (podobnie jak Scheme ) domyślnie używa zakresu zmiennych leksykalnych zarówno dla kodu interpretowanego, jak i kompilowanego.

Większość systemów Lisp, których projekty przyczyniły się do Common Lisp, takich jak ZetaLisp i Franz Lisp używane dynamicznie scoped zmienne w swoich ustnych i leksykalnie zakrojony zmiennych w swoich kompilatorów. Scheme wprowadził do Lispa jedyne użycie zmiennych o zasięgu leksykalnym; inspiracja z ALGOL 68 . CL obsługuje również zmienne o zakresie dynamicznym, ale muszą być jawnie zadeklarowane jako „specjalne”. Nie ma różnic w zakresie między interpreterami i kompilatorami ANSI CL.

Common Lisp jest czasami określany jako Lisp-2, a Scheme jako Lisp-1 , odnosząc się do używania przez CL oddzielnych przestrzeni nazw dla funkcji i zmiennych. (W rzeczywistości CL ma wiele przestrzeni nazw, takich jak te dla znaczników go, nazw bloków i loopsłów kluczowych). Istnieje od dawna kontrowersje między zwolennikami CL i Scheme dotyczące kompromisów związanych z wieloma przestrzeniami nazw. W programie Scheme konieczne jest (ogólnie) unikanie nadawania zmiennym nazw, które kolidują z funkcjami; Funkcje schematu często mają argumenty o nazwie lis, lstlub , lystaby nie kolidować z funkcją systemową list. Jednak w języku CL konieczne jest jawne odwoływanie się do przestrzeni nazw funkcji podczas przekazywania funkcji jako argumentu — co również jest częstym zjawiskiem, jak w sortpowyższym przykładzie.

CL różni się również od Scheme pod względem obsługi wartości logicznych. Scheme używa specjalnych wartości #t i #f do reprezentowania prawdy i fałszu. CL podąża za starszą konwencją Lisp, używając symboli T i NIL, przy czym NIL oznacza również pustą listę. W CL każda wartość różna od NIL jest traktowana jako prawdziwa przez warunki, takie jak if, podczas gdy w schemacie wszystkie wartości inne niż #f są traktowane jako prawdziwe. Te konwencje pozwalają niektórym operatorom w obu językach służyć zarówno jako predykaty (odpowiadające na pytanie o wartości logicznej), jak i jako zwracające użyteczną wartość do dalszych obliczeń, ale w schemacie wartość '(), która jest równoważna NIL w Common Lisp, daje wartość true w wyrażeniu logicznym.

Wreszcie, dokumenty standardów Scheme wymagają optymalizacji tail-call , czego standard CL nie wymaga. Większość implementacji CL oferuje optymalizację typu tail-call, chociaż często tylko wtedy, gdy programista używa dyrektywy optymalizacji. Niemniej jednak, powszechne CL styl kodowania nie sprzyja wszechobecnego użycia rekursji że styl Schemat woli, co programista Scheme by wyrazić ogonowej rekursji, użytkownik CL zazwyczaj wyrazić iteracyjny ekspresji w do, dolist, loop, lub (ostatnio) z iteratepakiet.

Realizacje

Zobacz implementacje kategorii Common Lisp .

Common Lisp jest zdefiniowany przez specyfikację (jak Ada i C ), a nie przez jedną implementację (jak Perl ). Istnieje wiele implementacji i standardowych obszarów szczegółów, w których mogą się one istotnie różnić.

Ponadto implementacje zwykle zawierają rozszerzenia, które zapewniają funkcjonalność nieobjętą standardem:

  • Interaktywny najwyższy poziom (REPL)
  • Zbieranie śmieci
  • Debuger, Stepper i Inspektor
  • Słabe struktury danych (tabele haszujące)
  • Rozszerzalne sekwencje
  • Rozszerzalna PĘTLA
  • Dostęp do środowiska
  • Protokół metaobiektu CLOS
  • Rozszerzalne strumienie oparte na CLOS
  • System warunków oparty na CLOS
  • Strumienie sieciowe
  • Trwałe CLOS
  • Obsługa Unicode
  • Interfejs w języku obcym (często do C)
  • Interfejs systemu operacyjnego
  • Interfejs Java
  • Wątki i wieloprzetwarzanie
  • Dostarczanie aplikacji (aplikacje, biblioteki dynamiczne)
  • Zapisywanie obrazów

Biblioteki oprogramowania darmowego i open-source zostały stworzone, aby obsługiwać rozszerzenia Common Lisp w sposób przenośny i można je znaleźć przede wszystkim w repozytoriach projektów Common-Lisp.net i CLOCC (Common Lisp Open Code Collection).

Wspólne implementacje Lisp mogą używać dowolnej kombinacji kompilacji kodu natywnego, kompilacji kodu bajtowego lub interpretacji. Common Lisp został zaprojektowany do obsługi kompilatorów przyrostowych, kompilatorów plików i kompilatorów blokowych. W specyfikacji języka proponuje się standardowe deklaracje optymalizacji kompilacji (takie jak inlinerowanie funkcji lub specjalizacja typów). Większość popularnych implementacji Lisp kompiluje kod źródłowy do natywnego kodu maszynowego . Niektóre implementacje mogą tworzyć (zoptymalizowane) samodzielne aplikacje. Inne kompilują do interpretowanego kodu bajtowego , który jest mniej wydajny niż kod natywny, ale ułatwia przenoszenie kodu binarnego. Niektóre kompilatory kompilują kod Common Lisp do kodu C. Błędne przekonanie, że Lisp jest językiem czysto interpretowanym, jest najprawdopodobniej spowodowane tym, że środowiska Lisp zapewniają interaktywną podpowiedź, a kod jest kompilowany jeden po drugim, w sposób przyrostowy. Z Common Lisp kompilacja przyrostowa jest szeroko stosowana.

Niektóre implementacje oparte na systemie Unix ( CLISP , SBCL ) mogą być używane jako język skryptowy ; to znaczy, wywoływane przez system w sposób przezroczysty, tak jak interpreter powłoki Perla lub Uniksa .

Lista wdrożeń

Realizacje komercyjne

Allegro Wspólne Lisp
dla Microsoft Windows, FreeBSD, Linux, Apple macOS i różnych wariantów UNIX. Allegro CL zapewnia zintegrowane środowisko programistyczne (IDE) (dla systemów Windows i Linux) oraz szerokie możliwości dostarczania aplikacji.
Lisp w płynie
dawniej nazywany Lucid Common Lisp . Tylko konserwacja, bez nowych wydań.
LispWorks
dla Microsoft Windows, FreeBSD, Linux, Apple macOS, iOS, Android i różnych wariantów UNIX. LispWorks zapewnia zintegrowane środowisko programistyczne (IDE) (dostępne dla większości platform, ale nie dla iOS i Android) oraz szerokie możliwości dostarczania aplikacji.
mocl
dla iOS, Androida i macOS.
Otwórz generację
dla DEC Alpha.
Naukowiec Common Lisp
który jest przeznaczony do obliczeń naukowych o wysokiej wydajności.

Swobodnie redystrybuowalne wdrożenia

Uzbrojony niedźwiedź Common Lisp (ABCL)
Implementacja CL działająca na wirtualnej maszynie języka Java . Zawiera kompilator kodu bajtowego Java i umożliwia dostęp do bibliotek Java z CL. Dawniej był to tylko składnik Armed Bear J Editor .
CLISP
Implementacja kompilująca kod bajtowy, przenośna i działająca na kilku systemach Unix i podobnych do Unix (w tym macOS ), a także Microsoft Windows i kilku innych systemach.
Zamknięcie CL (CCL)
Pierwotnie darmowy i open-source fork Macintosh Common Lisp. Jak sugeruje historia, CCL został napisany dla Macintosha, ale Clozure CL działa teraz na macOS , FreeBSD , Linux , Solaris i Windows . Na każdej platformie obsługiwane są 32 i 64-bitowe porty x86 . Dodatkowo są porty Power PC dla Mac OS i Linux. CCL był wcześniej znany jako OpenMCL, ale ta nazwa nie jest już używana, aby uniknąć pomyłek z wersją open source Macintosh Common Lisp.
CMUCL
Pochodzący z Carnegie Mellon University , obecnie utrzymywany jako darmowe i otwarte oprogramowanie przez grupę wolontariuszy. CMUCL używa szybkiego kompilatora kodu natywnego. Jest dostępny w systemach Linux i BSD dla Intel x86; Linux dla Alfy; macOS dla Intel x86 i PowerPC; oraz Solaris, IRIX i HP-UX na swoich rodzimych platformach.
Corman Common Lisp
dla Microsoft Windows. W styczniu 2015 Corman Lisp został opublikowany na licencji MIT.
Wbudowany Common Lisp (ECL)
ECL zawiera interpreter i kompilator kodu bajtowego. Może również skompilować kod Lisp do kodu maszynowego za pomocą kompilatora C. ECL następnie kompiluje kod Lisp do C, kompiluje kod C za pomocą kompilatora C, a następnie może załadować powstały kod maszynowy. Możliwe jest również, aby zamieścić w ECL C programów i kodu C do programów Common Lisp.
GNU Common Lisp (GCL)
The GNU kompilator Lisp projektu. GCL nie jest jeszcze w pełni zgodny z ANSI, jest jednak implementacją z wyboru dla kilku dużych projektów, w tym narzędzi matematycznych Maxima , AXIOM i (historycznie) ACL2 . GCL działa na Linuksie w jedenastu różnych architekturach, a także pod Windows, Solaris i FreeBSD .
Common Lisp Macintosha (MCL)
Wersja 5.2 dla komputerów Apple Macintosh z procesorem PowerPC z systemem Mac OS X jest oprogramowaniem typu open source. RMCL (oparty na MCL 5.2) działa na komputerach Apple Macintosh z procesorami Intela przy użyciu translatora binarnego Rosetta firmy Apple.
ManKai Common Lisp (MKCL)
Oddział ECL . MKCL kładzie nacisk na niezawodność, stabilność i ogólną jakość kodu poprzez mocno przerobiony, natywnie wielowątkowy system wykonawczy. W systemie Linux MKCL zawiera system wykonawczy w pełni zgodny z POSIX.
Movitz
Implementuje środowisko Lisp dla komputerów x86 bez polegania na jakimkolwiek podstawowym systemie operacyjnym.
Poplog
Poplog implementuje wersję CL, z POP-11 i opcjonalnie Prolog i Standard ML (SML), umożliwiając programowanie w różnych językach. Dla wszystkich językiem implementacji jest POP-11, który jest kompilowany przyrostowo. Posiada również zintegrowany edytor podobny do Emacsa , który komunikuje się z kompilatorem.
Wspólny Lisp Steel Bank (SBCL)
Oddział z CMUCL . „Ogólnie rzecz biorąc, SBCL różni się od CMU CL większym naciskiem na łatwość utrzymania”. SBCL działa na platformach CMUCL, z wyjątkiem HP/UX; ponadto działa na Linuksie dla AMD64, PowerPC, SPARC, MIPS, Windows x86 i ma eksperymentalne wsparcie dla Windows AMD64. SBCL domyślnie nie używa interpretera; wszystkie wyrażenia są kompilowane do kodu natywnego, chyba że użytkownik włączy interpreter. Kompilator SBCL generuje szybki kod natywny zgodnie z poprzednią wersją gry The Computer Language Benchmarks Game .
Ufasoft Common Lisp
port CLISP dla platformy Windows z rdzeniem napisanym w C++.

Inne realizacje

Austin Kioto Wspólne Lisp
ewolucja Kyoto Common Lisp autorstwa Billa Scheltera
Motyl Wspólne Lisp
implementacja napisana w Scheme dla komputera wieloprocesorowego BBN Butterfly
CLICK
kompilator Common Lisp to C
CLOE
Common Lisp dla komputerów PC od Symbolics
Kodemista Common Lisp
używany w komercyjnej wersji systemu algebry komputerowej Axiom
ExperCommon Lisp
wczesna implementacja dla Apple Macintosh przez ExperTelligence
Złoty zwyczajny sepleń
implementacja na PC firmy GoldHill Inc.
Ibuki Wspólne Lisp
skomercjalizowana wersja Kyoto Common Lisp
Kioto wspólne Lisp
pierwszy kompilator Common Lisp, który używał C jako języka docelowego. GCL, ECL i MKCL pochodzą z tej implementacji Common Lisp.
L
mała wersja Common Lisp dla systemów wbudowanych opracowana przez IS Robotics, obecnie iRobot
Maszyny Lisp (od Symbolics , TI i Xerox)
dostarczyli implementacje Common Lisp oprócz ich natywnego dialektu Lisp (Lisp Machine Lisp lub Interlisp). CLOS był również dostępny. Symbolics zapewnia ulepszoną wersję Common Lisp.
Procyon Wspólne Lisp
implementacja dla Windows i Mac OS, używana przez Franza dla ich portu Windows Allegro CL
Gwiazda Szafir Zwykły LISP
implementacja na PC
SubL
wariant Common Lisp używany do implementacji systemu opartego na wiedzy Cyc
Wspólny seplenienie najwyższego poziomu
wczesna implementacja do równoczesnego wykonywania
WCL
implementacja biblioteki współdzielonej
VAX wspólne seplenienie
Implementacja Digital Equipment Corporation działająca na systemach VAX z systemem VMS lub ULTRIX
XLISP
implementacja napisana przez Davida Betz

Aplikacje

Common Lisp jest używany do tworzenia aplikacji badawczych (często w Sztucznej Inteligencji ), do szybkiego tworzenia prototypów lub do wdrożonych aplikacji.

Common Lisp jest używany w wielu aplikacjach komercyjnych, w tym w Yahoo! Witryna sklepu internetowego sklepu, w której pierwotnie uczestniczył Paul Graham, a później została przepisana w językach C++ i Perl . Inne godne uwagi przykłady to:

  • ACT-R , architektura kognitywna wykorzystywana w wielu projektach badawczych.
  • Authorizer's Assistant, duży, oparty na regułach system używany przez American Express, analizujący wnioski kredytowe.
  • Cyc , długotrwały projekt stworzenia systemu opartego na wiedzy, który zapewnia ogromną ilość zdroworozsądkowej wiedzy.
  • Gensym G2 , system ekspercki czasu rzeczywistego i silnik reguł biznesowych
  • Genworks GDL, oparty na jądrze Gendl o otwartym kodzie źródłowym.
  • Środowisko deweloperskie dla serii gier wideo Jak and Daxter , opracowane przez firmę Naughty Dog .
  • Wyszukiwarka tanich taryf ITA Software , używana przez strony podróżnicze, takie jak Orbitz i Kayak.com oraz linie lotnicze, takie jak American Airlines , Continental Airlines i US Airways .
  • Mirai , pakiet grafiki 3D. Został użyty do animowania twarzy Golluma w filmie Władca Pierścieni: Dwie Wieże .
  • Opusmodus to system komponowania muzyki oparty na Common Lisp, używanym w komponowaniu wspomaganym komputerowo .
  • Prototype Verification System (PVS), zmechanizowane środowisko do formalnej specyfikacji i weryfikacji.
  • PWGL to wyrafinowane środowisko programowania wizualnego oparte na Common Lisp, używane w komponowaniu wspomaganym komputerowo i syntezie dźwięku.
  • Piano, kompletny zestaw do analizy samolotów, napisany w Common Lisp, używany przez takie firmy jak Boeing , Airbus i Northrop Grumman .
  • Grammarly , anglojęzyczna platforma do ulepszania pisania, ma swój podstawowy silnik gramatyczny napisany w Common Lisp.
  • Narzędzie Dynamic Analysis and Replanning Tool (DART), o którym mówi się, że jako jedyne zwróciło się w latach 1991-1995 za wszystkie trzydzieści lat inwestycji DARPA w badania nad sztuczną inteligencją.
  • NASA 's Jet Propulsion Lab ' s " Deep Space 1 ", wielokrotnie nagradzanym Common Lisp Program dla autopiloting z Deep Space One kosmicznym.
  • SigLab, platforma Common Lisp do przetwarzania sygnałów wykorzystywana w obronie przeciwrakietowej, zbudowana przez firmę Raytheon .
  • System planowania misji Mars Pathfinder NASA .
  • SPIKE, system harmonogramowania dla ziemskich lub kosmicznych obserwatoriów i satelitów, w szczególności Hubble Space Telescope, napisany w Common Lisp.
  • Common Lisp został wykorzystany do prototypowania modułu odśmiecania środowiska Microsoft .NET Common Language Runtime .
  • Oryginalna wersja Reddita , choć programiści przerzucili się później na Pythona z powodu braku bibliotek Common Lisp, zgodnie z oficjalnym wpisem na blogu współzałożyciela Reddit, Steve'a Huffmana .

Istnieją również aplikacje open-source napisane w Common Lisp, takie jak:

Zobacz też

Bibliografia

Bibliografia

Chronologiczna lista opublikowanych (lub planowanych) książek o Common Lisp (języku) lub o programowaniu z Common Lispem (zwłaszcza o programowaniu AI).

Zewnętrzne linki