Movatterモバイル変換


[0]ホーム

URL:


Przejdź do zawartości
Wikipediawolna encyklopedia
Szukaj

Lisp

Z Wikipedii, wolnej encyklopedii
Lisp
Logo języka Lisp
Logo języka
Pojawienie się

1958

Paradygmat

wieloparadygmatowy (funkcyjny,obiektowy,symboliczny)

Typowanie

dynamiczne

Pochodne

Common Lisp,Scheme,Emacs Lisp,AutoLISP,Clojure i inne

Twórca

John McCarthy

Lisp[1] – rodzinajęzyków programowania z długą historią i charakterystyczną składnią. Zaprojektowany przezJohna McCarthy’ego naMIT w 1958 roku. Pierwszym interpreterem języka Lisp byłaimplementacja funkcjieval wykonana przez studenta McCarthy’ego –Steve’a Russella. Lisp jest drugim z kolei pod względem wieku językiem programowania wysokiego poziomu pozostającym w użyciu (starszy jest tylkoFortran). Podobnie jak Fortran, Lisp ulegał na przestrzeni czasu licznym zmianom. Powstało również wiele jego dialektów. Dziś do najpopularniejszych należą trzy:Common Lisp,Scheme iClojure.

Lisp powstał jako wygodnamatematyczna notacja dlaprogramów komputerowych, oparta narachunku lambda stworzonym przezAlonzo Churcha. Szybko został najchętniej wybieranym językiem do badania i rozwojusztucznej inteligencji. Wywodzi się z niego wiele technik programistycznych, takich jakstruktury drzewiaste,odśmiecanie pamięci,dynamiczne typowanie czy nowe koncepcje wprogramowaniu obiektowym (Common Lisp Object System).

NazwaLisp pochodzi odLISt Processing. Podstawową strukturą danych w Lispie jestlista;kod źródłowy programów w Lispie składa się z list. Dzięki temu język jesthomoikoniczny, tzn. programy w Lispie mogą manipulować kodem źródłowym jak zwykłą strukturą danych. Umożliwia to pisaniemakr, pozwalających programiście tworzyć nową składnię lub nawet małe, zagnieżdżone w Lispie, językiDSL.

Kod tworzony jakostruktura danych sprawia, że Lisp ma charakterystyczną składnię. Cały kod źródłowy ma postać tzw.S-wyrażeń (S-expressions), czyli list otoczonych nawiasami. Wywołanie funkcji, makra lub formy specjalnej ma postać listy, której pierwszym elementem jest nazwa funkcji, makra lub formy specjalnej lub wyrażenie, którego wynikiem jest funkcja, a następnymi elementami – argumenty. Na przykład funkcję o nazwief z argumentamia,b ic wywołuje się za pomocą kodu(fabc), natomiast gdy(gab) zwraca funkcje, można użyć((gab)cd).

Historia

[edytuj |edytuj kod]

Lisp został wymyślony przezJohna McCarthy’ego w 1958 podczas jego pobytu naMIT. W roku1960 McCarthy opublikował swój projekt wCommunications 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 i ich maszynowe obliczanie, część I; części II nigdy nie opublikowano). Pokazał, że za pomocą kilku operatorów i notacji dla funkcji można zbudować język implementującymaszynę Turinga.

Pierwszą implementację Lispu opracowałSteve Russell na komputerzeIBM 704 za pomocąkart dziurkowanych[3]. Russell przeczytał artykuł McCarthy’ego i doszedł (ku zdziwieniu McCarthy’ego) do wniosku, że funkcjęeval można zaimplementować jakointerpreter Lispu[4].

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

Wczesna historia

[edytuj |edytuj kod]

Information Processing Language był pierwszym językiem zaprojektowanym do tworzenia sztucznej inteligencji i już zawierał kilka pomysłów, które później zostały użyte w Lispie, jak na przykład przetwarzanie list czyrekurencja.

Oryginalna notacja McCarthy’ego używała M-wyrażeń, które potem były przetwarzane na S-wyrażenia, na przykład M-wyrażeniecar[cons[A,B]] jest równoznaczne z S-wyrażeniem(car (cons A B)). Gdy tylko Lisp został zaimplementowany, programiści szybko porzucili M-wyrażenia na rzecz S-wyrażeń. M-wyrażenia powróciły na chwilę wMLISPie[6] autorstwaHorace’a Enea iCGOLu autorstwaVaughana Pratta.

Dwa makra asemblera na maszynieIBM 704 stały się podstawowymi operacjami do przetwarzania list:car (Contents of Address Register) icdr (Contents of Decrement Register)[7]. Różne dialekty Lispu wciąż używają nazwcar icdr dla funkcji zwracających odpowiednio pierwszy element i resztę listy.

Genealogia i odmiany

[edytuj |edytuj kod]

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ęść dialektów miało kilka implementacji –Common Lisp, na przykład, posiada ich ponad tuzin.

Różnice między poszczególnymi dialektami mogą być znaczące – Common Lisp iScheme używają na przykład różnych 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 często oferuje dodatkowe rozszerzenia i biblioteki.

Ważne historycznie dialekty

[edytuj |edytuj kod]
  • LISP 1.5[2] – Pierwsza szeroko rozprzestrzeniona wersja, stworzona przez McCarthy’ego i 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 stworzona wStanford AI Lab i szeroko rozprzestrzeniona pod systememTOPS-10(inne języki) na maszynachPDP-10. Została zastąpiona przez Maclisp i InterLisp.
  • MACLISP[4] – stworzony dla „Projektu MAC” w MIT (bez związku zApple Macintosh ani z McCarthym), bezpośredni spadkobierca LISP 1.5. Działał na PDP-10 podMulticsem. (MACLISP później został przemianowany na Maclisp, często bywa również nazywany MacLispem).
  • InterLisp[5] – stworzony wBBN Technologies na komputery PDP-10 działające z systememTenex, później zaadaptowany dla maszyn LispuXeroxa. Mała wersja zwana „InterLISP 65” została wydana dla komputerów markiAtari bazujących na procesorzeMOS 6502. Przez pewien czas Maclisp i InterLisp były w równie szerokim użyciu.
  • Franz Lisp(inne języki) – początkowo projektBerkeley, później zaimplementowany przezFranz Inc. Nazwa jest żartobliwą deformacją imienia „Franz Liszt”. Nazwa „Franz Lisp” nie odnosi się doAllegro Common Lisp, odmiany Common Lispu sprzedawanej przez Franz Inc. w ostatnim czasie.
  • ZetaLisp – używany namaszynach Lispowych, bezpośredni następca Maclispu.
  • Emacs Lisp – używany jako język rozszerzeń edytoraEmacs.
  • Common Lisp (1984), opisany wCommon Lisp: The Language – połączenie kilku różnych podejść (ZetaLisp,Spice Lisp,NIL iS-1 Lisp) do stworzenia następcy Maclispu z pewnymi wpływami ze Scheme. Ta wersja Common Lispu była dostępna na wiele platform i uznawana za standard do czasu pojawienia się specyfikacji ANSI Common Lisp (ANSI X3.226-1994).
  • EuLisp(inne języki) – próba stworzenia nowego, wydajnego i oczyszczonego z historycznego bagażu dialektu Lispu.
  • ISLisp(inne języki) – jw. Ustandaryzowany[8] jakoISO/IEC 13816:1997 i później skorygowany wISO/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 – specyfikacjaAmerican National Standards Institute (ANSI) Common Lispu, stworzona przez podkomisjęX3J13(inne języki), która rozpoczęła pracę[6] wychodząc odCommon Lisp: The Language jako dokumentu bazowego i działała na zasadzie publicznegokonsensusu co do zagadnień przenośności i kompatybilności implementacji Common Lispu. Pomimo iż formalnie jest to standard ANSI, implementacje, sprzedaż, wykorzystanie i wpływ ANSI Common Lispu było i jest widoczne na całym świecie.

Powiązania ze sztuczną inteligencją

[edytuj |edytuj kod]

Od swoich początków Lisp był blisko powiązany ze społecznością badającą i rozwijającąsztuczną inteligencję, szczególnie naPDP-10[9]. Lisp został użyty w implementacji języka programowaniaMicro Planner, który był podstawą znanego systemu SISHRDLU. Wlatach 70., gdy badania nad AI rozwinęły się również 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 i sprzęcie dostępnym w 1970.Garbage collection, stworzone przezDaniela 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 stworzeniamaszyn lispowych: dedykowanego sprzętu do uruchamiania środowisk i programów w Lispie. Postęp w dziedzinie sprzętu komputerowego, jak również w kompilatorach wkrótce sprawił, że maszyny lispowe stały się przestarzałe, co zaszkodziło rynkowi Lispu.

W latach80. i90. włożono duży wysiłek w zunifikowanie wielu dialektów Lispu (szczególnie dialektówInterLisp,Maclisp,ZetaLisp, iFranz Lisp) w pojedynczy język. Nowy język,Common Lisp był istotnie kompatybilnym podzbiorem dialektów, które zastępował. W1994,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

[edytuj |edytuj kod]

Po spadku popularności w latach 90. wywołanym m.in. upowszechnieniem się C++ i silnym marketingiem Javy, jak również brakiem dobrych iwolnych implementacji Lispu, Lisp doświadcza wzrostu zainteresowania od roku2000[potrzebny przypis]. Większość aktywności skupia się wokół stworzeniaopen source’owych implementacji Common Lispu i zawiera rozwój nowych przenośnych bibliotek i aplikacji. Zainteresowanie to można częściowo zmierzyć przez sprzedaż papierowej wersji książkiPractical Common Lisp autorstwaPetera Seibela, wstępu do CL dla nowychprogramistów Lispu, opublikowanej w2004 roku[10]. Była ona drugą co do popularności książką o programowaniu naAmazon. Aktualnie dostępna jest za darmo winternecie[11]. Można również zauważyć zwiększoną frekwencję na związanych z Lispem konferencjach[12] i aktywność na powiązanychgrupach[13].

Wielu nowych programistów Lispu zostało zainspirowanych przez wypowiedzi takich postaci jakPaul Graham czyEric S. Raymond by spopularyzować język uznawany za przestarzały. Nowi programiści zwykle opisują język jako dający nowe spojrzenie na programowanie i twierdzą, że dzięki temu stali się dużo bardziej wydajni niż w innych językach[14]. Wpływ na odzyskiwanie popularności przez Lisp mógł mieć również komentarzPetera Norviga[15], autora książekParadigms of AI Programming: Case Studies in Common Lisp iArtificial Intelligence: A Modern Approach lubPhillip Greenspun, który odniósł sukces biznesowy, używając Lispu.

Dialekty

[edytuj |edytuj kod]

Dwomagłównymi dialektami Lispu ogólnego przeznaczenia aktualnie sąCommon Lisp iScheme. Języki te reprezentują znacząco różne podejścia projektowe.

Common Lisp, bazujący głównie naMaclispie,InterLisp i dialektach z maszyn lispowych, jest poszerzonym nadzbiorem wcześniejszych dialektów, z szeroką specyfikacją, obejmującą wiele wbudowanych typów danych i form syntaktycznych, jak również system obiektowy.

Scheme reprezentuje podejście minimalistyczne, ze znacznie mniejszym zbiorem standardowych funkcji, ale za to z określonymi cechami implementacyjnymi (jak na przykład optymalizacjarekursji ogonowej czy pełnekontynuacje), które niekoniecznie mogą być dostępne w Common Lispie. CL zapożyczył również pewne cechy ze Scheme jak na przykładleksykalny zasięg czy leksykalnedomknięcie.

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

Nowe dialekty Lispa to:

  • Arc,
  • Nu,
  • Clojure.

Wpływ na świat programowania

[edytuj |edytuj kod]

Lisp był pierwszym językiem, w którym kod źródłowy był również strukturą danych używaną przez język – w tym przypadku listą. Umożliwiło to wprowadzenie makr (których nie należy mylić z prostymi makrami podstawieniowymi znanymi na przykład z preprocesora C), których zadaniem jest tworzenie 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 i zredukowanie jego ilości.

Konstrukcjaif-then-else, współcześnie uznawana za konieczny element każdego języka programowania, została wymyślona przez McCarthy’ego dla Lispu w bardziej ogólnej formie (jako konstrukcja cond). Pomysł został skopiowany i spopularyzowany przezAlgola. Lisp wprowadził również koncepcjędynamicznego typowania i mechanizmgarbage collection.

Lisp wpłynął naAlana Kaya, który prowadził badania nadSmalltalkiem, by następnie ulec wpływowi Smalltalka przez wprowadzenie cechprogramowania 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 na przykładFortran czyC. Nowsze języki, takie jakJava czyRuby oferują część 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[16], w tym przewyższając o rzędy wielkości wydajność popularnych języków skryptowych, głównie ze względu na przejmowanie cech Lispu (GC, dynamiczne typowanie, refleksja) przez względnie „popularne” języki.

Zobacz też „The evolution of Lisp”[17], artykuł autorstwaGuya Steele’a Jr. iRicharda Gabriela.

Składnia i semantyka

[edytuj |edytuj kod]
Uwaga: Przykłady w tym artykule są pisane w Common Lispie (aczkolwiek większość z nich jest również 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 nawyrażenia iinstrukcje – cały kod i dane są zapisane jako wyrażenia. Wynikiemewaluacji (wartościowania) wyrażeń jest wartość (lub lista wartości), która może być użyta jako argument do innego wyrażenia.

McCarthy w artykule z1958 roku wprowadził dwa modele składni –S-wyrażenia (Symbolic Expressions, wyrażenia symboliczne, zwane również sexpami), które odzwierciedlały wewnętrzną reprezentację kodu i danych, jak również M-wyrażenia (Meta Expressions, meta wyrażenia), które wyrażały zewnętrzny kod. M-wyrażenia nigdy nie stały się zbyt popularne i wszystkie popularne Lispy korzystają z S-wyrażeń do określania zarówno kodu, jak i 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ł często krytykowany, głównie przez programistów innych języków, niektórzy nadali Lispowi takie przydomki jakLost In Stupid Parentheses (Zagubiony w głupich nawiasach) czyLots of Irritating Superfluous Parentheses (Wiele irytujących zbytecznych nawiasów)[18]. 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 przezkomputer. Lisp nie jest jednak ograniczony do notacji nawiasowej – możliwe jest takie poszerzenie go, by używał innych, alternatywnych notacji.XMLisp na przykład to rozszerzenie Common Lispu integrujące S-wyrażenia zXML-em.

Bazowanie na wyrażeniach daje językowi dużą elastyczność. Ponieważfunkcje w Lispie są zapisywane jako listy, mogą być manipulowane jak zwykłe dane. Umożliwia to łatwe 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 niemal nieograniczone poszerzanie języka.

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

Wyrażenia są zapisywane jako listy z wykorzystaniemnotacji polskiej. Pierwszym elementem listy jest nazwa formy, czyli funkcji, makra lub specjalnego operatora. Pozostałe elementy listy są argumentami. Na przykład funkcjalist 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 operatoraquote (ang.cytuj), który zapobiega ewaluacji argumentów (stosowanie go do liczb nie jest konieczne, ponieważ 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

(list12(list34))

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

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

(+1234)

zwraca po ewaluacji 10. Odpowiednik wnotacji infiksowej wyglądałby1 + 2 + 3 + 4. Funkcje arytmetyczne mogą przyjmować dowolną ilość argumentów.

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

(ifnil(list12"foo")(list34"bar"))

zwraca(3 4 "bar"). Oczywiście konstrukcja byłaby bardziej użyteczna, gdyby podstawić jakieś nietrywialne wyrażenie w miejscenil.

Wyrażenia lambda

[edytuj |edytuj kod]

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

(lambda(arg)(+arg1))

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

((lambda(arg)(+arg1))5)

zwraca więc6.

Atomy

[edytuj |edytuj kod]

W oryginalnymLISPie dostępne były dwa podstawowetypy danych: atomy i 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ą lub symbolem. Symbol był unikalnym nazwanym obiektem, zapisanym jako alfanumeryczny ciąg znaków w kodzie źródłowym i 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) i liczbę 2.

Podstawową różnicą pomiędzy atomami i listami był fakt, iż atomy były niezmienne i unikalne. Dwa atomy, które pojawiły się w różnych miejscach w kodzie źródłowym, ale były zapisane w dokładnie ten sam sposób, reprezentowały ten sam obiekt, podczas gdy każda lista była oddzielnym obiektem, mogła być modyfikowana niezależnie od innych list i 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 predykatatom dlawstecznej kompatybilności, definiując go jako prawdę dla wszystkiego, co nie było komórkącons (np. listy lub częściowej listy).

Komórki cons i listy

[edytuj |edytuj kod]
Diagram dla listy (42 69 613)

Lista w Lispie jest jednokierunkowa. Każda komórka nazywa sięcons (w Schemepara, ang.pair) i składa się z dwóchwskaźników, zwanychcar icdr.

Spośród wielu struktur danych, jakie mogą być zbudowane za pomocą komórek cons, najbardziej podstawową jest tzw.prawidłowa lista. Prawidłowa lista składa się albo ze specjalnego symbolunil, 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, lub nanil, 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 funkcjecar icdr są również zwanefirst(pierwszy) andrest(reszta), gdy odnosimy się do komórek cons, które są częściami listy (w przeciwieństwie np. do drzewa).

Wynika z tego, że lista w Lispie nie jest podstawowym obiektem, jak na przykładinstancjakontener wC++ czyJavie. 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 można wykonać np. za pomocą kolejnych wywołań funkcjicdr na liście lub za pomocą którejś zfunkcji wyższego rzędu.

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

S-wyrażenia jako reprezentacja list

[edytuj |edytuj kod]

S-wyrażenia reprezentują strukturęlist. Jest kilka sposobów, by opisać tę samą listę za pomocą S-wyrażenia. Komórka cons może być opisana za pomocąnotacji kropkowanych par (ang.dotted-par notation) jako(a . b), gdziea jest wskazywane przez car, natomiastb przez cdr. Prawidłowa lista może być 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 może być zapisana za pomocą kombinacji obydwu tych sposobów – S-wyrażenie(a b c . d) reprezentuje listę trzech komórek cons, z czego w ostatniej cdr wskazuje nad (ta sama lista zapisana za pomocą pełnej notacji wyglądałaby następująco:(a . (b . (c . d)))).

Funkcje przetwarzające listy

[edytuj |edytuj kod]

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

(list12'a3);zwraca (1 2 a 3)
(list1'(23)4);zwraca (1 (2 3) 4)

Ze względu na fakt, iż listy są tworzone z komórek cons, funkcjacons może być 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.

(cons1'(23));zwraca (1 2 3)
(cons'(12)'(34));zwraca ((1 2) 3 4)

Funkcjaappend łączy dwie lub więcej list w jedną. Ponieważ listy w Lispie są listami łączonymi jednostronnie, połączenie dwóch list mazłożonośćO(n).{\displaystyle O(n).}

(append'(12)'(34));zwraca (1 2 3 4)
(append'(123)'()'(a)'(56));zwraca (1 2 3 a 5 6)

Dzielone struktury

[edytuj |edytuj kod]

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

(setffoo(list'a'b'c))(setfbar(cons'x(cdrfoo)))

listyfoo ibar to odpowiednio(a b c) i(x b c). Jednakże ogon(b c) w obu listach to ta sama struktura. Nie jest to kopia, komórki wskazujące nab ic znajdują się 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 na przykład zamiana symboluc nagoose, zaowocuje modyfikacją obydwu:

(setf(thirdfoo)'goose)

Ten kod zmieniafoo na(a b goose), przy okazji jednak zmieniającbar na(x b goose), co może być niepożądane. Takie zachowanie może być przyczyną wielu błędów, a funkcje, które modyfikują przekazywane argumenty, są z tego powodu nazywanedestrukcyjnymi.

Zwolennicyprogramowania 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” – na przykład funkcjaset-car! (czytajset car bang), która zamienia car komórki cons. W Common Lispie funkcje destrukcyjne są powszechne; odpowiednikiemset-car! jest funkcjarplaca (skrót od „replace car”). Niemniej jednak ta funkcja jest dość rzadko używana, ponieważ Common Lisp zawiera specjalne udogodnienie,setf, ułatwiające definiowanie i 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 i cytowanie

[edytuj |edytuj kod]

Lisp ewaluuje wyrażenia wprowadzane przez użytkownika. Symbole i listy zwracają zwykle jakieś prostsze wyrażenia – na przykład symbol zwraca wartość zmiennej, którą nazywa;(+ 2 3) zwraca5. Większość innych form zwraca jednak same siebie – jeżeli wprowadzisz5 do Lispu, zwróci5.

Każde wyrażenie może zostać zacytowane, by zapobiec ewaluacji (jest to konieczne przy wprowadzaniu symboli i list). Tę rolę pełni specjalny operatorquote, lub jego skrót' (pojedynczy znakapostrofu). Na przykład zwykle jeżeli wprowadzisz symbolfoo, 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 znacznie popularniejsze,'foo.

Zarówno Common Lisp, jak i Scheme obsługują również operatorbackquote (zwykle zwanyquasiquote przez użytkowników Scheme) wprowadzany jako znak'. Jedyną różnicą ze zwykłym quote jest fakt, iż backquote pozwala na ewaluację i wstawienie wyniku wyrażenia do cytowanej listy za pomocą operatorówcomma icomma-at. Jeżeli zmiennasnue ma wartość(bar baz), to'(foo ,snue) jest ewaluowane do(foo (bar baz)), podczas gdy'(foo ,@snue) ewaluuje się do(foo bar baz). Backquote jest najczęściej używany przy definiowaniu makr.

Formy ewaluujące się do samych siebie i formy cytowane są odpowiednikiemliterałó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.

(defunpowinna-byc-stala'(jedendwatrzy))(let((zmienna(powinna-byc-stala)))(setf(thirdzmienna)'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ęść implementacji definiuje ją jako błędną (czego wynikiem jestzachowanie nieokreślone w plikach kompilowanych, ponieważ kompilator może połączyć podobne stałe, umieścić je w obszarze pamięci tylko do odczytu itp.). Kiedy takie zachowanie jest zamierzone, odpowiednim sposobem jest użyciedomknięcia.

Zasięg idomknięcia

[edytuj |edytuj kod]

Nowe dialekty Lispu można podzielić na podstawie zasad zasięgu (wiązania zmiennych) – część z nich używazasięgów dynamicznych, częśćstatycznych (leksykalnych). Scheme i Common Lisp używają domyślnie zasięgu leksykalnego, podczas gdy bardziej prymitywne dialekty używane jako języki zagnieżdżone wEmacsie iAutoCADzie używają zasięgów dynamicznych.

Przykład zasięgu dynamicznego w Emacs-Lisp

(defunfoo()(*xx))(defunbar()(let((x10))(message(int-to-string(foo)))))(bar)

Chociaż zmiennax nie została zdefiniowana wewnątrz funkcjifoo, to wywołanie funkcjibar 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 zmiennax wewnątrz funkcjifoo jest niezdefiniowana.

(defunfoo()(*xx))(defunbar()(let((x10))(print(foo))))(bar)

W przypadku funkcji, która została utworzona z zasięgiem dynamicznym, jeśli dana zmienna nie znajduje się wewnątrz funkcji, to jest ona poszukiwana od miejsca, w którym został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 została zdefiniowana. Drugi przypadek jest najczęściej stosowany w innych językach programowania.

Przykład domknięcia leksykalnego w dialekcieScheme

(define(make-counterx)(let((countx))(define(counter)(set!count(+count1))count)counter))(definecount-form-10(make-counter10))(display(count-form-10))(newline)(display(count-form-10))(newline);Powyższy kod wyświetli 11 i 12.

Funkcjamake-counter tworzy nową funkcjęcounter. Chociaż zakres zmiennejcount po zakończeniu wywołania funkcjimake-counter powinien się zakończyć, to funkcjacounter zwrócona przez funkcjęmake-counter ma nadal do niej dostęp, tzn. zmiennacount jest domknięta wewnątrz funkcjicounter.

Kod źródłowy jako lista

[edytuj |edytuj kod]

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

Makra Lispu operują na tych strukturach. Ponieważ kod w Lispie ma taką samą strukturę jak lista, makra mogą być tworzone 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 mogą zrobić z kodem. W przeciwieństwie do tego, wynik parsowania w większości języków jest wyłącznie do użytku przez implementację i niedostępny dla programisty. Makra wC na przykład działają na poziomiepreprocesora, zanim parser jest uruchamiany, i nie mogą 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. Większość systemów Lispu do poważnych zastosowań zawiera jednak równieżkompilator. Kompilator tłumaczy listę dokodu maszynowego lubkodu bajtowego przed wywołaniem.

Ewaluacja i REPL

[edytuj |edytuj kod]

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

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

Funkcjaread akceptuje ciąg znaków zawierający S-wyrażenie jako argument i zwraca odpowiadającą mu listę. Na przykład jeżeli użytkownik wprowadzi ciąg znaków „(+ 1 2)”,read przetłumaczy go na listę z trzema elementami: symbolem+, liczbą 1 i liczbą 2. Ta lista jest również prawidłowym kawałkiem kodu w Lispie, to znaczy może być ewaluowana. Jest to możliwe, ponieważ car listy wskazuje na funkcję+ – operator dodawania, a dokładnie symbol plus, który wskazuje na funkcje, która dodaje liczby. Jest to ważne rozróżnienie, gdyż użytkownik może przypisywać takie same funkcje do prawie dowolnych ciągów symboli.

Funkcjaeval ewaluuje listę, zwracając inną listę jako wynik. Ewaluacja nie jest konieczne interpretacją – część 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 najpierwrekurencyjnie ewaluuje każdy argument w cdr, by następnie z wynikami tych ewaluacji wywołać daną funkcję. W danym przykładzie funkcją jest dodawanie, wywoływane z listą argumentów(1 2) zwraca wynik3. Jest to wynik ewaluacji.

Zadaniem funkcjiprint jest reprezentacja wartości wynikowej w sposób czytelny dla użytkownika. Dla prostych wartości takich jak3, to zadanie jest trywialne. Dla listprint musi przetworzyć całą strukturę i wypisać ją jako S-wyrażenie.

By zaimplementować lispowy REPL, wystarczy zaimplementować te trzy funkcje i funkcję nieskończonejpętli (implementacja funkcjieval może być skomplikowana, gdyż wymaga implementacji wszystkich specjalnych operatorów takich jakif). Gdy to zostanie wykonane, podstawowy REPL może być wprowadzony za pomocą tylko jednej linii kodu:(loop(print(eval(read)))).

Kod w Lispie jestwartościowany zachłannie. WCommon Lispie argumenty są wartościowane w kolejności od lewej do prawej, podczas gdy specyfikacja Scheme tego nie definiuje, pozostawiając kompilatorom możliwość optymalizacji.

Struktury kontrolne

[edytuj |edytuj kod]

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

ProgramiściScheme 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 często spotykane dialekty Lispu mają imperatywne konstrukcje pętli, począwszy od znanej ze Scheme pętlido 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 mogą mieć pewność, że zostaną one zamienione na pętlę. W przeciwieństwie do tegoANSI Common Lisp nie daje[19] określonych wskazówek odnośnie do rekursji ogonowej, co sprawia, że część implementacji traktuje wywołania ogonowe jak zwykłe. W związku z tym fakt, iż używanie rekursji ogonowej jako zamiennika pętli jest w Common Lispie niezalecane[20] nie jest jedynie kwestią stylistyczną, ale również wpływ na to ma wydajność (ponieważ nawet oczywiste wywołanie ogonowe niekoniecznie może być zamienione na pojedynczy skok) i poprawność (ponieważ wiele wywołań ogonowych niezamienionych na skok może grozićprzepełnieniem stosu).

Część wyrażeń w Lispie tospecjalne 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 zawsze są wartościowane – lub, w przypadku pętli, mogą być wartościowane więcej niż raz.

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

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

Często ten sam algorytm może być wyrażony w Lispie zarówno funkcyjnie, jak i imperatywnie. Jak zaznaczono powyżej, Scheme raczej rekomenduje styl funkcyjny, używając rekursji ogonowej i 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, podczas gdy styl Scheme bardziej przypomina języki czysto funkcyjne takie jak np.Haskell.

Ze względu na wczesne specjalizowanie w przetwarzaniu list, Lisp posiada szeroką gamęfunkcji wyższego rzędu służących do przechodzenia po sekwencjach. Często tam, gdzie w innych językach potrzebna byłaby bezpośrednia pętla (jakfor w C), w Lispie to samo zadanie może być wykonane przy pomocy jednej z funkcji wyższego rzędu, podobnie jest w innych funkcyjnych językach programowania.

Dobrym przykładem jest funkcja w Scheme zwanamap, a w Common Lispiemapcar. Po podaniu funkcji i jednej lub kilku list,mapcar stosuje tę funkcję kolejno do elementów list, zbierając wyniki do nowej listy.

(mapcar#'+'(12345)'(1020304050))

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

[edytuj |edytuj kod]

Oto kilka 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.

Obliczeniesilni danej liczby:

(defunfactorial(n)(if(<=n1)1(*n(factorial(-n1)))))

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

(defunfactorial(n&optional(acc1))(if(<=n1)acc(factorial(-n1)(*accn))))

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

(defunfactorial(n)(loopforifrom1tonforfac=1then(*faci)finally(returnfac)))

Funkcja odwracająca listę (wbudowana funkcjareverse wykonuje to samo zadanie, istnieje też destrukcyjny odpowiedniknreverse):

(defun-reverse(l&optionalacc)(if(atoml)acc(-reverse(cdrl)(cons(carl)acc))))

Funkcje wyższego rzędu

[edytuj |edytuj kod]

Funkcje wyższego rzędu są jednym z elementówprogramowania funkcyjnego. W Lispie funkcje można przypisywać do zmiennych przekazywać jako parametry do innych funkcji, mogą być także zwracane jako wartości przez funkcję. Funkcja, która operuje na innych funkcjach, jest nazywana funkcją wyższego rzędu (ang.Higher Order Procedure).

Przykład funkcji w Common Lispie

(print(reduce#'+'(12345678)))>36

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

(defunfun(x)(lambda(ab)(setqx(1+x))(+abx)))(print(reduce(fun0)'(12345678)))

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

(let((x0))(print(reduce(lambda(ab)(setqx(1+x))(+abx))'(12345678))))

W Common Lispie, aby wywołać funkcję przechowywaną w zmiennej lub parametrze, trzeba ją wywołać za pomocą funkcjifuncall lubapply

(defunmake-fun(x)(lambda(ab)(setqx(1+x))(+abx)))(defvarfun(make-fun20))(print(funcallfun23));lub(print(applyfun'(23)))

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

W przypadku dialektuScheme czyClojure nie stosuje się cytowania funkcji, ponieważ oba języki posiadają jedną przestrzeń nazw dla funkcji i zmiennych, nie występuje w nich także funkcjafuncall, ponieważ funkcje wewnątrz zmiennych wywołuje się tak samo jak zwykłe funkcje.

Przykład funkcji w językuClojure

(defnsum[&list](reduce+list))

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

Makra

[edytuj |edytuj kod]

Makra są najpotężniejszym elementem języka Lisp i są dla niego unikalne. Dzięki makrom można dodawać nowe elementy do języka. Makro lispowe w odróżnieniu np. od makr występujących w pre-procesorzeję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 powinno zwrócić kod lispowy także w postaci listy, która zostanie obliczona.

(defmacrodef(nameval)(list'setqnameval))

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 i przecinka i małpy, które obliczają wyrażenie (wyłączają cytowanie), przecinek małpa dodatkowo usuwa otaczające nawiasy (stosuje się to np. wtedy gdy przekazujemy ciąg wyrażeń do makra w parametrze typu &body). Wszystkie trzy znaki są to skróty interpretera Lispu, które są zamieniane na funkcje.

(defmacrowhile(test&bodybody)'(do()((not,test)),@body))

Powyższe makro tworzy nowe wyrażenie implementujące pętlę while. Do testowania makr służą dwie funkcjemacroexpand, która rozwija makrorekurencyjnie aż do napotkania podstawowych wyrażeń i funkcji orazmacroepxand-1, która rozwija makro o jeden poziom.Przykład użycia funkcji w interpreterzeCLISP

(print(macroexpand'(while(<x10)(printx)(setqx(1+x)))))(BLOCKNIL(LETNIL(TAGBODY#:LOOP-11439(IF(NOT(<X10))(GO#:END-11440))(PRINTX)(SETQX(1+X))(PSETQ)(GO#:LOOP-11439)#:END-11440(RETURN-FROMNIL(PROGN)))))

Wywołaniemacroexpand rozwinęło oprócz naszego makra także wbudowane makrodo.

(print(macroexpand-1'(while(<x10)(printx)(setqx(1+x)))))(DONIL((NOT(<X10)))(PRINTX)(SETQX(1+X)))

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

Systemy obiektowe

[edytuj |edytuj kod]

Wiele różnych systemów i modeli obiektowości zostało zbudowanych na podstawie Lispu, między innymi:

  • ObjectLisp[21], lubObject Lisp, używany przezLisp Machines Incorporated
  • LOOPS (Lisp Object-Oriented Programming System) i późniejszyCommonLOOPS
  • Flavors, stworzony naMIT i jego spadkobiercaNew Flavors, używany przezSymbolics
  • Common Lisp Object System,CLOS, następca New Flavors i CommonLOOPS
  • Lush – zorientowany obiektowo język programowania bazujący na Lispie
  • SageCLOS zorientowany obiektowo interfejs doAutoLISPu stworzony przez Ralpha Gimeneza

CLOS obsługujewielokrotne dziedziczenie,multimetody i system „kombinacji metod”. W gruncie rzeczyCommon Lisp zawierający CLOS był pierwszym oficjalnie ustandaryzowanym językiem zorientowanym obiektowo.

Lisp w kulturze

[edytuj |edytuj kod]

Cytaty

[edytuj |edytuj kod]

SQL, Lisp i 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(inne języki),marzec 2007, [7]

Przypuszczam, że powinienem nauczyć się Lispu, ale wydaje się on taki obcy.

Paul Graham(inne języki),listopad 1983, [8]

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 lub Fortranie zawiera tworzone „w biegu”, nieformalnie podane i pełne błędów implementacje połowy cech Common Lispu.

Philip Greenspun, zwykle zwana10. reguła programowania Greenspuna(inne języki)[22]

Proszę nie przyjmować, że Lisp nadaje się tylko do programowania Animacji i Grafiki, SI, Bioinformatyki, B2B i E-Commerce, Zbierania Danych, aplikacji EDA/Semiconductor, Systemów Eksperckich, Finansów, Inteligentnych Agentów, Zarządzania Wiedzą, Mechanicznych CAD, Modelowania i Symulacji, Naturalnych Języków, Optymalizacji, Badań i Rozwoju, Analizy Ryzyka, Planowania, Telekomunikacji i Tworzenia Stron WWW tylko dlatego, że te rzeczy zostały wymienione na liście.

Kent Pitman(inne języki)

Lisp z wyglądu przypomina owsiankę z wmieszanymi obciętymi paznokciami.

Larry Wall, twórcaPerla

Lisp będący najpotężniejszym i najprzyzwoitszym z języków, to język, który projekt GNU zawsze preferuje.

Richard Stallman

Najwspanialszy język programowania, jaki kiedykolwiek zaprojektowano.

Alan Kay

Emacs” jest napisany w Lispie, który jest jedynym pięknym językiem programowania.

Neal Stephenson, In the Beginning...was the Command Line(inne języki)

Programista Lispu zna wartość wszystkich rzeczy, ale nie zna kosztu żadnej z nich

Alan Perlis, Epigrams on Programming(inne języki)

Sądzę, że jest to jedyny język programowania, który można szanować pod względem matematycznym, gdyż tylko o nim jestem w stanie udowadniać twierdzenia!

Gregory Chaitin

Komiksy

[edytuj |edytuj kod]

Lispowi poświęcono kilka komiksów z seriixkcd[23].

Zobacz też

[edytuj |edytuj kod]
Zobacz hasłoLisp w Wikisłowniku

Przypisy

[edytuj |edytuj kod]
  1. Odmiana:M.Lisp,D.Lispu,C.Lispowi,B.Lisp,N.Lispem,M.Lispie.
  2. Transkrypcja AIM-8 Johna McCarthy’ego.
  3. JohnJ. McCarthy JohnJ.,History of Lisp, Artificial Intelligence Laboratory Stanford University, 12 lutego 1979, s. 4 [dostęp 2025-03-12] .
  4. Paul Graham w książceHackers & Painters na stronie 185 przytacza wypowiedź McCarthy’ego:Steve Russell said, look, why don’t I program thiseval..., and I said to him, ho, ho, you’re confusing theory with practice, thiseval is intended for reading, not for computing. But he went ahead and did it. That is, he compiled theeval in my paper intoIBM 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...
  5. Tim Hart and Mike Levin: AI Memo 39-The new compiler. [dostęp 2006-10-13].
  6. David Canfield Smith: MLISP Users Manual. [dostęp 2019-12-01].
  7. Franciscus Faase: The origin of CAR and CDR in LISP. [dostęp 2013-08-20]. (ang.).
  8. Standardy ISLispu.
  9. 36-bitowy rozmiar słowa naPDP-6/PDP-10 został 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 został rozpoczęty w 1963 jako maszyna 24-bitowa. Urosła do 36 bitów dla LISPu, celu projektowego.”)[1].
  10. Informacja o trzecim wydaniu PCL. gigamonkeys.com. [zarchiwizowane ztego adresu (2009-06-20)].
  11. Practical Common Lisp.
  12. Keeping Lisp alive and practical | Reg Developer.
  13. Programming language popularity.
  14. The Road To Lisp Survey. [dostęp 2006-10-13]. [zarchiwizowane ztego adresu (2012-01-06)].
  15. A Retrospective on PAIP.
  16. Common Lisp – Myths and Legends.
  17. Guy L Steele Jr., Richard P Gabriel: The evolution of Lisp. [dostęp 2006-10-12].
  18. The Jargon File – Lisp. [dostęp 2006-10-13].
  19. 3.2.2.3 Semantic Constraints inCommon Lisp HyperSpec.
  20. 4.3. Control Abstraction (Recursion vs. Iteration) wTutorial on Good Lisp Programming Style autorstwaKenta Pitmana iPetera Norvig, sierpień 1993.
  21. Str. 17 z Bobrow 1986.
  22. Phillip Greenspun: Research. [dostęp 2006-10-13].
  23. Lisp,Lisp Cycles,With Apologies to Robert Frost.

Bibliografia

[edytuj |edytuj kod]

Linki zewnętrzne

[edytuj |edytuj kod]
Języki programowania
1GL
2GL/
Język drugiej generacji/
Asembler
3GL /
Język trzeciej generacji
wieloparadygmatowe
proceduralne
istrukturalne
historyczne
inne
obiektowe
funkcyjne
edukacyjne
4GL/
Język czwartej generacji/
Język dziedzinowy
Języki zapytań do baz danych
Generatory raportów / stron
Przetwarzanie danych, analiza i raportowanie
5GL/Logiczne
Ezoteryczne
Inne
Kontrola autorytatywna (wieloparadygmatowy język programowania):
Źródło: „https://pl.wikipedia.org/w/index.php?title=Lisp&oldid=76486369
Kategoria:
Ukryte kategorie:

[8]ページ先頭

©2009-2026 Movatter.jp