|
narzędziaW innych językach
|
Lisp
Lisp[1] - rodzina języków programowania z długą historią i charakterystyczną składnią. Po raz pierwszy określony w 1958 roku, Lisp jest drugim z kolei pod względem wieku językiem programowania wysokiego poziomu (starszy jest tylko Fortran). Podobnie jak Fortran, Lisp wiele się zmienił w porównaniu z początkami. W historii istniało wiele dialektów Lispu, jednak dziś do najpopularniejszych należą dwa - Common Lisp i Scheme. Lisp został stworzony jako wygodna matematyczna notacja dla programów komputerowych, oparta na rachunku lambda stworzonym przez Alonzo Churcha. Lisp szybko stał się najchętniej wybieranym językiem do badania i rozwoju sztucznej inteligencji. Ponieważ był jednym z pierwszych języków programowania wysokiego poziomu, Lisp wprowadził wiele nowych pomysłów, takich jak struktury drzewiaste, garbage collection, dynamiczne typowanie czy nowe koncepcje w programowaniu obiektowym. Nazwa Lisp pochodzi od List Processing Language. Podstawową struktura danych w Lispie jest lista, kod źródłowy programów w Lispie jest budowany z list. W wyniku tego, programy w Lispie mogą manipulować kodem źródłowym jak zwykłą strukturą danych, co umożliwia pisanie makr, które pozwalają programiście tworzyć nową składnię lub 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 jest w postaci tzw. S-wyrażeń (S-expressions), czyli list otoczonych nawiasami. Wywołanie funkcji, makra lub specjalnej formy ma postać listy z nazwą funkcji lub operatora jako pierwszego elementu listy (car) i argumentów jako następnych (cdr). Na przykład, wywołanie funkcji o nazwie f z argumentami a, b i c może być wykonane za pomocą następującego kodu: (f a b c). [edytuj] HistoriaLisp został wymyślony przez Johna McCarthy'ego w 1958 podczas jego pobytu na MIT. Opublikował jego projekt w artykule "Communications of the ACM" w roku 1960, pod tytułem "Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I" (Rekursywne funkcje wyrażeń symbolicznych i ich maszynowe obliczanie, część I, część II nie została nigdy opublikowana). Pokazał, że za pomocą kilku prostych operatorów i notacji dla funkcji można zbudować prosty język implementujący maszynę Turinga. Pierwszą implementację Lispu wykonał Steve Russel na komputerze IBM 704. Russel przeczytał artykuł McCarthy'ego i doszedł do wniosku (ku zdziwieniu McCarthy'ego), że funkcja eval może zostać zaimplementowana jako interpreter Lispu[2] Pierwszy kompletny kompilator Lispu stworzony w Lispie został napisany w 1962 przez Tima Harta i Mike'a Levina w MIT. [3] Wprowadził on model kompilacji inkrementalnej (ang. incremental compilation), dzięki czemu nie było wprowadzane rozróżnienie na funkcje kompilowane i interpretowane. Język użyty przez Harta i Levina był dużo bliższy nowoczesnemu stylowi Lispu niż wcześniejszy kod McCarthy'ego. [edytuj] Wczesna historiaInformation 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 czy rekurencja. Oryginalna notacja McCarthy'ego używała M-wyrażeń, które potem były przetwarzane do S-wyrażeń, na przykład M-wyrażenie Dwa makra asemblera na maszynie IBM 704 stały się podstawowymi operacjami do przetwarzania list: [edytuj] Genealogia i odmianyPrzez 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 i Scheme 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. [edytuj] Ważne historycznie dialekty
[edytuj] Powiązania ze sztuczną inteligencjąOd swoich początków Lisp był blisko powiązany ze społecznością badającą i rozwijającą sztuczną inteligencję, szczególnie na PDP-10[5]. Lisp został użyty w implementacji języka programowania Micro Planner, który był podstawą znanego systemu SI SHRDLU. W latach 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 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 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 latach 80. i 90. włożono duży wysiłek w zunifikowanie wielu dialektów Lispu (szczególnie dialektów InterLisp, Maclisp, ZetaLisp, and 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. [edytuj] Od 2000Po spadku popularności w latach 90. wywołanym m. in. upowszechnieniem się C++ i silnym marketingiem Javy, jak również brakiem dobrych i wolnych implementacji Lispu, Lisp doświadcza wzrostu zainteresowania od roku 2000. Większość aktywności skupia się wokół stworzenia open source'owych implementacji Common Lispu i zawiera rozwój nowych przenośnych bibliotek i aplikacji. Zainteresowanie to można częściowo zmierzyć poprzez sprzedaż papierowej wersji książki Practical Common Lisp autorstwa Petera Seibela, wstępu do CL dla nowych programistów Lispu, opublikowanej w 2004 roku[6]. Była ona drugą co do popularności książką o programowaniu na Amazon. Aktualnie dostępna jest za darmo w internecie[7]. Można również zauważyć zwiększoną frekwencję na związanych z Lispem konferencjach[8] i aktywność na powiązanych grupach[9]. Wielu nowych programistów Lispu został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 i twierdzą, że dzięki temu stali się dużo bardziej wydajni niż w innych językach[10]. Wpływ na odzyskiwanie popularności przez Lisp mógł mieć również komentarz Petera Norviga[11], autora książekParadigms of AI Programming: Case Studies in Common Lisp i Artificial Intelligence: A Modern Approach, lub Phillip Greenspun, który odniósł sukces biznesowy używając Lispu. [edytuj] Główne nowoczesne dialektyDwoma głównymi dialektami Lispu ogólnego przeznaczenia aktualnie są Common Lisp i Scheme. Języki te reprezentują znacząco różne podejścia projektowe. Common Lisp, bazujący głównie na Maclispie, 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 optymalizacja rekursji ogonowej czy pełne kontynuacje), które niekoniecznie mogą być dostępne w Common Lispie. CL zapożyczył również pewne cechy ze Scheme jak na przykład 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ą Emacs Lisp w Emacsie i AutoLisp w AutoCADzie. [edytuj] Wpływ na świat programowaniaLisp 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. Konstrukcja if-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 przez Algola. Lisp wprowadził również koncepcję dynamicznego typowania i mechanizm garbage collection. Lisp wpłynął na Alana Kaya, który prowadził badania nad Smalltalkiem, by następnie ulec wpływowi Smalltalka poprzez 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 na przyklad Fortran czy C. Nowsze języki, takie jak Java czy Ruby oferują część jego cech, mimo tego nie jest możliwe spójne zaimplementowanie dla nich wszystkich cech Lispu. Zobacz też "The evolution of Lisp"[12], artykuł autorstwa Guya Steele'a Jr. i Richarda Gabriela. [edytuj] Składnia i semantyka
Lisp jest językiem zorientowanym na wyrażenia. W przeciwieństwie do większości innych języków, nie ma w nim podziału na wyrażenia i instrukcje - cały kod i dane są zapisane jako wyrażenia. Wynikiem ewaluacji (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 z 1958 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 jezykó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)[13]. 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 na przykład to rozszerzenie Common Lispu integrujące S-wyrażenia z XMLem. 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ę poprzez użycie systemu makr, które pozwalają na niemal nieograniczone poszerzanie języka. Lista w Lispie jest zapisywana jako elementy rozdzielone białymi znakami i otoczone nawiasami. Na przykład, Wyrażenia są zapisywane jako listy z wykorzystaniem notacji polskiej. Pierwszym elementem listy jest nazwa formy, czyli funkcji, makra lub specjalnego operatora. Pozostałe elementy listy są argumentami. Na przykład, funkcja (list '1 '2 'foo) zwróci listę (list 1 2 (list 3 4)) zostanie ewaluowane do Operatory arytmetyczne są traktowane podobnie, jako że same w sobie są również funkcjami. Wyrażenie (+ 1 2 3 4) zwraca po ewaluacji 10. Odpowiednik w notacji infiksowej wyglądałby Specjalne operatory (zwane również specjalnymi formami) zapewniają Lispowi struktury kontrolne. Na przykład, specjalny operator (if nil (list 1 2 "foo") (list 3 4 "bar")) zwraca [edytuj] Lambda wyrażeniaInny specjalny operator, (lambda (arg) (+ arg 1)) zwraca funkcję, która przy wywołaniu pobiera jeden argument, przypisuje go do ((lambda (arg) (+ arg 1)) 5) zwraca więc [edytuj] AtomyW oryginalnym LISPie dostępne były dwa podstawowe typy 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 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 predykat atom dla wstecznej kompatybilności, definiując go jako prawdę dla wszystkiego, co nie było komórką cons (np. listy lub częściowej listy). [edytuj] Komórki cons i listyLista w Lispie jest jednokierunkowa. Każda komórka nazywa się cons (w Scheme para, ang. pair) i składa się z dwóch wskaźników, zwanych car i cdr. 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 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, lub 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 Wynika z tego, że lista w Lispie nie jest podstawowym obiektem, jak na przykład 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 można wykonać np. za pomocą kolejnych wywołań funkcji 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. [edytuj] S-wyrażenia jako reprezentacja listS-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 [edytuj] Funkcje przetwarzające listyLisp zapewnia wiele wbudowanych funkcji służących do manipulowania listami. Listy mogą być tworzone bezpośrednio za pomocą funkcji (list 1 2 'a 3) ;zwraca (1 2 a 3) (list 1 '(2 3) 4) ;zwraca (1 (2 3) 4) Ze względu na fakt, iż listy są tworzone z komórek cons, funkcja (cons 1 '(2 3)) ;zwraca (1 2 3) (cons '(1 2) '(3 4)) ;zwraca ((1 2) 3 4) Funkcja (append '(1 2) '(3 4)) ;zwraca (1 2 3 4) (append '(1 2 3) '() '(a) '(5 6)) ;zwraca (1 2 3 a 5 6) [edytuj] Dzielone strukturyW 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 w Common Lispie: (setf foo (list 'a 'b 'c)) (setf bar (cons 'x (cdr foo))) listy 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 symbolu (setf (third foo) 'goose) Ten kod zmienia 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" - na przykład funkcja [edytuj] Ewaluowanie form i cytowanieLisp 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; Każde wyrażenie może zostać zacytowane by zapobiec ewaluacji (jest to konieczne przy wprowadzaniu symboli i list). Tę rolę pełni specjalny operator Zarówno Common Lisp jak i Scheme obsługują również operator backquote (zwykle zwany quasiquote przez użytkowników Scheme). wprowadzany jako znak Formy ewaluujące się do samych siebie i 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ęść implementacji definiuje ją jako błędną (czego wynikem jest zachowanie 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życie domknięcia. [edytuj] Zasięg i domknięciaNowe dialekty Lispu można podzielić na podstawie zasad zasięgu - część z nich używa zasię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 w Emacsie i AutoCADzie używają zasięgów dynamicznych. [edytuj] Kod źródłowy jako listaGłówną różnicą mię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 gdy tylko zostają wprowadzone do systemu Lispu, są tłumaczone przez parser (funkcję Makra Lispu operują na tych strukturach. Ponieważ kod w Lispie ma taką samą strukturę jak lista, makra mogą być tworzone za pomocą wszystkich funkcji przetwarzajacych 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 w C na przykład działają na poziomie preprocesora, zanim parser jest uruchamiany, i nie mogą restrukturyzować kodu programu tak jak makra Lispu. W prostych implementacjach Lispu, ta struktura jest bezpośrednio intepretowana 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ę do kodu maszynowego lub bytecode'i przed wywołaniem. [edytuj] Ewaluacja i REPLW 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ń, 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. Funkcja read akceptuje ciąg znaków zawierający S-wyrażenie jako argument i zwraca odpowiadającą mu listę. Na przykład jeżeli wprowadzisz ciąg znaków " Funkcja Zadaniem funkcji By zaimplementować lispowy REPL, wystarczy zaimplementować te trzy funkcje i funkcję nieskończonej [[pętla|pętli] (oczywiście, implementacja funkcji Kod w Lispie jest wartościowany zachłannie. W Common 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. [edytuj] Struktury kontrolneLisp początkowo miał niewiele struktur kontrolnych, wiele zostało dodanych w czasie, gdy język ewoluował. Pierwotny operator warunkowy Lispu, 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 często spotykane dialekty Lispu mają imperatywe konstrukcje pętli, począwszy od znanej ze Scheme pętli 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 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ą funkcji 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ługuje wielowejściowe kontynuacje za pomocą operatora 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 programoania 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, poczas 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 (jak Dobrym przykładem jest funkcja w Scheme zwana (mapcar #'+ '(1 2 3 4 5) '(10 20 30 40 50)) W tym przypadku funkcja [edytuj] PrzykładyOto 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. 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 (defun factorial (n) (loop for i from 1 to n for fac = 1 then (* fac i) finally (return fac))) Funkcja odwracająca listę (wbudowana funkcja (defun -reverse (l &optional acc) (if (atom l) acc (-reverse (cdr l) (cons (car l) acc)))) [edytuj] Systemy obiektoweWiele różnych systemów i modeli obiektowości zostało zbudowanych na podstawie Lispu, między innymi:
CLOS obsługuje wielokrotne dziedziczenie, multimetody i system "kombinacji metod". W gruncie rzeczy Common Lisp zawierający CLOS był pierwszym oficjalnie ustandaryzowanym językiem zorientowanym obiektowo. [edytuj] Cytaty
[edytuj] Komiksy[edytuj] Przypisy
[edytuj] Źródła
[edytuj] Zobacz też[edytuj] Linki zewnętrzne
wieloparadygmatowe: Ada • C++ • Common Lisp • D • Fortran • Icon • JavaScript • Nemerle • Perl • Python • Ruby • Snobol proceduralne i strukturalne: AWK • C • COBOL • Forth • Modula-2 • Oberon • Pascal • PL/SQL • Rey • REXX • sh obiektowe: C# • Eiffel • Java • Object Pascal • Objective-C • PHP • Smalltalk funkcyjne: Erlang • F# • Haskell • Lisp • ML • Ocaml • Scheme inne: ABAP • Asembler • C-- • GAUSS • Lustre • MCPL • SAS 4GL • SQL • Visual Basic • VB.NET • occam • QCL ezoteryczne: INTERCAL • Brainfuck • BeFunge • Unlambda • Malbolge • Whitespace • FALSE • HQ9+ • Shakespeare • Whirl • Ook historyczne: ALGOL • APL • BASIC • Clipper • JAS • Lisp • MUMPS • PLAN • PL/I • PL/M • SAKO • SAS (asembler) • Simula |
||||||||||||