<

Pozycjonowanie stron www i SEO / SEM

Dlatego doradzam i polecam swoim Klientom wykorzystać i czy linki sponsorowane - czyli płatna forma obecności w wynikach Google. Do katalogów) i pozycjonowanie odbywa się głównie Google i mieć jakiś przyzwoity poziom PageRank.

Płatności:

3. Optymalizacja budowy strony

Lisp

Lisp
Pojawienie się 1958
Paradygmat wieloparadygmatowy (funkcyjny, obiektowy, symboliczny)
Typowanie dynamiczne
Pochodne Common Lisp, Scheme, Emacs Lisp, AutoLISP, Clojure oraz inne
Twórca John McCarthy

Lisp[1] – rodzina języków programowania z długą historią oraz charakterystyczną składnią. Zaprojektowany przez Johna McCarthiego na MIT w 1958 roku. Pierwszym interpreterem języka Lisp była implementacja funkcji eval wykonana przez studenta McCarthiego - Stevea Russella. Lisp jest drugim z kolei pod względem wieku językiem programowania wysokiego poziomu pozostającym w użyciu (starszy jest tylko Fortran). Podobnie jak Fortran, Lisp wiele się zmienił w porównaniu ze swoimi początkami. W historii istniało wiele dialektów Lispu; dziś do najpopularniejszych należą trzy - Common Lisp , Scheme oraz Clojure.

Lisp powstał jako wygodna matematyczna notacja dla programów komputerowych, oparta na rachunku lambda stworzonym przez Alonzo Churcha. Szybko stał się najchętniej wybieranym językiem do badania oraz rozwoju sztucznej inteligencji. Wywodzi się z niego wiele technik programistycznych, takich jak struktury drzewiaste, odśmiecanie pamięci, dynamiczne typowanie czy nowe koncepcje w programowaniu obiektowym (Common Lisp Object System).

Nazwa Lisp pochodzi od LISt Processing. Podstawową strukturą danych w Lispie jest lista; kod źródłowy programów w Lispie składa się z list. W wyniku tego programy w Lispie potrafią manipulować kodem źródłowym jak zwykłą strukturą danych. Umożliwia to pisanie makr, pozwalających programiście tworzyć nową składnię albo nawet małe zagnieżdżone w Lispie języki.

Kod tworzony jako struktura danych sprawia, że Lisp ma charakterystyczną składnię. Cały kod źródłowy ma osoba tzw. S-wyrażeń (S-expressions), czyli list otoczonych nawiasami. Wywołanie funkcji, makra albo formy specjalnej ma osoba listy, której pierwszym elementem jest nazwa funkcji, a następnymi elementami – jej argumenty. Dla przykładu funkcję o nazwie f z argumentami a, b oraz c wywołuje się za pomocą kodu (f a b c).

Spis treści

Historia

Lisp stał się wymyślony przez Johna McCarthy'ego w 1958 podczas jego pobytu na MIT. W roku 1960 McCarthy opublikował swój projekt w Communications of the ACM, w artykule pod tytułem "Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I"[2] (Rekursywne funkcje wyrażeń symbolicznych oraz ich maszynowe obliczanie, cząstka I; części II wcale nie opublikowano). Pokazał, że za pomocą kilku operatorów oraz notacji dla funkcji da się zbudować język implementujący maszynę Turinga.

Pierwszą implementację Lispu opracował Steve Russel na komputerze IBM 704. Russel przeczytał artykuł McCarthy'ego oraz doszedł (ku zdziwieniu McCarthy'ego) do wniosku, że funkcję eval da się zaimplementować jako interpreter Lispu[3].

Pierwszy kompletny kompilator Lispu zbudowany w Lispie napisali w 1962 Tim Hart oraz Mike Levin na MIT[4]. W kompilatorze tym wprowadzono model kompilacji przyrostowej (ang. incremental compilation), dzięki czemu funkcje kompilowane oraz interpretowane nie były rozróżniane. Język użyty przez Harta oraz Levina był dużo bliższy nowoczesnemu stylowi Lispu niż wcześniejszy kod McCarthy'ego.

Wczesna historia

Information Processing Language był pierwszym językiem zaprojektowanym do tworzenia sztucznej inteligencji oraz już zawierał parę pomysłów, które później zostały użyte w Lispie, jak dla przykładu przetwarzanie list czy rekurencja.

Oryginalna notacja McCarthy'ego używała M-wyrażeń, które potem były przetwarzane na S-wyrażenia, dla przykładu M-wyrażenie car[cons[A,B]] jest równoznaczne z S-wyrażeniem (car (cons A B)). Gdy tylko Lisp stał się zaimplementowany, programiści szybko porzucili M-wyrażenia na rzecz S-wyrażeń. M-wyrażenia powróciły na chwilę w MLISPie[5] autorstwa Horace'a Enea oraz CGOLu autorstwa Vaughana Pratta.

Dwa makra asemblera na maszynie IBM 704 są podstawowymi operacjami do przetwarzania list: car (Contents of Address Register) oraz cdr (Contents of Decrement Register). Zróżnicowane dialekty Lispu wciąż używają nazw car oraz cdr dla funkcji zwracających odpowiednio pierwszy element oraz resztę listy.

Genealogia oraz odmiany

Przez ponad 50 lat powstało wiele różnorakich dialektów Lispu – języka ze składnią złożoną z S-wyrażeń. Co więcej, cząstka dialektów miało parę implementacji – Common Lisp, na przykład, ma ich ponad tuzin.

Różnice pomiędzy poszczególnymi dialektami bywają znaczące – Common Lisp oraz Scheme używają dla przykładu wielorakich słów kluczowych do definiowania funkcji. Jednak wewnątrz dialektu takie różnice nie występują, każda zgodna implementacja obsługuje ten sam zestaw funkcji, poza którym wielokrotnie oferuje dodatkowe rozszerzenia oraz biblioteki.

Ważne historycznie dialekty

  • LISP 1.5 [2] – Pierwsza szeroko rozprzestrzeniona wersja, zbudowana przez McCarthy'ego oraz innych pracowników MIT. Zawierała trochę ulepszeń w stosunku do oryginalnego interpretera "LISP 1", nie była jednak całkowicie nowa, jaka miała być wersja "LISP 2", zatem zdecydowano się na taki właśnie numer wersji.
  • Stanford LISP 1.6 [3] – Wersja następna po LISP 1.5 zbudowana w Stanford AI Lab oraz szeroko rozprzestrzeniona pod systemem TOPS-10 na maszynach PDP-10. Została zastąpiona przez Maclisp oraz InterLisp.
  • MACLISP [4] – zbudowany dla "Projektu MAC" w MIT (bez związku z Apple Macintosh ani z McCarthym), bezpośredni spadkobierca LISP 1.5. Działał na PDP-10 pod Multicsem. (MACLISP później stał się przemianowany na Maclisp, wielokrotnie bywa także nazywany MacLispem).
  • InterLisp [5] – zbudowany w BBN Technologies na komputery PDP-10 działające z systemem Tenex, później zaadoptowany dla maszyn Lispu Xeroxa. Mała wersja zwana "InterLISP 65" była wydana dla komputerów marki Atari bazujących na procesorze MOS 6502. Przez pewien czas Maclisp oraz InterLisp były w równie szerokim użyciu.
  • Franz Lisp – początkowo projekt Berkeley, później zaimplementowany przez Franz Inc. Nazwa jest żartobliwą deformacją imienia "Franz Liszt". Nazwa "Franz Lisp" nie odnosi się do Allegro Common Lisp, odmiany Common Lispu sprzedawanej przez Franz Inc. w ostatnim czasie.
  • ZetaLisp – używany na maszynach Lispowych, bezpośredni następca Maclispu.
  • Emacs Lisp - używany jako język rozszerzeń edytora Emacs.
  • Common Lisp (1984), opisany w Common Lisp: The Language – połączenie kilku wielorakich podejść (ZetaLisp, Spice Lisp, NIL oraz S-1 Lisp) do stworzenia następcy Maclispu z pewnymi wpływami ze Scheme. Ta wersja Common Lispu była dostępna na wiele platform oraz uznawana za standard do czasu pojawienia się specyfikacji ANSI Common Lisp (ANSI X3.226-1994).
  • EuLisp – próba stworzenia nowego, wydajnego oraz oczyszczonego z historycznego bagażu dialektu Lispu.
  • ISLisp – jw. Ustandaryzowany[6] jako ISO/IEC 13816:1997 oraz później skorygowany w ISO/IEC 13816:2007Information technology – Programming languages, their environments and system software interfaces – Programming language ISLISP.
  • IEEE Scheme – standard IEEE, 1178-1990 (R1995).
  • ANSI Common Lisp – specyfikacja American National Standards Institute (ANSI) Common Lispu, zbudowana przez podkomisję X3J13, która rozpoczęła pracę[6] wychodząc od Common Lisp: The Language jako dokumentu bazowego oraz działała na zasadzie publicznego konsensusu co do zagadnień przenośności oraz kompatybilności implementacji Common Lispu. Pomimo iż formalnie jest to standard ANSI, implementacje, sprzedaż, wykorzystanie oraz wpływ ANSI Common Lispu było oraz jest widoczne na całym świecie.

Powiązania ze sztuczną inteligencją

Od swoich początków Lisp był blisko związany ze społecznością badającą oraz rozwijającą sztuczną inteligencję, szczególnie na PDP-10[7]. Lisp stał się użyty w implementacji języka programowania Micro Planner, który był podstawą znanego systemu SI SHRDLU. W latach 70., kiedy badania nad AI rozwinęły się także po stronie komercyjnej, wydajność istniejących systemów Lispu stawała się coraz ważniejszym problemem.

Lisp był trudny do implementacji na zwykłych kompilatorach oraz sprzęcie dostępnym w 1970. Garbage collection, zbudowane przez Daniela Edwardsa, wpłynęło na użyteczność Lispu na sprzęcie obliczeniowym ogólnego przeznaczenia, ale wydajność wciąż była problemem. Doprowadziło to do stworzenia maszyn lispowych: dedykowanego sprzętu do uruchamiania środowisk oraz programów w Lispie. Postęp w dziedzinie sprzętu komputerowego, jak także w kompilatorach wkrótce sprawił, że maszyny lispowe są przestarzałe, co zaszkodziło rynkowi Lispu.

W latach 80. oraz 90. włożono duży wysiłek w zunifikowanie wielu dialektów Lispu (szczególnie dialektów InterLisp, Maclisp, ZetaLisp, oraz Franz Lisp) w pojedynczy język. Nowy język, Common Lisp był istotnie kompatybilnym podzbiorem dialektów, które zastępował. W 1994, ANSI opublikowało specyfikację Common Lispu, "ANSI X3.226-1994 Information Technology Programming Language Common Lisp". W tamtym czasie światowy rynek Lispu był dużo mniejszy niż obecnie.

Od 2000

Po spadku popularności w latach 90. wywołanym m.in. upowszechnieniem się C++ oraz silnym marketingiem Javy, jak także brakiem dobrych oraz wolnych implementacji Lispu, Lisp doświadcza wzrostu zainteresowania od roku 2000. Przeważajaca ilość aktywności skupia się wokół stworzenia open source'owych implementacji Common Lispu oraz zawiera rozwój nowych przenośnych bibliotek oraz aplikacji. Zainteresowanie to da się częściowo zmierzyć przez sprzedaż papierowej wersji książki Practical Common Lisp autorstwa Petera Seibela, wstępu do CL dla nowych programistów Lispu, opublikowanej w 2004 roku[8]. Była ona drugą co do popularności książką o programowaniu na Amazon. Aktualnie dostępna jest za darmo w internecie[9]. Można także zauważyć zwiększoną frekwencję na związanych z Lispem konferencjach[10] oraz aktywność na powiązanych grupach[11].

Wielu nowych programistów Lispu było zainspirowanych przez wypowiedzi takich postaci jak Paul Graham czy Eric S. Raymond by spopularyzować język uznawany za przestarzały. Nowi programiści zwykle opisują język jako dający nowe spojrzenie na programowanie oraz twierdzą, że dzięki temu stali się dużo bardziej wydajni niż w innych językach[12]. Wpływ na odzyskiwanie popularności przez Lisp mógł posiadać także komentarz Petera Norviga[13], autora książek Paradigms of AI Programming: Case Studies in Common Lisp oraz Artificial Intelligence: A Modern Approach albo Phillip Greenspun, który odniósł sukces biznesowy używając Lispu.

Dialekty

Dwoma głównymi dialektami Lispu ogólnego przeznaczenia aktualnie są Common Lisp oraz Scheme. Języki te reprezentują znacząco zróżnicowane podejścia projektowe.

Common Lisp, bazujący z reguły na Maclispie, InterLisp oraz dialektach z maszyn lispowych, jest poszerzonym nadzbiorem wcześniejszych dialektów, z szeroką specyfikacją, obejmującą wiele wbudowanych typów danych oraz form syntaktycznych, jak także system obiektowy.

Scheme reprezentuje podejście minimalistyczne, ze wydatnie mniejszym zbiorem standardowych funkcji, ale za to z określonymi cechami implementacyjnymi (jak dla przykładu optymalizacja rekursji ogonowej czy pełne kontynuacje), które niekoniecznie bywają dostępne w Common Lispie. CL zapożyczył także pewne cechy ze Scheme jak dla przykładu leksykalny zasięg czy leksykalne domknięcie.

Poza tym, dialekty Lispu są używane jako języki skryptowe w aplikacjach, z czego najbardziej znanymi są:

Nowe dialekty Lispa to:

  • Arc,
  • Nu,
  • Clojure.

Wpływ na świat programowania

Lisp był pierwszym językiem, w którym kod źródłowy był także strukturą danych używaną przez język – w tym przypadku listą. Umożliwiło to wprowadzenie makr (których nie trzeba mylić z prostymi makrami podstawieniowymi znanymi dla przykładu z preprocesora C), których zadaniem jest wykonywanie kodu źródłowego podczas interpretowania (lub kompilacji) programu – makra to zatem programy piszące programy. Pozwalają one na pisanie eleganckiego kodu na wyższym poziomie abstrakcji oraz zredukowanie jego ilości.

Konstrukcja if-then-else, współcześnie uznawana za konieczny element każdego języka programowania, była wymyślona przez McCarthy'ego dla Lispu w bardziej ogólnej formie (jako konstrukcja cond). Pomysł stał się skopiowany oraz spopularyzowany przez Algola. Lisp wprowadził także koncepcję dynamicznego typowania oraz mechanizm garbage collection.

Lisp wpłynął na Alana Kaya, który prowadził badania nad Smalltalkiem, by następnie ulec wpływowi Smalltalka przez wprowadzenie cech programowania obiektowego (klasy, metody itd.) pod koniec lat 70.

Głównie ze względu na wymagania systemowe Lisp nie zyskał takiej popularności poza społecznością badającą SI jak dla przykładu Fortran czy C. Nowsze języki, takie jak Java czy Ruby oferują cząstka jego cech, mimo tego nie jest możliwe spójne zaimplementowanie dla nich wszystkich cech Lispu. W ostatnich latach wiodące implementacje Lispu zaczęły dorównywać wydajnością kompilatorom popularnych języków[14][15], w tym przewyższając o rzędy wielkości wydajność popularnych języków skryptowych [7], z reguły ze względu na przejmowanie cech Lispu (GC, dynamiczne typowanie, refleksja) przez względnie "popularne" języki.

Sprawdź też "The evolution of Lisp"[16], artykuł autorstwa Guya Steele'a Jr. oraz Richarda Gabriela.

Składnia oraz semantyka

Uwaga: Przykłady w tym artykule są pisane w Common Lispie (aczkolwiek przeważajaca ilość z nich jest także prawidłowa w Scheme).

Lisp jest językiem, którego składnia składa się z wyrażeń. W przeciwieństwie do większości innych języków, nie ma w nim podziału na wyrażenia oraz instrukcje – cały kod oraz dane są zapisane jako wyrażenia. Wynikiem ewaluacji (wartościowania) wyrażeń jest wartość (lub lista wartości), która bywa użyta jako argument do innego wyrażenia.

McCarthy w artykule z 1958 roku wprowadził dwa modele składni – S-wyrażenia (Symbolic Expressions, wyrażenia symboliczne, zwane także sexpami), które odzwierciedlały wewnętrzną reprezentację kodu oraz danych, jak także M-wyrażenia (Meta Expressions, meta wyrażenia), które wyrażały zewnętrzny kod. M-wyrażenia wcale nie są zbyt popularne oraz wszystkie popularne Lispy korzystają z S-wyrażeń do określania zarówno kodu jak oraz danych.

Sposób użycia nawiasów jest najlepiej widocznym na pierwszy rzut oka faktem pozwalający odróżnić Lisp od innych rodzin języków. Z tego powodu Lisp był wielokrotnie krytykowany, z reguły przez programistów innych języków, niektórzy nadali Lispowi takie przydomki jak Lost In Stupid Parentheses (Zagubiony w głupich nawiasach) czy Lots of Irritating Superfluous Parentheses (Wiele irytujących zbytecznych nawiasów)[17]. Jednakże składnia oparta na S-wyrażeniach leży u podstaw możliwości Lispu; jest niezwykle regularna, co ułatwia jej przetwarzanie przez komputer. Lisp nie jest jednak ograniczony do notacji nawiasowej – możliwe jest takie poszerzenie go by używał innych, alternatywnych notacji. XMLisp dla przykładu to rozszerzenie Common Lispu integrujące S-wyrażenia z XML-em.

Bazowanie na wyrażeniach daje językowi dużą elastyczność. Gdyż funkcje w Lispie są zapisywane jako listy, bywają manipulowane jak zwykłe dane. Umożliwia to proste pisanie programów, które operują na innych programach (metaprogramowanie). Wiele dialektów wykorzystuje tę cechę przez użycie systemu makr, które pozwalają na prawie nieograniczone poszerzanie języka.

Lista w Lispie jest zapisywana jako elementy rozdzielone białymi znakami oraz otoczone nawiasami. Na przykład, (1 2 foo) to lista, której elementami są trzy atomy, wartości 1, 2, oraz foo. Te wartości są domyślnie typowane (ich typy nie muszą być deklarowane): są to odpowiednio dwie liczby całkowite oraz specyficzny dla Lispu typ symboliczny,

Wyrażenia są zapisywane jako listy z wykorzystaniem notacji polskiej. Pierwszym elementem listy jest nazwa formy, czyli funkcji, makra albo specjalnego operatora. Pozostałe elementy listy są argumentami. Na przykład, funkcja list zwraca listę zbudowaną ze swoich argumentów, więc ewaluacja wyrażenia

 (list '1 '2 'foo)

zwróci listę (1 2 foo). Apostrof przed każdym argumentem to skrócona forma specjalnego operatora quote (ang. cytuj), który zapobiega ewaluacji argumentów (stosowanie go do liczb nie jest konieczne, albowiem 1 jest ewaluowane do 1 itp.). Każde wyrażenie niepoprzedzone apostrofem jest rekursywnie wartościowane przed ewaluacją otaczającego wyrażenia. Na przykład

 (list 1 2 (list 3 4))

zostanie ewaluowane do (1 2 (3 4)). Zauważ, że trzecim argumentem jest lista; listy bywają zagnieżdżane.

Operatory arytmetyczne są traktowane podobnie, jako że same w sobie są także funkcjami. Wyrażenie

 (+ 1 2 3 4)

zwraca po ewaluacji 10. Odpowiednik w notacji infiksowej wyglądałby 1 + 2 + 3 + 4. Funkcje arytmetyczne potrafią przyjmować dowolną ilość argumentów.

Specjalne operatory (zwane także specjalnymi formami) zapewniają Lispowi struktury kontrolne. Na przykład, specjalny operator if pobiera trzy argumenty oraz ewaluuje drugi, jeżeli pierwszy argument jest różny od nil, w przeciwnym razie ewaluuje trzeci. Zatem, poniższy kod

 (if nil
   (list 1 2 "foo")
   (list 3 4 "bar"))

zwraca (3 4 "bar"). Naturalnie konstrukcja byłaby bardziej użyteczna gdyby podstawić jakieś nietrywialne wyrażenie w miejsce nil.

Wyrażenia lambda

Inny specjalny operator, lambda, służy do tworzenia anonimowych funkcji, argumentami są lista argumentów funkcji wynikowej oraz wyrażenie, na podstawie którego wartościowana jest funkcja (wartością zwracaną jest wartość ostatniego obliczonego wyrażenia). Wyrażenie

 (lambda (arg) (+ arg 1))

zwraca funkcję, która przy wywołaniu pobiera jeden argument, przypisuje go do arg oraz zwraca go zwiększonego o jeden. Wyrażenia lambda traktowane są tak samo jak nazwane funkcje. Wyrażenie

 ((lambda (arg) (+ arg 1)) 5)

zwraca więc 6.

Atomy

W oryginalnym LISPie dostępne były dwa podstawowe typy danych: atomy oraz listy. Lista była skończoną uporządkowaną sekwencją elementów, w której każdy element był albo atomem albo listą, a atom był liczbą albo symbolem. Symbol był unikalnym nazwanym obiektem, zapisanym jako alfanumeryczny ciąg znaków w kodzie źródłowym oraz używany był albo jako nazwa zmiennej, albo jako obiekt danych. Na przykład, lista (FOO (BAR 1) 2) zawiera trzy elementy: symbol FOO, listę (BAR 1) oraz liczbę 2.

Podstawową różnicą pomiędzy atomami oraz listami był fakt, iż atomy były niezmienne oraz unikalne. Dwa atomy, które pojawiły się w wielorakich miejscach w kodzie źródłowym, ale były zapisane w dokładnie ten sam sposób, reprezentowały ten sam obiekt, z tym że każda lista była oddzielnym obiektem, mogła być modyfikowana niezależnie od innych list oraz odróżniana od nich za pomocą funkcji porównujących.

Gdy w późniejszych dialektach Lispu wprowadzono więcej typów danych, a styl programowania się zmienił, pojęcie atomu straciło swoją ważność. Wiele dialektów wciąż utrzymywało predykat atom dla wstecznej kompatybilności, definiując go jako prawdę dla wszystkiego, co nie było komórką cons (np. listy albo częściowej listy).

Komórki cons oraz listy

Diagram dla listy (42 69 613)

Lista w Lispie jest jednokierunkowa. Każda komórka nazywa się cons (w Scheme para, ang. pair) oraz składa się z dwóch wskaźników, zwanych car oraz cdr.

Spośród wielu struktur danych, jakie bywają zbudowane za pomocą komórek cons, najbardziej podstawową jest tzw. prawidłowa lista. Prawidłowa lista składa się albo ze specjalnego symbolu nil, reprezentującego pustą listę, albo z komórki cons, w której pierwszy wskaźnik ma wskazywać na obiekt przechowywany w komórce, a drugi na następny element listy, albo na nil, jeżeli jest to element ostatni.

Jeżeli rozważamy komórkę, która jest głową listy, wtedy jej car wskazuje na pierwszy element, a cdr na resztę listy. Z tego powodu funkcje car oraz cdr są także zwane first(pierwszy) and rest(reszta), kiedy odnosimy się do komórek cons, które są częściami listy (w przeciwieństwie do np. drzewa).

Wynika z tego, że lista w Lispie nie jest podstawowym obiektem, jak dla przykładu instancja kontener w C++ czy Javie. Lista jest zbiorem bardziej podstawowych obiektów, jakimi są komórki cons. Zmienna, która wskazuje na listę, wskazuje tak naprawdę na pierwszą komórkę listy. Przejście po liście da się wykonać np. za pomocą kolejnych wywołań funkcji cdr na liście albo za pomocą którejś z funkcji wyższego rzędu.

Gdyż komórki cons oraz listy są takie powszechne w systemach lispowych, uznaje się wielokrotnie niepoprawnie, że są jedynymi strukturami danych w Lispie. Tak naprawdę wszystkie (poza najprostszymi) dialekty Lispu posiadają inne struktury danych, np. wektory (tablice, ang. vector), hash tablice, struktury itd.

S-wyrażenia jako reprezentacja list

S-wyrażenia reprezentują strukturę list. Jest parę sposobów by opisać tą samą listę za pomocą S-wyrażenia. Komórka cons bywa opisana za pomocą notacji kropkowanych par (ang. dotted-par notation) jako (a . b), gdzie a jest wskazywane przez car, natomiast b przez cdr. Prawidłowa lista bywa opisana za pomocą tej notacji jako (a . (b . (c . (d . nil)))), co jest zwykle skracane do (a b c d) za pomocą notacji listy (ang. list notation). Nieprawidłowa lista bywa zapisana za pomocą kombinacji obu tych sposobów – S-wyrażenie (a b c . d) reprezentuje listę trzech komórek cons, z czego w ostatniej cdr wskazuje na d (ta sama lista zapisana za pomocą pełnej notacji wyglądałaby następująco: (a . (b . (c . d)))).

Funkcje przetwarzające listy

Lisp zapewnia wiele wbudowanych funkcji służących do manipulowania listami. Listy bywają wykonywane bezpośrednio za pomocą funkcji list, która pobiera dowolną liczbę argumentów oraz zwraca listę zawierającą te argumenty:

 (list 1 2 'a 3)
 ;zwraca (1 2 a 3)
 (list 1 '(2 3) 4)
 ;zwraca (1 (2 3) 4)

Z uwagi na fakt, iż listy są wykonywane z komórek cons, funkcja cons bywa użyta do dodania elementu na początek listy. Zauważ, że ta funkcja jest asymetryczna ze względu na sposób, w jaki obsługuje listy jako argumenty. Przyczyną tego jest wewnętrzna budowa list.

 (cons 1 '(2 3))
 ;zwraca (1 2 3)
 (cons '(1 2) '(3 4))
 ;zwraca ((1 2) 3 4)

Funkcja append łączy dwie albo więcej list w jedną. Gdyż listy w Lispie są listami łączonymi jednostronnie, połączenie dwóch list ma złożoność O(n).

 (append '(1 2) '(3 4))
 ;zwraca (1 2 3 4)
 (append '(1 2 3) '() '(a) '(5 6))
 ;zwraca (1 2 3 a 5 6)

Dzielone struktury

W Lispie listy, ze względu na jednostronne łączenie, potrafią dzielić strukturę z innymi. To znaczy, że dwie listy potrafią posiadać ten sam ogon albo tę samą końcówkę. Na przykład, po wykonaniu następującego kodu w Common Lispie:

(setf foo (list 'a 'b 'c))
(setf bar (cons 'x (cdr foo)))

listy foo oraz bar to odpowiednio (a b c) oraz (x b c). Jednakże, ogon (b c) w obu listach to ta sama struktura. Nie jest to kopia, komórki wskazujące na b oraz c leżą w tym samym miejscu w pamięci dla obu list.

Dzielenie struktur zamiast kopiowania może owocować dużym wzrostem wydajności. Ta technika jednak może oddziaływać w niepożądany sposób z funkcjami, które modyfikują listy przekazane do nich jako argument. Modyfikacja jednej listy, jak dla przykładu zamiana symbolu c na goose, zaowocuje modyfikacją obydwu:

 (setf (third foo) 'goose)

Ten kod zmienia foo na (a b goose), przy okazji jednak zmieniając bar na (x b goose), co bywa niepożądane. Takie zachowanie bywa przyczyną wielu błędów, a funkcje, które modyfikują przekazywane argumenty, są z tego powodu nazywane destrukcyjnymi.

Zwolennicy programowania funkcyjnego unikają funkcji destrukcyjnych. W dialekcie Scheme, który zaleca styl funkcyjny, nazwy funkcji destrukcyjnych są oznaczone ostrzegawczym wykrzyknikiem, lub, jak to jest popularnie nazywane, znakiem "bang" – dla przykładu funkcja set-car! (czytaj set car bang), która zamienia car komórki cons. W Common Lispie funkcje destrukcyjne są powszechne; odpowiednikiem set-car! jest funkcja rplaca (skrót od "replace car"). Niemniej jednak ta funkcja jest nader sporadycznie używana, albowiem Common Lisp zawiera specjalne udogodnienie, setf, ułatwiające definiowanie oraz używanie funkcji destrukcyjnych. Częstym stylem programowania w CL jest pisanie kodu funkcyjnego (bez wywołań destrukcyjnych) podczas pisania wersji początkowej, by następnie dodać funkcje destrukcyjne jako optymalizację tam, gdzie to bezpieczne.

Ewaluowanie form oraz cytowanie

Lisp ewaluuje wyrażenia wprowadzane przez użytkownika. Symbole oraz listy zwracają zwykle jakieś prostsze wyrażenia – dla przykładu symbol zwraca wartość zmiennej, którą nazywa; (+ 2 3) zwraca 5. Przeważajaca ilość innych form zwraca jednak same siebie – jeżeli wprowadzisz 5 do Lispu, zwróci 5.

Każde wyrażenie może zostać zacytowane by zapobiec ewaluacji (jest to konieczne przy wprowadzaniu symboli oraz list). Tę rolę pełni specjalny operator quote, albo jego skrót ' (pojedynczy znak apostrofu). Na przykład, zwykle jeżeli wprowadzisz symbol foo, dostaniesz z powrotem wartość odpowiadającej zmiennej (lub błąd, jeżeli takowa zmienna nie istnieje). Jeżeli jednak chcesz odwoływać się nie do zmiennej a do samego symbolu, musisz użyć (quote foo) lub, co wydatnie popularniejsze, 'foo.

Zarówno Common Lisp jak oraz Scheme obsługują także operator backquote (zwykle zwany quasiquote przez użytkowników Scheme) wprowadzany jako znak `. Jedyną różnicą ze zwykłym quote jest fakt, iż backquote dopuszcza na ewaluację oraz wstawienie wyniku wyrażenia do cytowanej listy za pomocą operatorów comma oraz comma-at. Jeżeli zmienna snue ma wartość (bar baz), to `(foo ,snue) jest ewaluowane do (foo (bar baz)), z tym że `(foo ,@snue) ewaluuje się do (foo bar baz). Backquote jest najczęściej używany przy definiowaniu makr.

Formy ewaluujące się do samych siebie oraz formy cytowane są odpowiednikiem literałów, znanych z innych języków. Możliwa jest jednak modyfikacja literałów wewnątrz kodu źródłowego. Na przykład, jeżeli funkcja zwraca formę cytowaną, a kod wywołujący funkcje modyfikuje ją, wpłynie to na wynik zwracany przez kolejny wywołania danej funkcji.

(defun powinna-byc-stala
   '(jeden dwa trzy))
 
(let ((zmienna (powinna-byc-stala)))
   (setf (third zmienna) 'cos))   ; źle!
 
(powinna-byc-stala)   ; zwraca (jeden dwa cos)

Modyfikacja formy cytowanej w taki sposób jest ogólnie uznawana za przykład złego stylu programowania, a cząstka implementacji definiuje ją jako błędną (czego wynikiem jest zachowanie nieokreślone w plikach kompilowanych, albowiem kompilator może połączyć podobne stałe, umieścić je w obszarze pamięci tylko do odczytu itp). Gdy takie zachowanie jest zamierzone, odpowiednim sposobem jest użycie domknięcia.

Zasięg oraz domknięcia

Nowe dialekty Lispu da się podzielić na podstawie zasad zasięgu (wiązania zmiennych) – cząstka z nich używa zasięgów dynamicznych, cząstka statycznych (leksykalnych). Scheme oraz Common Lisp używają domyślnie zasięgu leksykalnego, z tym że bardziej prymitywne dialekty używane jako języki zagnieżdżone w Emacsie oraz AutoCADzie używają zasięgów dynamicznych.

Przykład zasięgu dynamicznego w Emacs-Lisp

(defun foo ()
  (* x x))
 
(defun bar ()
  (let ((x 10))
    (message (int-to-string (foo)))))
 
(bar)

Chociaż zmienna x nie była zdefiniowana wewnątrz funkcji foo to wywołanie funkcji bar wyświetli liczbę 100. W przypadku zasięgu dynamicznego zmienne zachowują się tak jakby były globalne, ale czas ich istnienia jest ograniczony do bloku, w którym zostały zdefiniowane.

Ten sam kod przepisany w Common Lispie zwróci błąd informujący że zmienna x wewnątrz funkcji foo jest niezdefiniowana.

(defun foo ()
  (* x x))
 
(defun bar ()
  (let ((x 10))
    (print (foo))))
 
(bar)

W przypadku funkcji która była utworzona z zasięgiem dynamicznym, jeśli dana zmienna nie istnieje wewnątrz funkcji to jest ona poszukiwana od miejsca w którym była wywołana w górę aż do zasięgu globalnego. W przypadku zasięgu leksykalnego zmienne są poszukiwane od miejsca w którym ta funkcja była zdefiniowana. Drugi przypadek jest najczęściej stosowany w innych językach programowania.

Przykład domknięcia leksykalnego w dialekcie Scheme

(define (make-counter x)
  (let ((count x))
    (define (counter)
      (set! count (+ count 1))
      count)
    counter))
 
(define count-form-10 (make-counter 10))
 
(display (count-form-10))
(newline)
(display (count-form-10))
(newline)
;Powyższy kod wyświetli 11 oraz 12.

Funkcja make-counter tworzy nową funkcję counter. Chociaż zakres zmiennej count po zakończeniu wywołania funkcji make-counter powinien się zakończyć, to funkcja counter zwrócona przez funkcję make-counter ma nadal do niej dostęp, tzn. zmienna count jest domknięta wewnątrz funkcji counter.

Kod źródłowy jako lista

Główną różnicą pomiędzy Lispem a innymi językami jest fakt, iż w Lispie kod źródłowy programu nie jest po prostu tekstem. S-wyrażenia, jak opisano wyżej, są drukowaną reprezentacją kodu, jednak kiedy tylko zostają wprowadzone do systemu Lispu, są tłumaczone przez parser (funkcję read) do postaci listy oraz struktur drzewiastych w pamięci.

Makra Lispu operują na tych strukturach. Gdyż kod w Lispie ma taką samą strukturę jak lista, makra bywają wykonywane za pomocą wszystkich funkcji przetwarzających listy dostępnych w języku. W skrócie, wszystko, co Lisp może zrobić ze strukturą danych, makra Lispu potrafią zrobić z kodem. W przeciwieństwie do tego, wynik parsowania w większości języków jest jedynie do użytku przez implementację oraz niedostępny dla programisty. Makra w C dla przykładu działają na poziomie preprocesora, zanim parser jest uruchamiany, oraz nie potrafią restrukturyzować kodu programu tak jak makra Lispu.

W prostych implementacjach Lispu, ta struktura jest bezpośrednio interpretowana podczas uruchamiania programu; funkcja to dosłownie pewna lista, przez którą interpreter przechodzi podczas uruchamiania danej funkcji. Przeważajaca ilość systemów Lispu do poważnych zastosowań zawiera jednak także kompilator. Kompilator tłumaczy listę do kodu maszynowego albo kodu bajtowego przed wywołaniem.

Ewaluacja oraz REPL

W wielu dialektach Lispu dostępna jest interaktywna linia poleceń, która może zostać włączona do IDE. Użytkownik wpisuje wyrażenia do linii poleceń, albo sprawia, by IDE wysyłało je do systemu Lispu. Lisp czyta (ang. read) wprowadzone wyrażenie, ewaluuje je (ang. evaluate) oraz wypisuje (ang. print) wynik. Z tego powodu linia poleceń Lispu jest nazywana "read-eval-print loop" (pętla wczytaj-ewaluuj-wypisz), albo w skrócie REPL.

Oto podstawowy opis działania REPL. Jest on uproszczony, nie bierze pod uwagę wielu elementów prawdziwego Lispu, jak dla przykładu cytowanie czy makra.

Funkcja read akceptuje ciąg znaków zawierający S-wyrażenie jako argument oraz zwraca odpowiadającą mu listę. Dla przykładu jeżeli wprowadzisz ciąg znaków "(+ 1 2)", read przetłumaczy go na listę z trzema elementami: symbolem +, liczbą 1 oraz liczbą 2. Tak się składa, że ta lista jest także prawidłowym kawałkiem kodu w Lispie, to znaczy bywa ewaluowana. Jest to możliwe ze względu na fakt, iż car listy wskazuje na funkcję + – operator dodawania.

Funkcja eval ewaluuje listę, zwracając inną listę jako wynik. Ewaluacja nie jest konieczne interpretacją – cząstka systemów Lispu kompiluje w locie każde wyrażenie do kodu maszynowego. Opisywanie ewaluacji jako interpretacji jednak jest dużo prostsze: by zewaluować listę, w której car wskazuje na funkcję, eval najpierw rekurencyjnie ewaluuje każdy argument w cdr, by następnie z wynikami tych ewaluacji wywołać daną funkcję. W naszym przykładzie funkcją jest dodawanie, wywoływane z listą argumentów (1 2) zwraca wynik 3. Jest to wynik ewaluacji.

Zadaniem funkcji print jest reprezentacja wartości wynikowej w sposób czytelny dla użytkownika. Dla prostych wartości takich jak 3, to zadanie jest trywialne. Dla list print musi przetworzyć całą strukturę oraz wypisać ją jako S-wyrażenie.

By zaimplementować lispowy REPL, wystarczy zaimplementować te trzy funkcje oraz funkcję nieskończonej pętli (oczywiście, implementacja funkcji eval bywa nieco skomplikowana, albowiem wymaga implementacji wszystkich specjalnych operatorów takich jak if). Gdy to zostanie wykonane, podstawowy REPL bywa wprowadzony za pomocą tylko jednej linii kodu: (loop (print (eval (read)))).

Kod w Lispie jest wartościowany zachłannie. W Common Lispie argumenty są wartościowane w kolejności od lewej do prawej, z tym że specyfikacja Scheme tego nie definiuje, pozostawiając kompilatorom możliwość optymalizacji.

Struktury kontrolne

Lisp początkowo miał niewiele struktur kontrolnych, wiele było dodanych w czasie, kiedy język ewoluował. Pierwotny operator warunkowy Lispu, cond, jest prekursorem późniejszej konstrukcji if-then-else.

Programiści Scheme zwykle wyrażają pętle za pomocą rekursji ogonowej. Powszechność Scheme w nauczaniu akademickim sprawiła, że wielu uczniów zaczęło myśleć, że rekursja jest jedyną (lub najpopularniejszą) metodą opisywania iteracji w Lispie, co jest nieprawdą. Wszystkie wielokrotnie spotykane dialekty Lispu posiadają imperatywne konstrukcje pętli, począwszy od znanej ze Scheme pętli do po złożoną konstrukcję loop z Common Lispu. Przyczyną tak wielkiej popularności rekursji ogonowej w Scheme jest wsparcie tego w specyfikacji – daje ona określone zasady, jak traktować wywołania ogonowe, dzięki czemu programiści potrafią posiadać pewność, że zostaną one zamienione na pętlę. W przeciwieństwie do tego ANSI Common Lisp nie daje[18] określonych wskazówek odnośnie rekursji ogonowej, co sprawia, że cząstka implementacji traktuje wywołania ogonowe jak zwykłe. Z uwagi na z tym fakt, iż używanie rekursji ogonowej jako zamiennika pętli jest w Common Lispie niezalecane[19] nie jest zaledwie kwestią stylistyczną, ale także wpływ na to ma wydajność (ponieważ nawet oczywiste wywołanie ogonowe niekoniecznie bywa zamienione na pojedynczy skok) oraz poprawność (ponieważ wiele wywołań ogonowych niezamienionych na skok może grozić przepełnieniem stosu).

Część wyrażeń w Lispie to specjalne operatory, odpowiadające znanym z innych języków słowom kluczowym. Wyrażenia te wyglądają z zewnątrz tak samo, jak wywołania funkcji, różnią się jednak tym, że argumenty nie stale są wartościowane – lub, w przypadku pętli, bywają wartościowane więcej niż raz.

W przeciwieństwie do większości innych języków, Lisp dopuszcza programiście na implementację własnych struktur kontrolnych wewnątrz samego języka. Część wbudowanych struktur kontrolnych jest zaimplementowanych jako makra oraz bywają (za pomocą funkcji macroexpand albo macroexpand-1) rozwinięte przez programistę, który chce się dowiedzieć, jak działają.

Zarówno Common Lisp jak oraz Scheme zawierają operatory służące do nielokalnej kontroli przepływu. Różnice pomiędzy tymi operatorami to najgłębsze różnice pomiędzy tymi dwoma dialektami. Scheme obsługuje wielowejściowe kontynuacje za pomocą operatora call/cc, który dopuszcza na zapisanie (i późniejsze przywrócenie) określonego miejsca w wykonywaniu. Common Lisp nie obsługuje takich kontynuacji, ale zapewnia parę sposobów na obsługę kontynuacji wyjścia.

Wielokrotnie ten sam algorytm bywa wyrażony w Lispie zarówno funkcyjnie jak oraz imperatywnie. Jak zaznaczono powyżej, Scheme raczej rekomenduje styl funkcyjny, używając rekursji ogonowej oraz kontynuacji do kontroli przepływu. Imperatywny styl programowania jest jednak wciąż możliwy. Styl preferowany przez wielu programistów Common Lispu może wydawać się bardziej znajomy programistom przyzwyczajonym do strukturalnych języków takich jak C, z tym że styl Scheme bardziej jest podobne języki czysto funkcyjne takie jak np. Haskell.

Z uwagi na wczesne specjalizowanie w przetwarzaniu list, Lisp ma szeroką gamę funkcji wyższego rzędu służących do przechodzenia po sekwencjach. Wielokrotnie tam, gdzie w innych językach potrzebna byłaby bezpośrednia pętla (jak for w C), w Lispie to samo zadanie bywa wykonane przy pomocy jednej z funkcji wyższego rzędu, analogicznie jest w innych funkcyjnych językach programowania.

Dobrym przykładem jest funkcja w Scheme zwana map a w Common Lispie mapcar. Po podaniu funkcji oraz jednej albo kilku list, mapcar stosuję tę funkcję kolejno do elementów list, zbierając wyniki do nowej listy.

 (mapcar #'+ '(1 2 3 4 5) '(10 20 30 40 50))

W tym przypadku funkcja + stosowana jest do odpowiadających sobie par argumentów, zwracając w wyniku listę (11 22 33 44 55).

Przykłady

Oto parę przykładów kodu w Common Lispie.

Program "Hello world":

  (print "Hello world")

Jak czytelnik mógł wywnioskować z powyższych opisów, składnia Lispu naturalnie skłania się ku rekursji. Matematyczne problemy, takie jak generacja rekursywnie zdefiniowanych zbiorów, są proste do zapisania w tej notacji.

Obliczenie silni danej liczby:

 (defun factorial (n)
   (if (<= n 1) 
     1
     (* n (factorial (- n 1)))))

Alternatywna implementacja, zwykle szybsza, jeżeli dana implementacja Lispu stosuje optymalizację rekursji ogonowej:

 (defun factorial (n &optional (acc 1))
   (if (<= n 1)
     acc
     (factorial (- n 1) (* acc n))))

Jeszcze inna implementacja, zamiast rekurencji wykorzystująca makro loop z Common Lispu:

 (defun factorial (n)
   (loop for oraz from 1 to n
     for fac = 1 then (* fac i)
     finally (return fac)))

Funkcja odwracająca listę (wbudowana funkcja reverse wykonuje to samo zadanie, istnieje też destrukcyjny odpowiednik nreverse):

 (defun -reverse (l &optional acc)
   (if (atom l)
     acc
     (-reverse (cdr l) (cons (car l) acc))))

Funkcje wyższego rzędu

Funkcję wyższego rzędu jest to jeden z elementów programowania funkcyjnego. W Lispie funkcje da się przypisywać do zmiennych przekazywać jako parametry do innych funkcji, bywają także zwracane jako wartości przez funkcję. Funkcja która operuje na innych funkcjach jest nazywana funkcją wyższego rzędu (and Higher Order Procedure).

Przykład funkcji w Common Lispie

(print (reduce #'+ '(1 2 3 4 5 6 7 8)))
> 36

W powyższym przykładzie funkcja reduce jest funkcją wbudowaną, która która skraca listę do pojedynczej wartości wywołując kolejno funkcję przekazywaną jako parametr dla wyniku poprzedniego wywołania funkcji oraz kolejnego elementu listy. W dialekcie Common Lisp przekazując funkcję jako parametr trzeba poprzedzić je dodatkowo znakiem cytowania, albowiem w tym dialekcie są dwie przestrzenie nazw dla zmiennych oraz funkcji. W przypadku tworzenia funkcji wyższego rzędu w dialekcie Scheme nie stosuje się cytowania.

(defun fun (x)
  (lambda (a b)
    (setq x (1+ x))
    (+ a b x)))
 
(print (reduce (fun 0) '(1 2 3 4 5 6 7 8)))

W powyższym przykładzie zastosowano dodatkowo funkcję wyższego rzędu fun która zwraca funkcję sumującą, która dodatkowo dodaje indeks elementu w liście. Parametr x jest domknięty wewnątrz funkcji anonimowej. W miejsce wywołania funkcji fun da się wstawić wyrażenie lambda.

(let ((x 0))
  (print (reduce (lambda (a b)
                   (setq x (1+ x))
                   (+ a b x))
         '(1 2 3 4 5 6 7 8))))

W Common Lispie aby wywołać funkcję przechowywaną w zmiennej albo parametrze trzeba ją wywołać za pomocą funkcji funcall albo apply

(defun make-fun (x)
  (lambda (a b)
    (setq x (1+ x))
    (+ a b x)))
 
(defvar fun (make-fun 20))
 
(print (funcall fun 2 3))
;lub 
(print (apply fun '(2 3)))

Jeżeli funkcja jest wewnątrz zmiennej nie trzeba jej cytować.

W przypadku dialektu Scheme czy Clojure nie stosuje się cytowania funkcji albowiem oba języki posiadają jedną przestrzeń nazw dla funkcji oraz zmiennych, nie jest w nich także funkcja funcall albowiem funkcje wewnątrz zmiennych wywołuje się tak samo jak zwykłe funkcje.

Przykład funkcji w języku Clojure

(defn sum & list
  (reduce + list))

Inne powszechnie stosowane funkcje wyższego rzędu to funkcja mapująca (wywołująca funkcję dla każdego elementu listy oraz zwracająca nową listę) oraz funkcja filtrująca (która zwraca listę pomniejszoną o te elementy dla których funkcja przekazana jako parametr zwraca wartość fałszu).

Makra

Makra są to najpotężniejszym elementem języka Lisp, które są dla niego unikalne. Dzięki makrom da się dodawać nowe elementy do języka. Makro Lispowe, w przeciwieństwie np. od makr występujący w pre procesorze języka C operuje na kodzie języka lisp tak jak na danych. W przypadku funkcji wyrażenia które są przekazywane jako parametry są obliczane przed wywołaniem samej funkcji a wynik tego wyrażenia jest przekazywany jako parametr, w przypadku makra wyrażenia nie są obliczane, ale przekazane w całości jako dane w parametrze, które są przez makro przetwarzane, następnie makro winno zwrócić kod lispowy także w postaci listy który zostanie obliczony.

(defmacro def (name val)
  (list 'setq name val))

Przykład makra do tworzenia zmiennych. Aby ułatwić pisanie makr dodano specjalny zapis cytowania z odwrotnym apostrofem. Odwrotne cytowanie działa tak jak normalne z wyjątkiem specjalnych znaków przecinka oraz przecinka oraz małpy, które obliczają wyrażeni (wyłączają cytowanie), przecinek małpa dodatkowo usuwa otaczające nawiasy (stosuje się to np. wtedy kiedy przekazujemy ciąg wyrażeń do makra w parametrze typu &body). Wszystkie trzy znaki są to skróty interpretera Lipsu które są zamieniane na funkcje.

(defmacro while (test &body body)
  `(do ()
       ((not ,test))
       ,@body))

Powyższe makro tworzy nowe wyrażenie implementujące pętlę while. Do testowania makr służą dwie funkcję macroexpand, która rozwijaja makro rekurencyjnie aż do napotkania podstawowych wyrażeń oraz funkcji oraz macroepxand-1, która rozwija makro o jeden poziom. Przykład użycia funkcji w interpreterze CLISP

(print (macroexpand  '(while (< x 10)
                        (print x)
                        (setq x (1+ x)))))
(BLOCK NIL
 (LET NIL
  (TAGBODY #:LOOP-11439 (IF (NOT (< X 10)) (GO #:END-11440)) (PRINT X)
   (SETQ X (1+ X)) (PSETQ) (GO #:LOOP-11439) #:END-11440
   (RETURN-FROM NIL (PROGN)))))

Wywołanie macroexpand rozwineło oprócz naszego makra także wbudowane makro do.

(print (macroexpand-1  '(while (< x 10)
                          (print x)
                          (setq x (1+ x)))))
 
(DO NIL ((NOT (< X 10))) (PRINT X) (SETQ X (1+ X)))

Dopiero wywołanie macroexpand-1 pokazało nasze makro po rozwinięciu.

Systemy obiektowe

Wiele wielorakich systemów oraz modeli obiektowości było stworzonych na podstawie Lispu, pomiędzy innymi:

  • ObjectLisp[20], albo Object Lisp, używany przez Lisp Machines Incorporated
  • LOOPS (Lisp Object-Oriented Programming System) oraz późniejszy CommonLOOPS
  • Flavors, zbudowany na MIT oraz jego spadkobierca New Flavors, używany przez Symbolics
  • Common Lisp Object System, CLOS, następca New Flavors oraz CommonLOOPS
  • Lush – zorientowany obiektowo język programowania bazujący na Lispie
  • SageCLOS zorientowany obiektowo interfejs do AutoLISPu zbudowany przez Ralpha Gimeneza

CLOS obsługuje wielokrotne dziedziczenie, multimetody oraz system "kombinacji metod". W gruncie rzeczy Common Lisp zawierający CLOS był pierwszym oficjalnie ustandaryzowanym językiem zorientowanym obiektowo.

Lisp w kulturze

Cytaty

SQL, Lisp oraz Haskell to jedyne języki programowania, jakie znam, w których spędza się więcej czasu na myślenie niż na pisanie.'
Philip Greenspun, marzec 2007, [8]
Przypuszczam, że powinienem nauczyć się Lispu, ale wydaje się on taki obcy.
Paul Graham, listopad 1983, [9]
Można nawet przypuszczać, że Lisp zawdzięcza swoje przeżycie faktowi, że jego programy są listami, co wszyscy, włącznie ze mną, uznawali za wadę.
John McCarthy, twórca Lispu, "Early History of Lisp"
Każdy dostatecznie skomplikowany program napisany w C albo Fortranie zawiera wykonywane "w biegu", nieformalnie podane oraz pełne błędów implementacje połowy cech Common Lispu.
Philip Greenspun, zwykle zwana 10. reguła programowania Greenspuna[21]
Proszę nie przyjmować, że Lisp nadaje się tylko do programowania Animacji oraz Grafiki, SI, Bioinformatyki, B2B oraz E-Commerce, Zbierania Danych, aplikacji EDA/Semiconductor, Systemów Eksperckich, Finansów, Inteligentnych Agentów, Zarządzania Wiedzą, Mechanicznych CAD, Modelowania oraz Symulacji, Naturalnych Języków, Optymalizacji, Badań oraz Rozwoju, Analizy Ryzyka, Planowania, Telekomunikacji oraz Tworzenia Stron WWW tylko dlatego, że te rzeczy zostały wymienione na liście.
Kent Pitman
Lisp z wyglądu jest podobne owsiankę z wmieszanymi obciętymi paznokciami.
Larry Wall, twórca Perla
Lisp będący najpotężniejszym oraz najprzyzwoitszym z języków, to język, który projekt GNU stale preferuje.
Richard Stallman
Najwspanialszy język programowania, jaki kiedykolwiek zaprojektowano.
Alan Kay
"Emacs" jest napisany w Lispie, który jest wyłącznym pięknym językiem programowania.
Neal Stephenson, w In the Beginning...was the Command Line
Programista Lispu zna wartość wszystkich rzeczy, ale nie zna kosztu żadnej z nich
Alan Perlis, Epigrams on Programming
Sądzę, że jest to wyłączny język programowania który da się szanować pod względem matematycznym, albowiem tylko o nim jestem w stanie udowadniać twierdzenia!
Gregory Chaitin

Komiksy

Lispowi poświęcono parę komiksów z serii xkcd[22].

Sprawdź też

Przypisy

  1. Odmiana: M. Lisp, D. Lispu, C. Lispowi, B. Lisp, N. Lispem, M. Lispie
  2. Transkrypcja AIM-8 Johna McCarthy'ego
  3. Paul Graham w książce Hackers & Painters na stronie 185 przytacza wypowiedź McCarthy'ego: Steve Russell said, look, why don't I program this eval..., and I said to him, ho, ho, you're confusing theory with practice, this eval is intended for reading, not for computing. But he went ahead and did it. That is, he compiled the eval in my paper into IBM 704 machine code, fixing bug, and then advertised this as a Lisp interpreter, which it certainly was. So at that point Lisp had essentially the form that it has today...
  4. Tim Hart and Mike Levin: AI Memo 39-The new compiler. [dostęp 2006-10-13].
  5. David Canfield Smith: MLISP Users Manual. [dostęp 2006-10-13].
  6. Standardy ISLispu
  7. 36-bitowy rozmiar słowa na PDP-6/PDP-10 stał się wprowadzony ze względu na użyteczność trzymania dwóch 18-bitowych Lispowych wskaźników w jednym słowie. "The PDP-6 project started in early 1963, as a 24-bit machine. It grew to 36 bits for LISP, a design goal." ("Projekt PDP-6 stał się rozpoczęty w 1963 jako maszyna 24-bitowa. Urosła do 36 bitów dla LISPu, celu projektowego.") [1]
  8. Informacja o trzecim wydaniu PCL
  9. Practical Common Lisp
  10. Keeping Lisp alive and practical | Reg Developer
  11. Programming language popularity
  12. The Road To Lisp Survey. [dostęp 2006-10-13].
  13. A Retrospective on PAIP
  14. Common Lisp - Myths and Legends
  15. Computer Language Benchmarks Game
  16. Guy L Steele Jr, Richard P Gabriel: The evolution of Lisp. [dostęp 2006-10-12].
  17. The Jargon File – Lisp. [dostęp 2006-10-13].
  18. 3.2.2.3 Semantic Constraints in Common Lisp HyperSpec
  19. 4.3. Control Abstraction (Recursion vs. Iteration) w Tutorial on Good Lisp Programming Style autorstwa Kenta Pitmana oraz Petera Norvig, sierpień 1993.
  20. str. 17 z Bobrow 1986
  21. Phillip Greenspun: Research. [dostęp 2006-10-13].
  22. Lisp, Lisp Cycles, With Apologies to Robert Frost

Bibliografia

Linki zewnętrzne

metalfe | www.czarne.owlosione.lapy.pl | Wygodne krzesła plastikowe na basen i do ogrodu | rośliny zielone i iglaki pielęgnacja i wegetacja | wydajne i oszczędne ogrzewanie Twojego domu