Lisp (programovací jazyk) - Lisp (programming language)

Lisp
Lisp logo.svg
Paradigma Multi-paradigma : funkční , procedurální , reflexní , meta
Navrhl John McCarthy
Vývojář Steve Russell , Timothy P. Hart a Mike Levin
Poprvé se objevil 1958 ; Před 63 lety ( 1958 )
Kázeň při psaní Dynamický , silný
Nářečí
Ovlivněn
IPL
Ovlivněn

Lisp (historicky LISP ) je rodina programovacích jazyků s dlouhou historií a výraznou, plně závorkovou předponou . Původně specifikován v roce 1958, Lisp je druhým nejstarším programovacím jazykem na vysoké úrovni . Pouze Fortran je starší, o jeden rok. Lisp se od svých počátků změnil a během jeho historie existovalo mnoho dialektů . Dnes jsou nejznámějšími univerzálními dialekty Lisp raketa , Common Lisp , Scheme a Clojure .

Lisp byl původně vytvořen jako praktický matematický zápis pro počítačové programy , ovlivněné (i když ne původně odvozené) notaci Alonzo Church ‚s lambda kalkulu . Rychle se stal oblíbeným programovacím jazykem pro výzkum umělé inteligence (AI). Jako jeden z nejčasnějších programovacích jazyků, Lisp propagoval mnoho nápadů ve vědě o počítačích , včetně datových struktur dřevin , Automatic Storage Management , dynamické psaní , conditionals , vyššího řádu funkce , rekurze , na vlastním hostování kompilátor , a čtení-eval-print smyčka .

Název LISP pochází z „LISt Processor“. Propojené seznamy jsou jednou z hlavních datových struktur Lispu a zdrojový kód Lispu je tvořen seznamy. Programy Lisp tedy mohou manipulovat se zdrojovým kódem jako datovou strukturou, což vede k makro systémům, které umožňují programátorům vytvářet novou syntaxi nebo nové jazyky specifické pro doménu vložené do Lispu.

Zaměnitelnost kódu a dat dává Lispu jeho okamžitě rozpoznatelnou syntaxi. Veškerý kód programu je zapsán jako výrazy s nebo v závorkových seznamech. Volání funkce nebo syntaktický formulář je zapsán jako seznam s názvem funkce nebo operátora jako prvním a následujícími argumenty; například funkce, fkterá má tři argumenty, by se nazývala jako . (f arg1 arg2 arg3)

Dějiny

John McCarthy vyvinul Lisp v roce 1958, když byl na Massachusetts Institute of Technology (MIT). McCarthy publikoval svůj návrh v článku v Communications of the ACM v roce 1960 s názvem „Rekurzivní funkce symbolických výrazů a jejich výpočet pomocí strojů, část I“. Ukázal, že pomocí několika jednoduchých operátorů a zápisu anonymních funkcí vypůjčených z Church je možné pro algoritmy vytvořit Turingův úplný jazyk.

Information Processing Language byl prvním jazykem AI od roku 1955 nebo 1956 a obsahoval již mnoho konceptů, jako je zpracování seznamu a rekurze, které se začaly používat v Lispu.

McCarthyho původní zápis používal závorkové „ M-výrazy “, které by byly přeloženy do S-výrazů . Například M-výraz car[cons[A,B]]je ekvivalentní S-výrazu . Jakmile byl implementován Lisp, programátoři se rychle rozhodli používat S-výrazy a od M-výrazů bylo upuštěno. M-výrazy se opět vynořily s krátkodobými pokusy MLisp od Horace Enea a CGOL od Vaughana Pratta . (car (cons A B))

Lisp poprvé implementoval Steve Russell na počítači IBM 704 pomocí děrných štítků . Russell přečetl McCarthyho papír a uvědomil si (k McCarthyho překvapení), že funkci Lisp eval lze implementovat do strojového kódu . Výsledkem byl fungující překladač Lisp, který bylo možné použít ke spouštění programů Lisp, nebo přesněji „vyhodnotit výrazy Lisp“.

Primitivními operacemi pro rozkládání seznamů se staly dvě makra sestavovacího jazyka pro IBM 704 : car( Obsah adresní části registrového čísla) a cdr( Obsah dekrementační části registrového čísla), kde „registr“ odkazuje na registry centrálního zpracování počítače jednotka (CPU). Lisp dialekty ještě používají cara cdr( / k ɑːr / a / k ʊ d ər / ) pro operace, které vrátí první položku v seznamu a zbytek v seznamu, v tomto pořadí.

První kompletní kompilátor Lisp, napsaný v Lispu, implementovali v roce 1962 Tim Hart a Mike Levin na MIT. Tento kompilátor představil model Lisp přírůstkové kompilace, ve kterém se kompilované a interpretované funkce mohou libovolně míchat. Jazyk použitý v Hartově a Levinově poznámce je mnohem blíže modernímu stylu Lisp než McCarthyho dřívější kód.

První rutinu sběru odpadků vyvinul postgraduální student MIT Daniel Edwards .

Během 1980 a 1990, byl vyroben velké úsilí sjednotit práci na nových Lisp dialekty (většinou následníků Maclisp jako je ZetaLisp a NIL (New Realizace Lisp) do jednoho jazyka. Nový jazyk, obyčejný Lisp , byl poněkud kompatibilní s dialekty, které nahradila (kniha Common Lisp the Language konstatuje kompatibilitu různých konstrukcí). V roce 1994 vydala ANSI standard Common Lisp „ANSI X3.226-1994 Information Technology Programming Language Common Lisp“.

Časová osa

Napojení na umělou inteligenci

Od počátku byl Lisp úzce spojen s komunitou pro výzkum umělé inteligence , zejména na systémech PDP-10 . Lisp byl použit jako implementace programovacího jazyka Micro Planner , který byl použit ve známém AI systému SHRDLU . V 70. letech, kdy výzkum AI plodil komerční odnože, se výkon stávajících systémů Lisp stal stále větším problémem.

Genealogie a varianty

Během své šedesátileté historie vytvořil Lisp mnoho variací na základní téma jazyka S-expression. Navíc každý daný dialekt může mít několik implementací - například existuje více než tucet implementací Common Lisp .

Rozdíly mezi dialekty mohou být docela viditelné - například Common Lisp používá klíčové slovo defunk pojmenování funkce, ale Scheme používá define. V rámci dialektu, který je standardizován, však odpovídající implementace podporují stejný základní jazyk, ale s různými rozšířeními a knihovnami.

Historicky významné dialekty

  • LISP 1 - První implementace.
  • LISP 1.5 - První široce distribuovaná verze vyvinutá McCarthym a dalšími na MIT. Pojmenován tak, protože obsahoval několik vylepšení původního tlumočníka „LISP 1“, ale nejednalo se o zásadní restrukturalizaci, jak by plánovaný LISP 2 byl.
  • Stanford LISP 1.6-Toto byl nástupce LISP 1.5 vyvinutého ve Stanford AI Lab a široce distribuovaný do systémů PDP-10 s operačním systémem TOPS-10 . Systémy Maclisp a InterLisp byly zastaralé.
  • MACLISP - vyvinutý pro projekt MAC MIT , MACLISP je přímým potomkem LISP 1.5. Běžel na systémech PDP-10 a Multics . MACLISP se později začal nazývat Maclisp a je často označován jako MacLisp. „MAC“ v MACLISP nesouvisí ani s Macintoshem Apple, ani s McCarthy .
  • Interlisp -vyvinutý v BBN Technologies pro systémy PDP-10 s operačním systémem TENEX , později přijat jako „západní pobřeží“ Lisp pro stroje Xerox Lisp jako InterLisp-D . Pro 8bitovou rodinnou počítačovou řadu Atari na bázi 6502 byla vydána malá verze s názvem „InterLISP 65“ . Po nějakou dobu byli Maclisp a InterLisp silnými konkurenty.
  • Franz Lisp - původně projekt Kalifornské univerzity v Berkeley ; později vyvinut Franz Inc. Název je vtipnou deformací jména „ Franz Liszt “ a neodkazuje na Allegro Common Lisp , dialekt Common Lisp prodávaný společností Franz Inc. v posledních letech.
  • XLISP , ze kterého AutoLISP vycházel.
  • Standard Lisp a Portable Standard Lisp byly široce používány a portovány, zejména u systému Computer Algebra System REDUCE.
  • ZetaLisp , také nazývaný Lisp Machine Lisp - používá se na strojích Lisp , přímý potomek Maclispu. ZetaLisp měl na Common Lisp velký vliv.
  • LeLisp je francouzský dialekt Lisp. Jeden z prvních stavitelů rozhraní (nazývaný SOS rozhraní) byl napsán v LeLisp.
  • Schéma (1975).
  • Common Lisp (1984), jak popisuje Common Lisp the Language -konsolidace několika rozdílných pokusů (ZetaLisp, Spice Lisp , NIL a S-1 Lisp ) o vytvoření nástupnických dialektů Maclisp, s podstatnými vlivy z dialektu schématu také . Tato verze Common Lisp byla k dispozici pro rozsáhlé platformy a byla mnohými přijímána jako de facto standard až do vydání ANSI Common Lisp (ANSI X3.226-1994). Mezi nejrozšířenější sub-dialekty Common Lisp patří Steel Bank Common Lisp (SBCL), CMU Common Lisp (CMU-CL), Clozure OpenMCL (nezaměňovat s Clojure!), GNU CLisp a novější verze Franz Lisp; všechny dodržují pozdější standard ANSI CL (viz níže).
  • Dylan byl ve své první verzi mixem schématu se systémem Common Lisp Object System.
  • EuLisp -pokus vyvinout nový efektivní a vyčištěný Lisp.
  • ISLISP -pokus vyvinout nový efektivní a vyčištěný Lisp. Standardizováno jako ISO/IEC 13816: 1997 a později revidováno jako ISO/IEC 13816: 2007: Informační technologie - Programovací jazyky, jejich prostředí a systémová softwarová rozhraní - Programovací jazyk ISLISP .
  • IEEE Scheme - IEEE standard, 1178–1990 (R1995).
  • ANSI Common Lisp - An American National Standards Institute (ANSI) norma pro Common Lisp, vytvořený subkomisí X3J13 , objednaný začít s Common Lisp: Jazyk jako základní dokument a do práce prostřednictvím veřejné konsensu procesu nalézt řešení společných otázek přenositelnost programů a kompatibilita implementací Common Lisp. Ačkoli formálně jde o standard ANSI, implementace, prodej, používání a vliv ANSI Common Lisp byla a stále je vidět na celém světě.
  • ACL2 nebo „A Computational Logic for Applicative Common Lisp“, aplikační (bez vedlejších efektů) varianta Common LISP. ACL2 je jak programovací jazyk, který dokáže modelovat počítačové systémy, tak nástroj, který pomáhá prokazovat vlastnosti těchto modelů.
  • Clojure , nedávný dialekt Lispu, který se kompiluje do virtuálního stroje Java a má zvláštní zaměření na souběžnost .
  • Game Oriented Assembly Lisp (nebo GOAL) je programovací jazyk videohry, který vyvinul Andy Gavin a tým Jak a Daxter z Naughty Dog . Byl napsán pomocí Allegro Common Lisp a použit při vývoji celé série her Jak a Daxter .
  • Chialisp, dialekt na vysoké úrovni sestavující až do CLVM, programovacího prostředí v řetězci v blockchainu Chia

2000 do současnosti

Poté, co v devadesátých letech minulého století poněkud poklesl, zaznamenal Lisp po roce 2000 oživení zájmu. Většina nových aktivit byla zaměřena na implementace Common Lisp , Scheme , Emacs Lisp , Clojure a Racket a zahrnuje vývoj nových přenosných knihoven a aplikací.

Mnoho nových programátorů Lisp bylo inspirováno spisovateli jako Paul Graham a Eric S.Raymond, aby sledovali jazyk, který ostatní považovali za zastaralý. Noví programátoři Lispu často popisují jazyk jako zážitek otevírající oči a tvrdí, že jsou podstatně produktivnější než v jiných jazycích. Toto zvýšení povědomí může být v kontrastu s „ zimou AI “ a Lispovým krátkým ziskem v polovině 90. let.

Od roku 2010 existovalo jedenáct aktivně udržovaných implementací Common Lisp. Scieneer Common Lisp je nová komerční implementace vidlicová z CMUCL s prvním vydáním v roce 2002.

Open source komunita vytvořila novou podpůrnou infrastrukturu: CLiki je wiki, který sbírá Common Lisp související informace, na Common Lisp adresář seznamy zdrojů, #lisp je populární IRC kanál a umožňuje sdílení a komentování kousky kódu (s podporou podle lisppaste , IRC bot napsaný v Lispu), Planet Lisp sbírá obsahy různých Lisp souvisejících blogů, na LispForum uživatelé diskutovat o tématech, Lisp, Lispjobs je služba pro vyhlašování pracovní nabídky a tam je týdenní zpravodajství, Weekly Lisp News . Common-lisp.net je web hostující open source projekty Common Lisp. Quicklisp je správce knihoven pro Common Lisp.

Padesát let Lispu (1958–2008) se slavilo na LISP50@OOPSLA. V Bostonu, Vancouveru a Hamburku se pravidelně scházejí místní uživatelé. Mezi další akce patří European Common Lisp Meeting, European Lisp Symposium a International Lisp Conference.

Komunita Scheme aktivně udržuje přes dvacet implementací . V roce 2000 (desetiletí) bylo vyvinuto několik významných nových implementací (Chicken, Gambit, Gauche, Ikarus, Larceny, Ypsilon). Revidovaná zpráva 5 o standardu schématu Algorithmic Language Scheme byla v komunitě Scheme široce přijímána. Proces žádosti o implementaci schématu vytvořil pro Scheme mnoho kvazi standardních knihoven a rozšíření. Komunita uživatelů jednotlivých implementací Scheme se stále rozrůstá. V roce 2003 byl zahájen nový proces standardizace jazyků, který v roce 2007 vedl ke standardu R 6 RS Scheme. Zdá se, že akademické využití Scheme pro výuku informatiky poněkud upadlo. Některé univerzity již ve svých úvodních kurzech informatiky nepoužívají Scheme; MIT nyní používá Python místo Scheme pro svůj bakalářský program počítačové vědy a masivní otevřený online kurz MITx.

Existuje několik nových dialektů Lisp: Arc , Hy , Nu , Liskell a LFE (Lisp Flavored Erlang). Analyzátor pro Julii je implementován v Femtolisp, dialektu schématu (Julia je inspirována schématem, které je zase dialektem Lisp).

V říjnu 2019 vydal Paul Graham specifikaci pro Bel , „nový dialekt Lispu“.

Hlavní dialekty

Common Lisp a Scheme představují dva hlavní proudy vývoje Lisp. Tyto jazyky ztělesňují výrazně odlišné možnosti designu.

Common Lisp je nástupcem Maclispu . Primárními vlivy byly Lisp Machine Lisp , Maclisp, NIL , S-1 Lisp , Spice Lisp a Scheme. Má mnoho funkcí Lisp Machine Lisp (velký Lisp dialekt používaný k programování Lisp Machines ), ale byl navržen tak, aby byl efektivně implementovatelný na jakémkoli osobním počítači nebo pracovní stanici. Common Lisp je univerzální programovací jazyk, a proto má velký jazykový standard zahrnující mnoho vestavěných datových typů, funkcí, maker a dalších jazykových prvků a objektový systém ( Common Lisp Object System ). Společný Lisp si také půjčil určité funkce ze Scheme, jako je lexikální rozsah a lexikální uzávěry . Pro cílení na různé platformy, jako je LLVM , virtuální stroj Java , x86-64, PowerPC, Alpha, ARM, Motorola 68000 a MIPS, a operační systémy jako Windows, macOS, Linux, Solaris, FreeBSD, jsou k dispozici běžné implementace Lisp NetBSD, OpenBSD, Dragonfly BSD a Heroku.

Scheme je staticky vymezený a náležitě rekurzivní dialekt programovacího jazyka Lisp, který vynalezli Guy L. Steele, Jr. a Gerald Jay Sussman . Byl navržen tak, aby měl výjimečně jasnou a jednoduchou sémantiku a několik různých způsobů vytváření výrazů. Scheme, navržené asi o deset let dříve než Common Lisp, je více minimalistickým designem. Má mnohem menší sadu standardních funkcí, ale s určitými implementačními funkcemi (jako je optimalizace koncového volání a úplné pokračování ), které nejsou v Common Lisp specifikovány. Široká škála programovacích paradigmat, včetně imperativních, funkčních a stylů předávání zpráv, nachází ve Scheme pohodlný výraz. Režim dále vyvíjet s řadou norem (revidované n Zpráva o Scheme algoritmické Language) a sérii schématu žádostí o realizaci .

Clojure je nedávný dialekt jazyka Lisp, který se zaměřuje hlavně na virtuální stroj Java a Common Language Runtime (CLR), Python VM, Ruby VM YARV a kompilaci do JavaScriptu . Je navržen tak, aby byl pragmatickým obecným jazykem. Clojure čerpá od Haskella značné vlivy a klade velmi silný důraz na neměnnost. Clojure poskytuje přístup k rámcům a knihovnám Java s volitelnými typy tipů a odvozováním typů , takže volání do Javy se může vyhnout reflexi a umožnit rychlé primitivní operace. Clojure není navržen tak, aby byl zpětně kompatibilní s jinými dialekty Lisp.

Lisp dialekty se dále používají jako skriptovací jazyky v mnoha aplikacích, přičemž nejznámější jsou Emacs Lisp v editoru Emacs , AutoLISP a později Visual Lisp v AutoCADu , Nyquist v Audacity a Scheme v LilyPond . Díky potenciálně malé velikosti užitečného tlumočníka schémat je obzvláště populární pro vestavěné skriptování. Mezi příklady patří SIOD a TinyScheme , které byly úspěšně vloženy do obrazového procesoru GIMP pod obecným názvem „Script-fu“. LIBREP, interpret Lispu Johna Harpera původně založený na jazyce Emacs Lisp , byl vložen do správce oken Sawfish .

Standardizované dialekty

Lisp má oficiálně standardizované dialekty: R6RS Scheme , R7RS Scheme , IEEE Scheme, ANSI Common Lisp a ISO ISLISP .

Jazykové inovace

Lisp byl prvním jazykem, kde je struktura programového kódu věrně a přímo zastoupena ve standardní datové struktuře - kvalitě, které se později přezdívá „ homoiconicita “. Funkce Lisp lze tedy manipulovat, měnit nebo dokonce vytvářet v programu Lisp bez manipulací nižší úrovně. To je obecně považováno za jednu z hlavních výhod jazyka s ohledem na jeho výrazovou sílu a činí jazyk vhodným pro syntaktická makra a metakruhové hodnocení .

Podmíněnou syntaxi if -then -else vymyslel McCarthy v kontextu Fortranu. Navrhl jeho zařazení do ALGOL , ale nebylo to součástí specifikace Algol 58 . Pro Lisp použil McCarthy obecnější kondiční strukturu. Algol 60 se ujal, když - pak - jinak, a popularizoval to.

Lisp hluboce ovlivnil Alana Kaye , vedoucího výzkumného týmu, který vyvinul Smalltalk v Xerox PARC ; a zase byl Lisp ovlivněn Smalltalkem, přičemž pozdější dialekty přebíraly objektově orientované programovací funkce (třídy dědičnosti, zapouzdřující instance, předávání zpráv atd.) v 70. letech minulého století. Flavors objekt systém zavedený koncept vícenásobné dědičnosti a mixin . Common Lisp Object System poskytuje vícenásobnou dědičnost, multimethods s roztroušenou odeslání a prvotřídní všeobecné funkce , poskytující flexibilní a silná forma dynamický výběr . Sloužil jako šablona pro mnoho dalších objektových systémů Lisp (včetně schématu ), které jsou často implementovány prostřednictvím protokolu metaobject , což je reflexní metakruhový design, ve kterém je objektový systém definován sám o sobě: Lisp byl po Smalltalk teprve druhým jazykem (a je stále jedním z mála jazyků), který vlastní takový metaobjektový systém. O mnoho let později Alan Kay navrhl, že v důsledku soutoku těchto funkcí mohou být za správně koncipované objektově orientované programovací systémy považovány pouze Smalltalk a Lisp.

Lisp představil koncept automatického shromažďování odpadků , ve kterém systém chodí po hromadě a hledá nevyužitou paměť. Pokrok v moderních sofistikovaných algoritmech shromažďování odpadků, jako je generační sběr odpadu, byl stimulován jeho použitím v Lispu.

Edsger W. Dijkstra ve své přednášce Turing Award z roku 1972 řekl:

"S několika velmi základními principy u jeho založení, [LISP] prokázal pozoruhodnou stabilitu. Kromě toho byl LISP nositelem značného počtu v jistém smyslu našich nejpropracovanějších počítačových aplikací. LISP byl žertem popsán jako" nejinteligentnější způsob zneužití počítače. “Myslím, že tento popis je velkým komplimentem, protože přenáší plnou příchuť osvobození: pomohl řadě našich nejnadanějších spoluobčanů při přemýšlení o dříve nemožných myšlenkách.“

Především proto, že jeho požadavky na zdroje s ohledem na časné počítačového hardwaru (včetně časných mikroprocesorů), Lisp nestal tak populární ven z AI společenství jako Fortran a ALGOL -descended C jazyk. Vzhledem ke své vhodnosti pro komplexní a dynamické aplikace si Lisp v roce 2010 užívá obnovení zájmu.

Syntaxe a sémantika

Poznámka : Příklady tohoto článku jsou napsány v jazyce Common Lisp (ačkoli většina z nich je také platná ve schématu ).

Symbolické výrazy (S-výrazy)

Lisp je výrazově orientovaný jazyk . Na rozdíl od většiny ostatních jazyků se nerozlišuje mezi „výrazy“ a „prohlášeními“ ; veškerý kód a data jsou zapsána jako výrazy. Když je výraz vyhodnocen , vytvoří hodnotu (v Common Lisp, případně více hodnot), kterou pak lze vložit do jiných výrazů. Každá hodnota může být libovolný datový typ.

McCarthyho práce z roku 1958 představila dva typy syntaxe: Symbolické výrazy ( S-výrazy , sexps), které zrcadlí vnitřní reprezentaci kódu a dat; a Meta výrazy ( M-výrazy ), které vyjadřují funkce S-výrazů. M-výrazy nikdy nenašly přízeň a téměř všechny Lispy dnes používají S-výrazy k manipulaci s kódem i daty.

Použití závorek je Lispovým nejzjevnějším rozdílem od ostatních rodin programovacích jazyků. Výsledkem je, že studenti již dlouho dávají Lisp přezdívky jako Ztraceni ve stupidních závorkách nebo Spousta dráždivých nadbytečných závorek . Syntaxe S-výrazu je však také zodpovědná za velkou část Lispovy síly: syntaxe je extrémně pravidelná, což usnadňuje manipulaci pomocí počítače. Syntaxe Lispu však není omezena na tradiční notaci v závorkách. Lze jej rozšířit o alternativní zápisy. Například XMLisp je rozšíření Common Lisp, které využívá protokol metaobject k integraci S-výrazů s Extensible Markup Language ( XML ).

Spoléhání se na výrazy dává jazyku velkou flexibilitu. Protože jsou funkce Lisp zapsány jako seznamy, lze je zpracovávat přesně jako data. To umožňuje snadné psaní programů, které manipulují s jinými programy ( metaprogramování ). Mnoho dialektů Lisp využívá tuto funkci pomocí makro systémů, což umožňuje rozšíření jazyka téměř bez omezení.

Seznamy

Seznam Lisp je napsán s prvky oddělenými mezerami a obklopeny závorkami. Například, je seznam, jehož prvky jsou tři atomy , a . Tyto hodnoty se implicitně zadávají: jedná se o dvě celá čísla a datový typ specifický pro Lisp nazývaný „symbol“ a nemusí být jako takové deklarovány. (1 2 foo) 12foo

Prázdný seznam ()je také reprezentován jako speciální atom nil. Toto je jediná entita v Lispu, která je atomem i seznamem.

Výrazy se zapisují jako seznamy pomocí notace předpony . Prvním prvkem v seznamu je název funkce, název makra, výraz lambda nebo název „speciálního operátora“ (viz níže). Zbývající část seznamu jsou argumenty. Funkce například listvrací své argumenty jako seznam, tedy výraz

 (list 1 2 (quote foo))

vyhodnotí do seznamu . "Citát" v předchozím příkladu je "speciální operátor", který vrací svůj argument, aniž by jej vyhodnotil. Všechny výrazy bez uvozovek jsou rekurzivně vyhodnoceny před vyhodnocením obklopujícího výrazu. Například, (1 2 foo)foo

 (list 1 2 (list 3 4))

vyhodnotí do seznamu . Všimněte si, že třetím argumentem je seznam; seznamy lze vnořovat. (1 2 (3 4))

Operátoři

S aritmetickými operátory se zachází podobně. Výraz

 (+ 1 2 3 4)

vyhodnotí na 10. Ekvivalent pod zápisem infixu by byl " ". 1 + 2 + 3 + 4

Lisp nemá ponětí o operátorech, jak jsou implementovány v jazycích odvozených od Algolu. Aritmetické operátory v Lispu jsou variadické funkce (nebo n-ary ), schopné pojmout libovolný počet argumentů. Operátor přírůstku ve stylu C ++ je někdy implementován pod incfsyntaxí dávající název

 (incf x)

ekvivalentní (setq x (+ x 1)), vrací novou hodnotu x.

„Speciální operátoři“ (někdy nazývaní „speciální formuláře“) poskytují řídicí strukturu Lispu. Například speciální operátor ifbere tři argumenty. Pokud je první argument nenulový, vyhodnotí se jako druhý argument; v opačném případě vyhodnotí třetí argument. Tedy výraz

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

hodnotí k . Samozřejmě by to bylo užitečnější, kdyby místo něj byl nahrazen netriviální výraz . (3 4 "bar")nil

Lisp také logické operátory a , nebo a ne . A a nebo operátoři dělat zkrácené vyhodnocování a vrátí se svou první nulu a non-nil argumentu, resp.

 (or (and "zero" nil "never") "James" 'task 'time)

vyhodnotí na „Jamese“.

Lambda výrazy a definice funkcí

Další speciální operátor, lambdase používá k navázání proměnných na hodnoty, které jsou poté vyhodnoceny ve výrazu. Tento operátor se také používá k vytváření funkcí: argumenty lambdajsou seznam argumentů a výraz nebo výrazy, které funkce vyhodnocuje (vrácená hodnota je hodnota posledního vyhodnoceného výrazu). Výraz

 (lambda (arg) (+ arg 1))

vyhodnotí funkci, která při použití vezme jeden argument, naváže ho arga vrátí číslo jedna větší než tento argument. S výrazy lambda se zachází odlišně od pojmenovaných funkcí; jsou vyvolány stejným způsobem. Proto ten výraz

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

hodnotí k 6. Tady děláme funkční aplikaci: spustíme anonymní funkci předáním hodnoty 5.

Pojmenované funkce jsou vytvořeny uložením výrazu lambda do symbolu pomocí makra defun .

 (defun foo (a b c d) (+ a b c d))

(defun f (a) b...)definuje novou funkci pojmenovanou fv globálním prostředí. Je koncepčně podobný výrazu:

 (setf (fdefinition 'f) #'(lambda (a) (block f b...)))

kde setfje makro použito k nastavení hodnoty prvního argumentu na nový objekt funkce. je globální definice funkce pro funkci s názvem . je zkratka pro speciální operátor, vrací funkční objekt. fdefinition 'ffdefinitionf#'function

Atomy

V původním LISP existovaly dva základní datové typy : atomy a seznamy. Seznam byl konečný uspořádaný sled prvků, kde každý prvek je buď atom nebo seznam a atom byl číslo nebo symbol. Symbol byl v podstatě jedinečná pojmenovaná položka, zapsaná jako alfanumerický řetězec ve zdrojovém kódu a používaná buď jako název proměnné, nebo jako datová položka v symbolickém zpracování . Seznam například obsahuje tři prvky: symbol , seznam a číslo 2. (FOO (BAR 1) 2)FOO(BAR 1)

Zásadní rozdíl mezi atomy a seznamy byl v tom, že atomy jsou neměnné a jedinečné. Dva atomy, které se ve zdrojovém kódu objevily na různých místech, ale byly napsány přesně stejným způsobem, představovaly stejný objekt, zatímco každý seznam byl samostatným objektem, který bylo možné měnit nezávisle na jiných seznamech a od ostatních seznamů je bylo možné odlišit operátory porovnání.

Jak bylo v pozdějších dialektech Lisp zavedeno více datových typů a vyvíjely se programovací styly , koncept atomu ztratil na důležitosti. Mnoho dialektů si stále zachovalo predikátní atom pro kompatibilitu se staršími daty , což ho definovalo pro jakýkoli objekt, který není proti.

Conses a seznamy

Schéma rámečku a ukazatele pro seznam (42 69 613)

Seznam Lisp je implementován jako jednotlivě propojený seznam . Každá buňka tohoto seznamu se nazývá nevýhody (ve schématu dvojice ) a skládá se ze dvou ukazatelů , které se nazývají auto a cdr . Ty jsou příslušně ekvivalentní polí dataa nextpopsaným v seznamu propojeném s článkem .

Z mnoha datových struktur, které lze vybudovat z buněk záporů, se jedna z nejzákladnějších nazývá správný seznam . Správný seznam je buď speciální nil(prázdný seznam) symbol, nebo nevýhody, ve kterých carbody na vztažný bod (což může být jiná struktura záporů, například seznam), a cdrbody do jiného vlastního seznamu.

Pokud je daná nevýhoda brána jako vedoucí propojeného seznamu, pak jeho auto ukazuje na první prvek seznamu a jeho cdr ukazuje na zbytek seznamu. Z tohoto důvodu jsou cari cdrfunkce jsou také nazývány first, a restkdyž se odkazuje na conses, které jsou součástí propojeného seznamu (spíše než, řekněme, strom).

Seznam Lisp tedy není atomový objekt, jako by to byla instance třídy kontejneru v C ++ nebo Javě. Seznam není nic jiného než souhrn spojených odkazů. Proměnná, která odkazuje na daný seznam, je jednoduše ukazatelem na první nevýhody v seznamu. Procházení seznamu lze provést cdring dolů v seznamu; to znamená, že po sobě jdoucí cdrs navštívíte všechny nevýhody seznamu; nebo pomocí některé z několika funkcí vyššího řádu k mapování funkce v seznamu.

Protože jsou konzoly a seznamy v systémech Lisp tak univerzální, je běžnou mylnou představou, že jsou jedinou datovou strukturou Lispu. Ve skutečnosti všichni kromě nejjednodušších Lisps mají jiné datové struktury, jako jsou vektory ( pole ), hashovací tabulky , struktury atd.

S-výrazy představují seznamy

Výrazy v závorkách S představují propojené struktury seznamu. Existuje několik způsobů, jak reprezentovat stejný seznam jako výraz S. Nevýhody lze zapsat tečkovanou dvojicí jako : kde je auto a cdr. Delší řádný seznam může být zapsán v notovém páru. To je obvykle zkráceno jako v zápisu seznamu . Nesprávný seznam může být napsán v kombinaci těchto dvou - pokud jde o seznam tří konzes, jejichž poslední cdr je (tj. Seznam v plně určené formě). (a . b)ab(a . (b . (c . (d . nil))))(a b c d)(a b c . d)d(a . (b . (c . d)))

Postupy zpracování seznamu

Lisp poskytuje mnoho vestavěných postupů pro přístup a ovládání seznamů. Seznamy lze vytvořit přímo pomocí listprocedury, která vezme libovolný počet argumentů a vrátí seznam těchto argumentů.

 (list 1 2 'a 3)
 ;Output: (1 2 a 3)
 (list 1 '(2 3) 4)
 ;Output: (1 (2 3) 4)

Vzhledem ke způsobu, jakým jsou seznamy konstruovány z párů proti , lze conspostup použít k přidání prvku na začátek seznamu. Všimněte si toho, že conspostup je asymetrický v tom, jak zpracovává argumenty seznamu, protože jsou konstruovány seznamy.

 (cons 1 '(2 3))
 ;Output: (1 2 3)
 (cons '(1 2) '(3 4))
 ;Output: ((1 2) 3 4)

appendProcedura připojí dva (nebo více) seznamy k sobě navzájem. Protože seznamy Lisp jsou propojené seznamy, připojování dvou seznamů má časovou složitost bez příznaků

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

Sdílená struktura

Seznamy Lisp, které jsou jednoduchými propojenými seznamy, mohou navzájem sdílet strukturu. To znamená, že dva seznamy mohou mít stejný ocas nebo konečnou posloupnost konsensů. Například po provedení následujícího kódu Common Lisp:

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

seznamy fooa barjsou a . Ocas má však v obou seznamech stejnou strukturu. Není to kopie; buňky záporů směřující na a jsou ve stejných paměťových umístěních pro oba seznamy. (a b c)(x b c)(b c)bc

Dramatické zlepšení výkonu může přinést spíše sdílení struktury než kopírování. Tato technika však může interagovat nežádoucími způsoby s funkcemi, které mění seznamy, které jim byly předány jako argumenty. Změna jednoho seznamu, například nahrazením seznamu ca goose, ovlivní druhý:

 (setf (third foo) 'goose)

To se změní foona , ale tím se také změní na - možná neočekávaný výsledek. To může být zdrojem chyb a funkce, které mění jejich argumenty, jsou zdokumentovány jako destruktivní právě z tohoto důvodu. (a b goose)bar(x b goose)

Příznivci funkčního programování se vyhnou destruktivním funkcím. V dialektu Scheme, který upřednostňuje funkční styl, jsou názvy destruktivních funkcí označeny varovným vykřičníkem nebo „třeskem“ - například set-car!(čti nastavený automobilový třesk ), který nahrazuje vůz proti. V dialektu Common Lisp jsou destruktivní funkce samozřejmostí; ekvivalent set-car!je pojmenován rplacapro „vyměnit auto“. Tato funkce se však vidí jen zřídka, protože Common Lisp obsahuje speciální zařízení, setfkteré usnadňuje definování a používání destruktivních funkcí. Častým stylem v Common Lisp je psaní kódu funkčně (bez destruktivních volání) při prototypování, pak přidání destruktivních volání jako optimalizace tam, kde je to bezpečné.

Sebehodnocující formuláře a citace

Lisp vyhodnocuje výrazy, které zadává uživatel. Symboly a seznamy se vyhodnocují podle jiného (obvykle jednoduššího) výrazu - například symbol se vyhodnotí podle hodnoty proměnné, kterou pojmenuje; hodnotí k . Většina ostatních forem se však hodnotí sama: pokud vstoupí do Lispu, vrátí se . (+ 2 3)555

Jakýkoli výraz lze také označit, aby se zabránilo jeho vyhodnocení (jak je nutné pro symboly a seznamy). To je role quotespeciálního operátoru, respektive jeho zkratka '(jedna uvozovka). Například obvykle při zadání symbolu foovrátí hodnotu odpovídající proměnné (nebo chybu, pokud taková proměnná neexistuje). Chcete -li odkazovat na doslovný symbol, zadejte nebo obvykle . (quote foo)'foo

Common Lisp i Scheme také podporují operátor backquote ( v schématu nazývaný kvazikvot ), zadaný `znakem ( vážný přízvuk ). To je téměř stejná jako obyčejný citát, kromě toho, že umožňuje výrazy, které mají být vyhodnoceny a jejich hodnoty interpolované do citovaného seznamu s čárkou , konec citátu a čárkami při ,@ sestřihu operátorů. Pokud má proměnná snuehodnotu, pak se vyhodnotí na , zatímco vyhodnotí na . Zpětná uvozovka se nejčastěji používá při definování rozšíření makra. (bar baz)`(foo ,snue)(foo (bar baz))`(foo ,@snue)(foo bar baz)

Formy s vlastním hodnocením a citované formy jsou Lispovým ekvivalentem doslovných hodnot. Může být možné upravit hodnoty (měnitelných) literálů v programovém kódu. Pokud například funkce vrací citovaný formulář a kód, který funkci volá, formulář upraví, může to změnit chování funkce při následných vyvoláních.

(defun should-be-constant ()
  '(one two three))

(let ((stuff (should-be-constant)))
  (setf (third stuff) 'bizarre))   ; bad!

(should-be-constant)   ; returns (one two bizarre)

Úprava takto citovaného formuláře je obecně považována za špatný styl a je definována ANSI Common Lisp jako chybná (což má za následek „nedefinované“ chování v kompilovaných souborech, protože kompilátor souborů může slučovat podobné konstanty, vkládat je do paměti chráněné proti zápisu, atd.).

Lispovu formalizaci citace zaznamenal Douglas Hofstadter (v Gödel, Escher, Bach ) a další jako příklad filozofické myšlenky sebereference .

Rozsah a uzavření

Rodina Lispů se dělí na použití dynamického nebo statického (aka lexikálního) rozsahu . Clojure, Common Lisp a Scheme ve výchozím nastavení používají statické rozsahy , zatímco newLISP , Picolisp a integrované jazyky v Emacsu a AutoCADu používají dynamické rozsahy. Od verze 24.1 používá Emacs dynamické i lexikální rozsahy.

Struktura seznamu programového kódu; vykořisťování makry a kompilátory

Zásadním rozdílem mezi jazykem Lisp a jinými jazyky je, že v jazyce Lisp je textová reprezentace programu jednoduše čitelný pro popis stejných interních datových struktur (propojené seznamy, symboly, číslo, znaky atd.), Které by používaly základní systém Lisp.

Lisp toho využívá k implementaci velmi výkonného makrosystému. Stejně jako ostatní jazyky maker, jako je C , makro vrací kód, který lze poté zkompilovat. Na rozdíl od maker C jsou však makra funkcí Lisp, a tak mohou využívat plný výkon Lispu.

Kromě toho, protože kód Lisp má stejnou strukturu jako seznamy, lze makra sestavovat s jakoukoli funkcí zpracování seznamu v daném jazyce. Stručně řečeno, cokoli, co může Lisp udělat s datovou strukturou, makra Lisp mohou udělat pro kód. Naproti tomu ve většině ostatních jazyků je výstup analyzátoru čistě interní pro implementaci jazyka a programátor s ním nemůže manipulovat.

Tato funkce usnadňuje vývoj efektivních jazyků v jazycích. Například Common Lisp Object System lze implementovat čistě jako jazykové rozšíření pomocí maker. To znamená, že pokud aplikace potřebuje jiný mechanismus dědičnosti, může použít jiný objektový systém. To je v naprostém kontrastu s většinou ostatních jazyků; například Java nepodporuje vícenásobnou dědičnost a neexistuje žádný rozumný způsob, jak ji přidat.

V zjednodušujících implementacích Lisp je tato struktura seznamu přímo interpretována ke spuštění programu; funkce je doslova částí struktury seznamu, kterou interpret při jejím provádění prochází. Většina podstatných systémů Lisp však také obsahuje kompilátor. Kompilátor převede strukturu seznamu na strojový kód nebo bytecode pro provedení. Tento kód lze spustit stejně rychle jako kód kompilovaný v konvenčních jazycích, jako je C.

Makra se před krokem kompilace rozšiřují, a nabízejí tak několik zajímavých možností. Pokud program potřebuje předpočítanou tabulku, pak makro může vytvořit tabulku v době kompilace, takže kompilátor potřebuje pouze výstup tabulky a nemusí volat kód pro vytvoření tabulky za běhu. Některé implementace Lisp mají dokonce mechanismus, eval-whenkterý umožňuje, aby byl kód přítomen během kompilace (když to makro potřebuje), ale nebyl přítomen v emitovaném modulu.

Vyhodnocení a smyčka čtení – eval – tisk

Jazyky Lisp se často používají s interaktivním příkazovým řádkem , který lze kombinovat s integrovaným vývojovým prostředím (IDE). Uživatel zadá výrazy na příkazovém řádku nebo nasměruje IDE, aby je přeneslo do systému Lisp. Lisp přečte zadané výrazy, vyhodnotí je a vytiskne výsledek. Z tohoto důvodu se příkazový řádek Lisp nazývá smyčka čtení – eval – print ( REPL ).

Základní operace REPL je následující. Toto je zjednodušující popis, který vynechává mnoho prvků skutečného Lispu, jako jsou citace a makra.

readFunkce přijímá textové S-výrazy jako vstup, a analyzuje je do vnitřní datové struktury. Pokud například zadáte text na výzvu, přeloží se do propojeného seznamu se třemi prvky: symbolem , číslem 1 a číslem 2. Stává se, že tento seznam je také platným kódem Lisp; to znamená, že to lze vyhodnotit. Důvodem je, že vůz seznamu pojmenovává funkci - operaci sčítání. (+ 1 2)read+

Všimněte si, že a foobude čten jako jeden symbol. 123bude čten jako číslo sto dvacet tři. "123"bude čten jako řetězec "123".

evalFunkce vyhodnocuje data, vrací nula nebo více jiných dat Lisp jako výsledek. Hodnocení nemusí znamenat interpretaci; některé systémy Lisp kompilují každý výraz do nativního strojového kódu. Je však jednoduché popsat hodnocení jako interpretaci: Chcete -li vyhodnotit seznam, jehož vůz pojmenuje funkci, evalnejprve vyhodnotí každý z argumentů uvedených v jeho cdr a poté použije funkci na argumenty. V tomto případě je funkce sčítání a její použití v seznamu argumentů dává odpověď . Toto je výsledek hodnocení. (1 2)3

Symbol se foovyhodnotí na hodnotu symbolu foo. Data jako řetězec "123" jsou vyhodnocena na stejný řetězec. Seznam se vyhodnotí do seznamu (1 2 3). (quote (1 2 3))

Úkolem printfunkce je reprezentovat výstup pro uživatele. Pro jednoduchý výsledek, jako 3je tento, je triviální. Výraz, který by vyhodnotil část struktury seznamu, by vyžadoval printprocházení seznamu a jeho vytištění jako S-výraz.

K implementaci Lisp REPL je nutné implementovat pouze tyto tři funkce a funkci s nekonečnou smyčkou. (Samozřejmě, že realizace evalbude složité, protože musí také realizovat všechny speciální operátory, jako je ifči lambda.) Toto hotový, základní REPL je jeden řádek kódu: . (loop (print (eval (read))))

Lisp REPL obvykle také poskytuje úpravy vstupů, historii vstupů, zpracování chyb a rozhraní pro ladicí program.

Lisp je obvykle hodnocen horlivě . V Common Lisp jsou argumenty vyhodnocovány v aplikačním pořadí ('nejzazší nejvnitřnější'), zatímco ve schématu není pořadí argumentů definováno, což ponechává prostor pro optimalizaci překladačem.

Řídicí struktury

Lisp měl původně velmi málo řídicích struktur, ale během vývoje jazyka bylo přidáno mnoho dalších. (Lispův původní podmíněný operátor, condje předchůdcem pozdějších if-then-elsestruktur.)

Programátoři v dialektu Scheme často vyjadřují smyčky pomocí rekurze ocasu . Společnost schématu v akademické informatice vedla některé studenty k názoru, že rekurze ocasu je jediným nebo nejběžnějším způsobem, jak psát iterace v Lispu, ale to není správné. Všechny často vídané dialekty Lisp mají iterační konstrukty imperativního stylu, od Schemeovy dosmyčky až po složité výrazy Common Lisploop . Klíčovým problémem, který z toho dělá spíše objektivní než subjektivní záležitost, je to, že Scheme klade specifické požadavky na zpracování ocasních volání , a proto je pro Scheme obecně podporováno použití rekurze ocasu tím, že tato praxe je výslovně podporována definice jazyka. Naproti tomu ANSI Common Lisp nevyžaduje optimalizaci běžně označovanou jako eliminace ocasu. To znamená, že skutečnost, že ocas rekurzivní styl jako příležitostný náhrada za použití tradičních iteračních konstrukty (jako je například do, dolistnebo loop), se nedoporučuje v Common Lisp není jen otázkou stylistické preference, ale potenciálně jeden z účinnosti (od zjevné ocasu call in Common Lisp se nemusí kompilovat jako jednoduchý skok ) a správnost programu (protože rekurze ocasu může zvýšit využití zásobníku v Common Lispu, hrozí přetečení zásobníku ).

Některé řídicí struktury Lisp jsou speciální operátory , ekvivalentní syntaktickým klíčovým slovům jiných jazyků. Výrazy používající tyto operátory mají stejný povrchový vzhled jako volání funkcí, ale liší se v tom, že argumenty nemusí být nutně vyhodnoceny - nebo v případě výrazu iterace mohou být vyhodnoceny více než jednou.

Na rozdíl od většiny ostatních hlavních programovacích jazyků umožňuje Lisp implementaci řídicích struktur pomocí jazyka. Několik řídicích struktur je implementováno jako makra Lisp a může je dokonce makro rozšířit programátor, který chce vědět, jak fungují.

Common Lisp i Scheme mají operátory pro nelokální řídicí tok. Rozdíly v těchto operátorech jsou jedny z nejhlubších rozdílů mezi těmito dvěma dialekty. Schéma podporuje pokračování účastníků pomocí call/ccpostupu, který umožňuje programu uložit (a později obnovit) konkrétní místo při provádění. Common Lisp nepodporuje pokračování re-entrantů, ale podporuje několik způsobů zpracování pokračování úniku.

Stejný algoritmus lze často vyjádřit v Lispu buď imperativním, nebo funkčním stylem. Jak již bylo uvedeno výše, Scheme má tendenci upřednostňovat funkční styl pomocí rekurze ocasu a pokračování k vyjádření toku řízení. Imperativní styl je však stále docela možný. Styl preferovaný mnoha programátory Common Lisp se může zdát známější programátorům používaným ve strukturovaných jazycích, jako je C, zatímco styl preferovaný Schemersem se více podobá čistě funkčním jazykům, jako je Haskell .

Vzhledem k rané tradici Lispu při zpracování seznamu má širokou škálu funkcí vyššího řádu vztahujících se k iteraci přes sekvence. V mnoha případech, kdy by byla forv Lispu zapotřebí explicitní smyčka v jiných jazycích (jako smyčka v C), lze stejný úkol provést pomocí funkce vyššího řádu. (Totéž platí pro mnoho funkčních programovacích jazyků.)

Dobrým příkladem je funkce, která se ve Scheme nazývá mapa v Common Lisp se nazývá mapcar. Daná funkce a jeden nebo více seznamů mapcaraplikuje funkci postupně na prvky seznamů v pořadí a výsledky shromažďuje v novém seznamu:

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

To platí +pro každou odpovídající dvojici prvků seznamu, čímž se získá výsledek . (11 22 33 44 55)

Příklady

Zde jsou příklady kódu Common Lisp.

Základní program „ Hello, World! “:

(print "Hello, World!")

Lisp syntaxe se přirozeně hodí k rekurzi. Matematické problémy, jako je výčet rekurzivně definovaných množin, lze v tomto zápisu snadno vyjádřit. Chcete -li například vyhodnotit faktoriál čísla :

(defun factorial (n)
    (if (zerop n) 1
        (* n (factorial (1- n)))))

Alternativní implementace zabere méně místa v zásobníku než předchozí verze, pokud základní systém Lisp optimalizuje rekurzi ocasu :

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

Kontrastní příklady výše s iterační verzí, která používá Common Lisp je loopmakro:

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

Následující funkce obrátí seznam. (Vestavěná reverzní funkce Lispu dělá totéž.)

(defun -reverse (list)
    (let ((return-value))
      (dolist (e list) (push e return-value))
      return-value))

Objektové systémy

Na Lisp, vedle nebo do Lispu byly postaveny různé objektové systémy a modely, včetně:

Viz také

Reference

Další čtení

externí odkazy

Dějiny
Asociace a schůzky
Knihy a návody
Rozhovory
Zdroje