Vanlig lisp - Common Lisp

Vanlig Lisp
Paradigme Multiparadigme : prosessuell , funksjonell , objektorientert , meta , reflekterende , generisk
Familie Lisp
Designet av Scott Fahlman , Richard P. Gabriel , David A. Moon , Kent Pitman , Guy Steele , Dan Weinreb
Utvikler ANSI X3J13 komité
Første gang dukket opp 1984 (for 37 år siden) , 1994 (for 27 år siden) for ANSI Common Lisp ( 1984 ) ( 1994 )
Skrive disiplin Dynamisk , sterk
omfang Leksikalsk, valgfritt dynamisk
OS Kryssplattform
Filnavnutvidelser .lisp, .lsp, .l, .cl, .fasl
Nettsted common-lisp .net
Store implementeringer
Allegro CL , ABCL , CLISP , Clozure CL , CMUCL , ECL , GCL , LispWorks , Scieneer CL , SBCL , Symbolics Common Lisp
Dialekter
CLtL1, CLtL2, ANSI Vanlig Lisp
Påvirket av
Lisp , Lisp Machine Lisp , Maclisp , Scheme , Interlisp
Påvirket
Clojure , Dylan , Emacs Lisp , EuLisp , ISLISP , *Lisp , AutoLisp , Julia , Moose , R , SKILL , SubL

Common Lisp ( CL ) er en dialekt av programmeringsspråket Lisp , utgitt i ANSI- standarddokument ANSI INCITS 226-1994 (S20018) (tidligere X3.226-1994 (R1999) ). The Common Lisp HyperSpec , en hyperkoblet HTML-versjon, er avledet fra ANSI Common Lisp standard.

Common Lisp -språket ble utviklet som en standardisert og forbedret etterfølger av Maclisp . På begynnelsen av 1980-tallet var flere grupper allerede i gang med forskjellige etterfølgere av MacLisp: Lisp Machine Lisp (aka ZetaLisp), Spice Lisp , NIL og S-1 Lisp . Common Lisp søkte å forene, standardisere og utvide funksjonene til disse MacLisp -dialektene. Common Lisp er ikke en implementering, men heller et språk spesifikasjon . Flere implementeringer av Common Lisp-standarden er tilgjengelig, inkludert gratis og åpen kildekode-programvare og proprietære produkter. Common Lisp er et programmeringsspråk for flere formål, med flere paradigmer . Den støtter en kombinasjon av prosessuelle , funksjonelle og objektorienterte programmeringsparadigmer . Som et dynamisk programmeringsspråk letter det evolusjonær og inkrementell programvareutvikling , med iterativ kompilering til effektive kjøretidsprogrammer. Denne inkrementelle utviklingen gjøres ofte interaktivt uten å avbryte den kjørende applikasjonen.

Den støtter også valgfri type annotering og casting, som kan legges til etter behov ved senere profilering og optimaliseringstrinn, slik at kompilatoren kan generere mer effektiv kode. For eksempel fixnumkan inneholde et ubokset heltall i et område som støttes av maskinvaren og implementeringen, noe som tillater mer effektiv aritmetikk enn på store heltall eller vilkårlige presisjonstyper. På samme måte kan kompilatoren fortelles på en per-modul eller per funksjon-basis hvilken type sikkerhetsnivå som ønskes, ved å bruke optimaliseringsdeklarasjoner .

Common Lisp inkluderer CLOS , et objektsystem som støtter multimetoder og metodekombinasjoner. Det implementeres ofte med en Metaobject -protokoll.

Common Lisp kan utvides med standardfunksjoner som Lisp -makroer (kodetransformasjoner) og lesermakroer (inndataparsere for tegn).

Common Lisp gir delvis bakoverkompatibilitet med Maclisp og John McCarthys originale Lisp . Dette gjør at eldre Lisp -programvare kan portes til Common Lisp.

Historie

Arbeidet med Common Lisp startet i 1981 etter et initiativ fra ARPA -sjef Bob Engelmore om å utvikle en enkelt fellesskapsstandard Lisp -dialekt. Mye av det opprinnelige språkdesignet ble gjort via elektronisk post. I 1982 ga Guy L. Steele Jr. den første oversikten over Common Lisp på ACM Symposium i 1982 om LISP og funksjonell programmering.

Den første språkdokumentasjonen ble utgitt i 1984 som Common Lisp the Language (kjent som CLtL1), første utgave. En andre utgave (kjent som CLtL2), utgitt i 1990, innlemmet mange endringer i språket, gjort under ANSI Common Lisp -standardiseringsprosessen: utvidet LOOP -syntaks, Common Lisp Object System, Condition System for feilhåndtering, et grensesnitt til pen skriver og mye mer. Men CLtL2 beskriver ikke den endelige ANSI Common Lisp -standarden og er dermed ikke en dokumentasjon av ANSI Common Lisp. Den endelige ANSI Common Lisp -standarden ble deretter publisert i 1994. Siden har det ikke blitt publisert noen oppdatering av standarden. Ulike utvidelser og forbedringer av Common Lisp (eksempler er Unicode, Concurrency, CLOS-basert IO) har blitt levert av implementeringer og biblioteker.

Syntaks

Felles Lisp er en dialekt av Lisp. Den bruker S-uttrykk for å betegne både kode og datastruktur. Funksjonsanrop, makroskjemaer og spesielle skjemaer skrives som lister, med navnet på operatøren først, som i disse eksemplene:

 (+ 2 2)           ; adds 2 and 2, yielding 4. The function's name is '+'. Lisp has no operators as such.
 (defvar *x*)      ; Ensures that a variable *x* exists,
                   ; without giving it a value. The asterisks are part of
                   ; the name, by convention denoting a special (global) variable. 
                   ; The symbol *x* is also hereby endowed with the property that
                   ; subsequent bindings of it are dynamic, rather than lexical.
 (setf *x* 42.1)   ; Sets the variable *x* to the floating-point value 42.1
 ;; Define a function that squares a number:
 (defun square (x)
   (* x x))
 ;; Execute the function:
 (square 3)        ; Returns 9
 ;; The 'let' construct creates a scope for local variables. Here
 ;; the variable 'a' is bound to 6 and the variable 'b' is bound
 ;; to 4. Inside the 'let' is a 'body', where the last computed value is returned.
 ;; Here the result of adding a and b is returned from the 'let' expression.
 ;; The variables a and b have lexical scope, unless the symbols have been
 ;; marked as special variables (for instance by a prior DEFVAR).
 (let ((a 6)
       (b 4))
   (+ a b))        ; returns 10

Datatyper

Common Lisp har mange datatyper .

Skalar typer

Antall typene inkluderer heltall , forholdstall , flyttall og komplekse tall . Common Lisp bruker bignums for å representere numeriske verdier av vilkårlig størrelse og presisjon. Forholdstypen representerer brøkdeler nøyaktig, et anlegg som ikke er tilgjengelig på mange språk. Common Lisp tvinger automatisk til numeriske verdier blant disse typene etter behov.

The Common Lisp karakter typen er ikke begrenset til ASCII -tegn. De fleste moderne implementeringer tillater Unicode -tegn.

Den symbol typen er vanlig å Lisp språk, men stort sett ukjent utenfor dem. Et symbol er et unikt, navngitt dataobjekt med flere deler: navn, verdi, funksjon, eiendomsliste og pakke. Av disse er verdicelle og funksjonscelle de viktigste. Symboler i Lisp brukes ofte på samme måte som identifikatorer på andre språk: for å holde verdien av en variabel; men det er mange andre bruksområder. Normalt, når et symbol evalueres, returneres verdien. Noen symboler evaluerer for seg selv, for eksempel er alle symbolene i søkeordpakken selvevaluerende. Boolske verdier i Common Lisp er representert med de selvvurderende symbolene T og NIL. Common Lisp har navneområder for symboler, kalt 'pakker'.

En rekke funksjoner er tilgjengelige for avrunding av skalære numeriske verdier på forskjellige måter. Funksjonen roundavrunder argumentet til nærmeste heltall, med halvveis tilfeller avrundet til det jevne heltall. Funksjonene truncate, floorog ceilinghenholdsvis rund mot null, ned eller opp. Alle disse funksjonene returnerer den forkastede brøkdelen som en sekundær verdi. For eksempel (floor -2.5)gir −3, 0,5; (ceiling -2.5)gir −2, −0,5; (round 2.5)gir 2, 0,5; og (round 3.5)gir 4, -0,5.

Datastrukturer

Sekvenstyper i Common Lisp inkluderer lister, vektorer, bitvektorer og strenger. Det er mange operasjoner som kan fungere på hvilken som helst sekvens.

Som i nesten alle andre Lisp -dialekter , består lister i Common Lisp av conses , noen ganger kalt cons -celler eller par . En ulempe er en datastruktur med to spor, kalt bilen og cdr . En liste er en sammenhengende kjede av conses eller den tomme listen. Hver ulempes bil refererer til et medlem av listen (muligens en annen liste). Hver conss cdr refererer til de neste cons - bortsett fra de siste cons i en liste, hvis cdr refererer til nilverdien. Conses kan også enkelt brukes til å implementere trær og andre komplekse datastrukturer; selv om det vanligvis anbefales å bruke struktur eller klasseforekomster i stedet. Det er også mulig å lage sirkulære datastrukturer med ulemper.

Common Lisp støtter flerdimensjonale matriser , og kan justere størrelser dynamisk hvis det er nødvendig. Flerdimensjonale matriser kan brukes til matrisematematikk. En vektor er en endimensjonal matrise. Arrays kan bære hvilken som helst type som medlemmer (til og med blandede typer i samme matrise) eller kan spesialiseres for å inneholde en bestemt type medlemmer, som i en vektor med biter. Vanligvis støttes bare noen få typer. Mange implementeringer kan optimalisere matrisefunksjoner når matrisen som brukes er typespesialisert. To typer spesialiserte matrisetyper er standard: en streng er en vektor med tegn, mens en bitvektor er en vektor med biter .

Hashtabeller lagrer assosiasjoner mellom dataobjekter. Ethvert objekt kan brukes som nøkkel eller verdi. Hashtabellene endres automatisk etter behov.

Pakker er samlinger av symboler, som hovedsakelig brukes til å skille delene av et program til navneområder . En pakke kan eksportere noen symboler og markere dem som en del av et offentlig grensesnitt. Pakker kan bruke andre pakker.

Strukturer som ligner på C -strukturer og Pascal -poster, representerer vilkårlige komplekse datastrukturer med et hvilket som helst antall og typer felt (kalt spor ). Strukturer tillater enkeltarv.

Klasser ligner strukturer, men tilbyr mer dynamiske funksjoner og flerarv. (Se CLOS ). Klassene har blitt lagt til sent i Common Lisp, og det er en viss konseptuell overlapping med strukturer. Objekter opprettet av klasser kalles forekomster . Et spesielt tilfelle er Generic Functions. Generiske funksjoner er både funksjoner og forekomster.

Funksjoner

Common Lisp støtter førsteklasses funksjoner . For eksempel er det mulig å skrive funksjoner som tar andre funksjoner som argumenter eller returnerer funksjoner også. Dette gjør det mulig å beskrive svært generelle operasjoner.

Common Lisp-biblioteket er sterkt avhengig av slike høyereordensfunksjoner. For eksempel sorttar funksjonen en relasjonsoperator som et argument og nøkkelfunksjon som et valgfritt søkeordargument. Dette kan ikke bare brukes til å sortere alle typer data, men også til å sortere datastrukturer i henhold til en nøkkel.

 ;; Sorts the list using the > and < function as the relational operator.
 (sort (list 5 2 6 3 1 4) #'>)   ; Returns (6 5 4 3 2 1)
 (sort (list 5 2 6 3 1 4) #'<)   ; Returns (1 2 3 4 5 6)
 ;; Sorts the list according to the first element of each sub-list.
 (sort (list '(9 A) '(3 B) '(4 C)) #'< :key #'first)   ; Returns ((3 B) (4 C) (9 A))

Evalueringsmodellen for funksjoner er veldig enkel. Når evaluatoren møter et skjema (f a1 a2...), antar det at symbolet f heter ett av følgende:

  1. En spesiell operatør (lett kontrollert mot en fast liste)
  2. En makrooperatør (må ha blitt definert tidligere)
  3. Navnet på en funksjon (standard), som enten kan være et symbol eller en underform som begynner med symbolet lambda.

Hvis f er navnet på en funksjon, blir argumentene a1, a2, ..., an evaluert i venstre til høyre rekkefølge, og funksjonen blir funnet og påkalt med de verdiene som er gitt som parametere.

Definere funksjoner

Den makrodefun definerer funksjoner hvor en funksjonsdefinisjon gir navnet på funksjonen, navnene på noen argumenter, og en funksjon legeme:

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

Funksjonsdefinisjoner kan omfatte kompilator direktiver , kjent som erklæringer , som gir hint til kompilatoren om optimalisering innstillinger eller datatyper argumenter. De kan også inneholde dokumentasjonsstrenger (docstrings), som Lisp -systemet kan bruke til å levere interaktiv dokumentasjon:

 (defun square (x)
   "Calculates the square of the single-float x."
   (declare (single-float x) (optimize (speed 3) (debug 0) (safety 1)))
   (the single-float (* x x)))

Anonyme funksjoner ( funksjonslitteraler ) defineres ved hjelp av lambdauttrykk, f.eks. (lambda (x) (* x x))For en funksjon som kvadrerer argumentet. Lisp programmeringsstil bruker ofte funksjoner av høyere orden som det er nyttig å gi anonyme funksjoner som argumenter for.

Lokale funksjoner kan defineres med fletog labels.

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

Det er flere andre operatører knyttet til definisjon og manipulering av funksjoner. For eksempel kan en funksjon kompileres med compileoperatøren. (Noen Lisp -systemer kjører funksjoner ved hjelp av en tolk som standard hvis de ikke blir bedt om å kompilere; andre kompilerer hver funksjon).

Definere generiske funksjoner og metoder

Makroen defgenericdefinerer generiske funksjoner . Generiske funksjoner er en samling metoder . Makroen defmethoddefinerer metoder.

Metoder kan spesialisere parametrene sine over CLOS standardklasser , systemklasser , strukturklasser eller individuelle objekter. For mange typer er det tilsvarende systemklasser .

Når en generisk funksjon kalles, vil multiple-dispatch bestemme den effektive metoden som skal brukes.

 (defgeneric add (a b))
 (defmethod add ((a number) (b number))
   (+ a b))
 (defmethod add ((a vector) (b number))
   (map 'vector (lambda (n) (+ n b)) a))
 (defmethod add ((a vector) (b vector))
   (map 'vector #'+ a b))
(defmethod add ((a string) (b string))
  (concatenate 'string a b))
 (add 2 3)                   ; returns 5
 (add #(1 2 3 4) 7)          ; returns #(8 9 10 11)
 (add #(1 2 3 4) #(4 3 2 1)) ; returns #(5 5 5 5)
 (add "COMMON " "LISP")      ; returns "COMMON LISP"

Generiske funksjoner er også en førsteklasses datatype . Det er mange flere funksjoner for generiske funksjoner og metoder enn beskrevet ovenfor.

Navnet på funksjonen

Navneområdet for funksjonsnavn er atskilt fra navneområdet for datavariabler. Dette er en viktig forskjell mellom Common Lisp og Scheme . For Common Lisp, operatører som definerer navn i funksjonen navne inkluderer defun, flet, labels, defmethodog defgeneric.

For å overføre en funksjon ved navn som et argument til en annen funksjon, må man bruke functionspesialoperatoren, ofte forkortet som #'. Det første sorteksemplet ovenfor refererer til funksjonen navngitt av symbolet >i funksjonsnavnområdet, med koden #'>. Omvendt, for å kalle en funksjon passert på en slik måte, ville man bruke funcalloperatøren på argumentet.

Ordningens evalueringsmodell er enklere: det er bare ett navneområde, og alle posisjoner i skjemaet evalueres (i hvilken som helst rekkefølge) - ikke bare argumentene. Kode skrevet på den ene dialekten er derfor noen ganger forvirrende for programmerere som er mer erfarne på den andre. For eksempel liker mange Common Lisp -programmerere å bruke beskrivende variabelnavn, for eksempel liste eller streng som kan forårsake problemer i Scheme, ettersom de lokalt ville skygge funksjonsnavn.

Hvorvidt et eget navnerom for funksjoner er en fordel, er en kilde til tvist i Lisp -samfunnet. Det blir vanligvis referert til som Lisp-1 vs Lisp-2-debatten . Lisp-1 refererer til Schemes modell og Lisp-2 refererer til Common Lisps modell. Disse navnene ble laget i et papir fra 1988 av Richard P. Gabriel og Kent Pitman , som grundig sammenligner de to tilnærmingene.

Flere returverdier

Common Lisp støtter konseptet med flere verdier , hvor ethvert uttrykk alltid har en primærverdi , men det kan også ha et hvilket som helst antall sekundære verdier , som kan mottas og inspiseres av interesserte innringere. Dette konseptet er forskjellig fra å returnere en listeverdi, ettersom de sekundære verdiene er fullt valgfrie og sendes via en dedikert sidekanal. Dette betyr at innringere kan forbli helt uvitende om at de sekundære verdiene er der hvis de ikke har behov for dem, og det gjør det praktisk å bruke mekanismen for å kommunisere informasjon som noen ganger er nyttig, men ikke alltid nødvendig. For eksempel,

  • Den TRUNCATEfunksjon runder det gitte nummer til et helt tall mot null. Imidlertid returnerer den også resten som en sekundær verdi, noe som gjør det veldig enkelt å bestemme hvilken verdi som ble avkortet. Den støtter også en valgfri divisorparameter, som kan brukes til å utføre euklidisk divisjon trivielt:
(let ((x 1266778)
      (y 458))
  (multiple-value-bind (quotient remainder)
      (truncate x y)
    (format nil "~A divided by ~A is ~A remainder ~A" x y quotient remainder)))

;;;; => "1266778 divided by 458 is 2765 remainder 408"
  • GETHASHreturnerer verdien av en nøkkel i et assosiativt kart , eller standardverdien ellers, og en sekundær boolean som indikerer om verdien ble funnet. Således kan kode som ikke bryr seg om verdien ble funnet eller gitt som standard ganske enkelt bruke den som den er, men når en slik forskjell er viktig, kan den inspisere den sekundære boolske og reagere på riktig måte. Begge brukstilfellene støttes av det samme anropet, og ingen av dem er unødig belastet eller begrenset av den andre. Å ha denne funksjonen på språknivå fjerner behovet for å sjekke om det finnes nøkkelen eller sammenligne den med null, slik det ville blitt gjort på andre språk.
(defun get-answer (library)
  (gethash 'answer library 42))

(defun the-answer-1 (library)
  (format nil "The answer is ~A" (get-answer library)))
;;;; Returns "The answer is 42" if ANSWER not present in LIBRARY

(defun the-answer-2 (library)
  (multiple-value-bind (answer sure-p)
      (get-answer library)
    (if (not sure-p)
        "I don't know"
     (format nil "The answer is ~A" answer))))
;;;; Returns "I don't know" if ANSWER not present in LIBRARY

Flere verdier støttes av en håndfull standardskjemaer, hvorav de vanligste er MULTIPLE-VALUE-BINDspesialskjemaet for å få tilgang til sekundære verdier og VALUESfor å returnere flere verdier:

(defun magic-eight-ball ()
  "Return an outlook prediction, with the probability as a secondary value"
  (values "Outlook good" (random 1.0)))

;;;; => "Outlook good"
;;;; => 0.3187

Andre typer

Andre datatyper i Common Lisp inkluderer:

  • Stienavn representerer filer og kataloger i filsystemet . Det vanlige Lisp -banenavnet er mer generelt enn de fleste konvensjoner for navngivning av operativsystemer, noe som gjør Lisp -programmers tilgang til filer bredt bærbare på tvers av forskjellige systemer.
  • Inngangs- og utgangsstrømmene representerer kilder og sluk av binære eller tekstdata, slik som de terminale eller åpne filer.
  • Common Lisp har en innebygd pseudo-tilfeldig tallgenerator (PRNG). Tilfeldige tilstandsobjekter representerer gjenbrukbare kilder til pseudo-tilfeldige tall, slik at brukeren kan kutte PRNG-en eller få den til å spille en sekvens på nytt.
  • Betingelser er en type som brukes til å representere feil, unntak og andre "interessante" hendelser som et program kan svare på.
  • Klasser er førsteklasses objekter , og er i seg selv forekomster av klasser som kalles metaobject-klasser ( metaclasses for kort).
  • Lesetabeller er en type objekt som styrer hvordan Common Lisps leser analyserer teksten i kildekoden. Ved å kontrollere hvilken lesetabell som er i bruk når koden leses inn, kan programmereren endre eller utvide språkets syntaks.

omfang

Som programmer på mange andre programmeringsspråk, bruker Common Lisp -programmer navn for å referere til variabler, funksjoner og mange andre typer enheter. Navngitte referanser er gjenstand for omfang.

Forbindelsen mellom et navn og enheten som navnet refererer til kalles en binding.

Omfang refererer til settet med omstendigheter der et navn er bestemt for å ha en bestemt binding.

Bestemmere av omfang

Omstendighetene som bestemmer omfanget i Common Lisp inkluderer:

  • plasseringen av en referanse i et uttrykk. Hvis det er den venstre posisjonen til en forbindelse, refererer den til en spesiell operator eller en makro- eller funksjonsbinding, ellers til en variabel binding eller noe annet.
  • typen uttrykk som referansen finner sted i. For eksempel (go x)betyr overføringskontroll til etikett x, mens (print x)refererer til variabelen x. Begge omfang av xkan være aktive i samme område av programtekst, siden tagbody -etiketter er i et eget navnerom fra variabelnavn. En spesiell form eller makroform har full kontroll over betydningen av alle symboler i syntaksen. For eksempel i (defclass x (a b) ()), en klassedefinisjon, (a b)er det en liste over grunnklasser, så disse navnene blir slått opp i klassenavn, og xer ikke en referanse til en eksisterende binding, men navnet på en ny klasse som er avledet fra aog b. Disse fakta kommer ut av semantikken til defclass. Det eneste generiske faktumet om dette uttrykket er det som defclassrefererer til en makrobinding; alt annet er opp til defclass.
  • plasseringen av referansen i programteksten. For eksempel, hvis en referanse til variabel xer innelukket i en bindingskonstruksjon, for eksempel en letsom definerer en binding for x, så er referansen i omfanget som er opprettet av den bindingen.
  • for en variabelreferanse, uansett om et variabelsymbol lokalt eller globalt har blitt erklært som spesielt. Dette avgjør om referansen løses i et leksikalt miljø eller i et dynamisk miljø.
  • den spesifikke forekomsten av miljøet der referansen løses. Et miljø er en kjøretidsordbok som tilordner symboler til bindinger. Hver type referanse bruker sitt eget miljø. Referanser til leksikale variabler løses i et leksikalsk miljø, osv. Mer enn ett miljø kan knyttes til den samme referansen. For eksempel, takket være rekursjon eller bruk av flere tråder, kan flere aktiveringer av den samme funksjonen eksistere samtidig. Disse aktiveringene deler den samme programteksten, men hver har sin egen leksikalske miljøforekomst.

For å forstå hva et symbol refererer til, må Common Lisp-programmereren vite hva slags referanse som blir uttrykt, hva slags omfang den bruker hvis den er en variabel referanse (dynamisk versus leksikalsk omfang), og også kjøretidssituasjonen: i hvilket miljø er referansen løst, hvor ble bindingen innført i miljøet, og så videre.

Typer miljø

Global

Noen miljøer i Lisp er globalt gjennomgripende. For eksempel, hvis en ny type er definert, er den kjent overalt deretter. Referanser til den typen ser det opp i dette globale miljøet.

Dynamisk

En type miljø i Common Lisp er det dynamiske miljøet. Bindinger etablert i dette miljøet har dynamisk omfang, noe som betyr at en binding opprettes i begynnelsen av utførelsen av en konstruksjon, for eksempel en letblokk, og forsvinner når den konstruksjonen er ferdig med å utføre: dens levetid er knyttet til dynamisk aktivering og deaktivering av en blokk. Imidlertid er en dynamisk binding ikke bare synlig i denne blokken; den er også synlig for alle funksjoner som påkalles fra den blokken. Denne typen synlighet er kjent som ubestemt omfang. Bindinger som viser dynamisk omfang (levetid knyttet til aktivering og deaktivering av en blokk) og ubestemt omfang (synlig for alle funksjoner som kalles fra denne blokken) sies å ha dynamisk omfang.

Common Lisp har støtte for dynamisk omfangede variabler, som også kalles spesialvariabler. Enkelte andre typer bindinger er nødvendigvis også dynamisk omfattende, for eksempel omstarter og fangmerkoder. Funksjonsbindinger kan ikke dynamisk omfanges ved hjelp av flet(som bare gir funksjonsbindinger med leksisk omfang), men funksjonsobjekter (et objekt på første nivå i Common Lisp) kan tilordnes dynamisk omfangede variabler, bundet med leti dynamisk omfang, deretter kalt med funcalleller APPLY.

Dynamisk omfang er ekstremt nyttig fordi det gir referanseklarhet og disiplin til globale variabler . Globale variabler er frynset i informatikk som potensielle feilkilder, fordi de kan gi opphav til ad-hoc, skjulte kommunikasjonskanaler mellom moduler som fører til uønskede, overraskende interaksjoner.

I Common Lisp oppfører en spesiell variabel som bare har en binding på toppnivå seg akkurat som en global variabel i andre programmeringsspråk. En ny verdi kan lagres i den, og den verdien erstatter ganske enkelt det som er i bindingen på toppnivå. Uforsiktig erstatning av verdien av en global variabel er kjernen i feil forårsaket av bruk av globale variabler. Imidlertid er en annen måte å jobbe med en spesiell variabel å gi den en ny, lokal binding i et uttrykk. Dette blir noen ganger referert til som "rebinding" av variabelen. Binding av en dynamisk omfangs variabel oppretter midlertidig et nytt minnested for den variabelen, og knytter navnet til den plasseringen. Selv om bindingen er i kraft, refererer alle referanser til den variabelen til den nye bindingen; den forrige bindingen er skjult. Når utførelsen av bindingsuttrykket avsluttes, er den midlertidige minneplasseringen borte, og den gamle bindingen avsløres, med den opprinnelige verdien intakt. Selvfølgelig kan flere dynamiske bindinger for den samme variabelen hekkes.

I Common Lisp -implementeringer som støtter multithreading, er dynamiske omfang spesifikke for hver kjøringstråd. Dermed tjener spesielle variabler som en abstraksjon for tråd lokal lagring. Hvis en tråd rebinder en spesiell variabel, har denne rebinding ingen effekt på den variabelen i andre tråder. Verdien lagret i en binding kan bare hentes av tråden som opprettet bindingen. Hvis hver tråd binder en spesiell variabel *x*, *x*oppfører den seg som tråd-lokal lagring. Blant tråder som ikke rebindes *x*, oppfører den seg som en vanlig global: alle disse trådene refererer til den samme toppnivåbindingen av *x*.

Dynamiske variabler kan brukes til å utvide utførelseskonteksten med ytterligere kontekstinformasjon som implisitt sendes fra funksjon til funksjon uten å måtte vises som en ekstra funksjonsparameter. Dette er spesielt nyttig når kontrolloverføringen må passere gjennom lag med ikke -relatert kode, som ganske enkelt ikke kan utvides med ekstra parametere for å passere tilleggsdataene. En situasjon som denne krever vanligvis en global variabel. Den globale variabelen må lagres og gjenopprettes, slik at ordningen ikke bryter under rekursjon: dynamisk variabel rebinding tar seg av dette. Og den variabelen må gjøres tråd-lokal (ellers må en stor mutex brukes) slik at ordningen ikke bryter under tråder: dynamiske omfangsimplementeringer kan også ta seg av dette.

I Common Lisp -biblioteket er det mange standard spesialvariabler. For eksempel er alle standard I/O-strømmer lagret i toppnivåbindinger av kjente spesialvariabler. Standard utgangsstrøm lagres i *standardutgang *.

Anta at en funksjon foo skriver til standard utgang:

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

For å fange utgangen i en tegnstreng, kan * standard-output * bindes til en strengstrøm og kalles:

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

Leksikalsk

Common Lisp støtter leksikalske miljøer. Formelt sett har bindingene i et leksikalsk miljø leksikalt omfang og kan ha enten et ubestemt omfang eller dynamisk omfang, avhengig av typen navneområde. Leksisk omfang betyr at synligheten er fysisk begrenset til blokken der bindingen etableres. Referanser som ikke er tekstmessig (dvs. leksisk) innebygd i den blokken, ser ganske enkelt ikke den bindingen.

Merkelappene i en TAGBODY har leksikalt omfang. Uttrykket (GO X) er feil hvis det ikke er innebygd i en TAGBODY som inneholder en etikett X. Imidlertid forsvinner etikettbindinger når TAGBODY avslutter utførelsen, fordi de har dynamisk omfang. Hvis denne blokkblokken blir lagt inn på nytt ved påkallelse av en leksikalsk lukning , er det ugyldig for den lukkede delen å prøve å overføre kontrollen til en kode via GO:

  (defvar *stashed*) ;; will hold a function

  (tagbody
    (setf *stashed* (lambda () (go some-label)))
    (go end-label) ;; skip the (print "Hello")
   some-label
    (print "Hello")
   end-label)
  -> NIL

Når TAGBODY er utført, evaluerer den først setf -skjemaet som lagrer en funksjon i den spesielle variabelen *stashed *. Deretter overfører (go end-label) kontrollen til sluttetiketten, og hopper over koden (skriv ut "Hei"). Siden ende-etiketten er på enden av tagbody, avsluttes tagbody og gir NIL. Anta at den tidligere huskede funksjonen nå kalles:

  (funcall *stashed*) ;; Error!

Denne situasjonen er feil. En implementerings svar er en feilbetingelse som inneholder meldingen "GO: tagbody for tag SOME-LABEL has already been left". Funksjonen prøvde å evaluere (go some-label), som er leksisk innebygd i tagbody, og løses til etiketten. Imidlertid kjøres ikke tagbody (omfanget er avsluttet), og kontrolloverføringen kan derfor ikke skje.

Lokale funksjonsbindinger i Lisp har leksikalsk omfang , og variable bindinger har også leksikalsk omfang som standard. I motsetning til GO -etiketter har begge disse uendelig omfang. Når en leksikalsk funksjon eller variabel binding er etablert, fortsetter denne bindingen å eksistere så lenge referanser til den er mulig, selv etter at konstruksjonen som slo fast at bindingen er avsluttet. Henvisninger til leksikale variabler og funksjoner etter avslutningen av deres etableringskonstruksjon er mulige takket være leksikale nedleggelser .

Leksikal binding er standard bindingsmodus for vanlige Lisp -variabler. For et individuelt symbol kan det byttes til dynamisk omfang, enten ved en lokal erklæring, med en global erklæring. Sistnevnte kan forekomme implisitt ved bruk av en konstruksjon som DEFVAR eller DEFPARAMETER. Det er en viktig konvensjon i Common Lisp -programmering at spesielle (dvs. dynamisk omfang) variabler har navn som begynner og slutter med en stjerne sigil * i det som kalles " øretelefonkonvensjonen ". Hvis den overholdes, oppretter denne konvensjonen effektivt et eget navnerom for spesielle variabler, slik at variabler som er ment å være leksikale, ikke ved et uhell blir gjort spesielle.

Leksisk omfang er nyttig av flere grunner.

For det første kan referanser til variabler og funksjoner kompileres til effektiv maskinkode, fordi miljøkonstruksjonen i løpetid er relativt enkel. I mange tilfeller kan den optimaliseres for å stable lagring, så åpning og lukking av leksikalske omfang har minimal overhead. Selv i tilfeller der det må genereres fullstendige nedleggelser, er tilgangen til lukkingens miljø fremdeles effektiv; vanligvis blir hver variabel en forskyvning til en vektor av bindinger, og så blir en variabelreferanse en enkel last- eller lagringsinstruksjon med en adressemodus for basis-pluss-forskyvning .

For det andre gir leksikalsk omfang (kombinert med ubestemt omfang) den leksikale nedleggelsen , som igjen skaper et helt paradigme for programmering sentrert rundt bruk av funksjoner som førsteklasses objekter, som er roten til funksjonell programmering.

For det tredje, kanskje viktigst, selv om leksikale nedleggelser ikke utnyttes, isolerer bruken av leksikalsk omfang programmoduler fra uønskede interaksjoner. På grunn av deres begrensede synlighet er leksikale variabler private. Hvis en modul A binder en leksikalsk variabel X og kaller en annen modul B, vil referanser til X i B ikke ved et uhell gå over til X -bundet i A. B har rett og slett ingen tilgang til X. For situasjoner der disiplinerte interaksjoner gjennom en variabel er ønskelig, Common Lisp gir spesielle variabler. Spesielle variabler gjør det mulig for en modul A å sette opp en binding for en variabel X som er synlig for en annen modul B, kalt fra A. Å kunne gjøre dette er en fordel, og å kunne forhindre at det skjer er også en fordel; følgelig støtter Common Lisp både leksikalsk og dynamisk omfang .

Makroer

En makro i Lisp ligner overfladisk en funksjon i bruk. I stedet for å representere et uttrykk som evalueres, representerer det imidlertid en transformasjon av programkildekoden. Makroen får kilden den omgir som argumenter, binder dem til parameterne og beregner en ny kildeform. Denne nye formen kan også bruke en makro. Makroutvidelsen gjentas til det nye kildeskjemaet ikke bruker en makro. Den siste beregnede formen er kildekoden som kjøres under kjøretid.

Typisk bruk av makroer i Lisp:

  • nye kontrollstrukturer (eksempel: sløyfekonstruksjoner, forgreningskonstruksjoner)
  • omfang og bindende konstruksjoner
  • forenklet syntaks for kompleks og gjentatt kildekode
  • definere skjemaer på toppnivå med kompileringstidseffekter
  • datastyrt programmering
  • innebygde domenespesifikke språk (eksempler: SQL , HTML , Prolog )
  • implisitte fullføringsskjemaer

Ulike standard Common Lisp -funksjoner må også implementeres som makroer, for eksempel:

  • standardabstraksjonen setf, for å tillate tilpassede kompileringstidsutvidelser av tildelings-/tilgangsoperatører
  • with-accessors, with-slots, with-open-fileOg andre lignende WITHmakroer
  • Avhengig av implementering, ifeller conder en makro bygget på den andre, den spesielle operatøren; whenog unlessbestår av makroer
  • Det kraftige loopdomenespesifikke språket

Makroer defineres av defmacro -makroen . Den spesielle operatør macrolet tillater definisjonen av lokale (leksikalt scoped) makroer. Det er også mulig å definere makroer for symboler ved å bruke definere-symbol-makro og symbol-makrolett .

Paul Grahams bok On Lisp beskriver bruken av makroer i Common Lisp i detalj. Doug Hoytes bok Let Over Lambda utvider diskusjonen om makroer og hevder "Makroer er den største fordelen som lisp har som programmeringsspråk og den største fordelen med ethvert programmeringsspråk." Hoyte gir flere eksempler på iterativ utvikling av makroer.

Eksempel på bruk av en makro for å definere en ny kontrollstruktur

Makroer lar Lisp -programmerere lage nye syntaktiske former på språket. En typisk bruk er å lage nye kontrollstrukturer. Eksempelmakroen gir en untillooping -konstruksjon. Syntaksen er:

(until test form*)

Makrodefinisjonen til :

(defmacro until (test &body body)
  (let ((start-tag (gensym "START"))
        (end-tag   (gensym "END")))
    `(tagbody ,start-tag
              (when ,test (go ,end-tag))
              (progn ,@body)
              (go ,start-tag)
              ,end-tag)))

tagbody er en primitiv Common Lisp -spesialoperatør som gir deg muligheten til å navngi koder og bruke go -skjemaet til å hoppe til disse kodene. Bak sitatet ` gir en notasjon som gir kode maler, der verdien av skjemaer foran et komma fylles ut. Skjemaer foran med komma og at-sign er spleiset i. Tagbody-skjemaet tester slutttilstanden. Hvis betingelsen er sann, hopper den til sluttkoden. Ellers blir den oppgitte hovedkoden utført, og deretter hopper den til start -taggen.

Et eksempel på bruk av ovennevnte frem til makro:

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

Koden kan utvides ved hjelp av funksjonen macroexpand-1 . Utvidelsen for eksemplet ovenfor ser slik ut:

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

Ved makro utvidelse av verdien av den variable testen er (= (tilfeldig 10) 0) og verdien av den variable legemet er ((skrive-line "Hallo")) . Kroppen er en liste over former.

Symboler oppheves vanligvis automatisk. Utvidelsen bruker TAGBODY med to etiketter. Symbolene for disse etikettene er beregnet av GENSYM og er ikke internert i noen pakke. To go -skjemaer bruker disse kodene til å hoppe til. Siden tagbody er en primitiv operatør i Common Lisp (og ikke en makro), vil den ikke bli utvidet til noe annet. Den utvidede formen bruker når -makroen, som også vil bli utvidet. Fullstendig utvidelse av et kildeformular kalles kodevandring .

I den fullt utvidede ( vandrede ) formen erstattes skjemaet når når det primitive hvis :

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

Alle makroer må utvides før kildekoden som inneholder dem kan evalueres eller kompileres normalt. Makroer kan betraktes som funksjoner som godtar og returnerer S-uttrykk -ligner på abstrakte syntakstrær , men ikke begrenset til dem. Disse funksjonene påkalles før evaluatoren eller kompilatoren for å produsere den endelige kildekoden. Makroer er skrevet i vanlig Common Lisp, og kan bruke alle tilgjengelige Common Lisp (eller tredjeparts) operatører.

Variabel fangst og skygge

Vanlige Lisp-makroer er i stand til det som vanligvis kalles variabel fangst , der symboler i makro-ekspansjonskroppen faller sammen med de i ringekonteksten, slik at programmereren kan lage makroer der forskjellige symboler har spesiell betydning. Begrepet variabelfangst er noe misvisende, fordi alle navnerom er sårbare for uønsket fangst, inkludert operatør- og funksjonsnavnområde, tagbody -etikettnavnområdet, catch -taggen, tilstandshåndtereren og omstart navnerom.

Variabel fangst kan introdusere programvarefeil. Dette skjer på en av følgende to måter:

  • På den første måten kan en makroutvidelse utilsiktet lage en symbolsk referanse som makroforfatteren antok vil løse i et globalt navnerom, men koden der makroen utvides skjer for å gi en lokal, skyggefull definisjon som stjeler referansen. La dette bli referert til som type 1 -fangst.
  • Den andre måten, type 2 -fangst, er det motsatte: noen av makroens argumenter er kodebiter levert av makrooppringeren, og disse bitene er skrevet slik at de refererer til omkringliggende bindinger. Makroen setter imidlertid inn disse kodestykkene i en utvidelse som definerer sine egne bindinger som ved et uhell fanger noen av disse referansene.

Scheme-dialekten til Lisp gir et makroskrivesystem som gir referensiell gjennomsiktighet som eliminerer begge typer fangstproblemer. Denne typen makrosystem kalles noen ganger "hygienisk", spesielt av tilhengerne (som anser makrosystemer som ikke automatisk løser dette problemet som uhygieniske).

I Common Lisp er makrohygiene sikret på to forskjellige måter.

En tilnærming er å bruke gensyms : garanterte-unike symboler som kan brukes i en makro-ekspansjon uten trussel om fangst. Bruken av gensymmer i en makrodefinisjon er en manuell oppgave, men det kan skrives makroer som forenkler instantiering og bruk av gensymmer. Gensyms løser enkelt opptak av type 2, men de kan ikke brukes på type 1 -fangst på samme måte, fordi makroutvidelsen ikke kan gi nytt navn til de forstyrrende symbolene i den omkringliggende koden som fanger referansene. Gensyms kan brukes til å gi stabile aliaser for de globale symbolene som makroutvidelsen trenger. Makroutvidelsen ville bruke disse hemmelige aliasene i stedet for de kjente navnene, så omdefinering av de kjente navnene ville ikke ha noen dårlig effekt på makroen.

En annen tilnærming er å bruke pakker. En makro definert i sin egen pakke kan ganske enkelt bruke interne symboler i pakken i sin utvidelse. Bruk av pakker omhandler fangst av type 1 og type 2.

Pakker løser imidlertid ikke type 1 -registrering av referanser til vanlige Common Lisp -funksjoner og operatører. Årsaken er at bruk av pakker for å løse fangstproblemer dreier seg om bruk av private symboler (symboler i en pakke, som ikke importeres til, eller på annen måte blir synlige i andre pakker). Mens Common Lisp-biblioteksymbolene er eksterne og ofte importert til eller synliggjort i brukerdefinerte pakker.

Følgende er et eksempel på uønsket fangst i operatørnavnområdet, som skjer i utvidelsen av en makro:

 ;; expansion of UNTIL makes liberal use of DO
 (defmacro until (expression &body body)
   `(do () (,expression) ,@body))

 ;; macrolet establishes lexical operator binding for DO
 (macrolet ((do (...) ... something else ...))
   (until (= (random 10) 0) (write-line "Hello")))

Den untilmakro vil ekspandere inn i en form som anrop dosom er ment å referere til den standard Common Lisp makro do. Imidlertid dokan den ha en helt annen betydning i denne sammenhengen , så den untilfungerer kanskje ikke som den skal.

Common Lisp løser problemet med skyggelegging av standardoperatører og funksjoner ved å forby redefinering. Fordi den omdefinerer standardoperatoren do, er det foregående faktisk et fragment av ikke-samsvarende Common Lisp, som gjør det mulig for implementeringer å diagnostisere og avvise den.

Tilstandssystem

Den tilstand systemet er ansvarlig for håndtering av unntak i Common Lisp. Det gir betingelser , handler og omstart s. Tilstand s er objekter som beskriver en eksepsjonell situasjon (for eksempel en feil). Hvis en tilstand blir signalisert, søker Common Lisp -systemet etter en behandler for denne tilstandstypen og ringer opp behandleren. Den handler kan nå søke etter omstarter og bruk en av disse starter på nytt til å reparere det aktuelle problemet, ved hjelp av informasjon som betingelse typer og alle relevante opplysninger gitt som en del av tilstanden objekt, og kaller den aktuelle restart funksjon.

Disse omstartene, hvis de ikke håndteres av kode, kan presenteres for brukere (som en del av et brukergrensesnitt, for eksempel for en feilsøker), slik at brukeren kan velge og påberope en av de tilgjengelige omstartene. Siden tilstandshåndtereren kalles i feilkonteksten (uten å avvikle stabelen), er full feilgjenoppretting mulig i mange tilfeller, der andre unntakshåndteringssystemer allerede ville ha avsluttet gjeldende rutine. Debuggeren selv kan også tilpasses eller erstattes ved hjelp av den *debugger-hook*dynamiske variabelen. Kode som finnes i avviklingsbeskyttelsesskjemaer, for eksempel sluttbehandlere, vil også bli utført etter behov til tross for unntaket.

I følgende eksempel (ved hjelp av symbolikk Genera ) brukeren prøver å åpne en fil i en Lisp funksjon test ringte fra Les-Eval-Print-LOOP ( ERSTATTER ), når filen ikke eksisterer. Lisp -systemet presenterer fire omstarter. Brukeren velger Prøv OPP igjen ved å bruke et annet banenavn på nytt og angir et annet banenavn (lispm-init.lisp i stedet for lispm-int.lisp). Brukerkoden inneholder ingen feilhåndteringskode. Hele feilhåndterings- og omstartskoden leveres av Lisp -systemet, som kan håndtere og reparere feilen uten å avslutte brukerkoden.

Command: (test ">zippy>lispm-int.lisp")

Error: The file was not found.
       For lispm:>zippy>lispm-int.lisp.newest

LMFS:OPEN-LOCAL-LMFS-1
   Arg 0: #P"lispm:>zippy>lispm-int.lisp.newest"

s-A, <Resume>: Retry OPEN of lispm:>zippy>lispm-int.lisp.newest
s-B:           Retry OPEN using a different pathname
s-C, <Abort>:  Return to Lisp Top Level in a TELNET server
s-D:           Restart process TELNET terminal

-> Retry OPEN using a different pathname
Use what pathname instead [default lispm:>zippy>lispm-int.lisp.newest]:
   lispm:>zippy>lispm-init.lisp.newest

...the program continues

Common Lisp Object System (CLOS)

Common Lisp inneholder en verktøykasse for objektorientert programmering , Common Lisp Object System eller CLOS , som er et av de kraftigste objektsystemene som er tilgjengelig på alle språk. For eksempel forklarer Peter Norvig hvor mange designmønstre som er enklere å implementere på et dynamisk språk med funksjonene i CLOS (Multiple Inheritance, Mixins, Multimethods, Metaclasses, Method combinations, etc.). Flere utvidelser av Common Lisp for objektorientert programmering har blitt foreslått å bli inkludert i ANSI Common Lisp-standarden, men til slutt ble CLOS vedtatt som standard objektsystem for Common Lisp. CLOS er et dynamisk objektsystem med flere forsendelser og flere arv , og skiller seg radikalt fra OOP -fasilitetene som finnes på statiske språk som C ++ eller Java . Som et dynamisk objektsystem tillater CLOS endringer ved kjøring av generiske funksjoner og klasser. Metoder kan legges til og fjernes, klasser kan legges til og redefineres, objekter kan oppdateres for klasseendringer og klassen av objekter kan endres.

CLOS er integrert i ANSI Common Lisp. Generiske funksjoner kan brukes som normale funksjoner og er en førsteklasses datatype. Hver CLOS -klasse er integrert i systemet Common Lisp. Mange vanlige Lisp -typer har en tilsvarende klasse. Det er mer potensiell bruk av CLOS for Common Lisp. Spesifikasjonen sier ikke om betingelser er implementert med CLOS. Stienavn og bekker kan implementeres med CLOS. Disse ytterligere bruksmulighetene for CLOS for ANSI Common Lisp er ikke en del av standarden. Faktiske Common Lisp -implementeringer bruker CLOS for banenavn, strømmer, input -output, betingelser, implementeringen av CLOS selv og mer.

Kompilator og tolk

En Lisp-tolk utfører Lisp-kildekoden direkte som Lisp-objekter (lister, symboler, tall, ...) lest fra s-uttrykk. En Lisp -kompilator genererer bytekode eller maskinkode fra Lisp -kildekoden. Common Lisp lar både individuelle Lisp -funksjoner kompileres i minnet og kompilering av hele filer til eksternt lagret kompilert kode ( fasl -filer).

Flere implementeringer av tidligere Lisp -dialekter ga både tolk og kompilator. Dessverre var semantikken ofte annerledes. Disse tidligere Lisps implementerte leksikalsk omfang i kompilatoren og dynamisk omfang i tolken. Common Lisp krever at både tolk og kompilator bruker leksikalsk omfang som standard. Common Lisp -standarden beskriver både semantikken til tolken og en kompilator. Kompilatoren kan kalles ved hjelp av funksjonen kompilere for enkeltfunksjoner og bruke funksjonen samle-fil for filer. Common Lisp tillater typedeklarasjoner og gir måter å påvirke retningslinjene for generering av kompilatorkode. For sistnevnte kan forskjellige optimaliseringskvaliteter gis verdier mellom 0 (ikke viktig) og 3 (viktigst): hastighet , plass , sikkerhet , feilsøking og kompilasjonshastighet .

Det er også en funksjon for å vurdere Lisp-kode: eval. evaltar kode som forhåndsanalyserte s-uttrykk og ikke, som på noen andre språk, som tekststrenger. På denne måten kan koden konstrueres med de vanlige Lisp -funksjonene for å konstruere lister og symboler, og deretter kan denne koden evalueres med funksjonen eval. Flere vanlige Lisp -implementeringer (som Clozure CL og SBCL) implementerer evalved hjelp av kompilatoren. På denne måten kompileres kode, selv om den evalueres ved hjelp av funksjonen eval.

Filkompilatoren påkalles ved hjelp av funksjonen compile-file . Den genererte filen med kompilert kode kalles en fasl -fil (fra rask last ). Disse FasL filer og også kildekoden filer kan lastes med funksjonen last inn en løpende Common Lisp system. Avhengig av implementeringen genererer filkompilatoren byte-kode (for eksempel for Java Virtual Machine ), C- språkkode (som deretter kompileres med en C-kompilator) eller, direkte, opprinnelig kode.

Vanlige Lisp -implementeringer kan brukes interaktivt, selv om koden blir fullstendig kompilert. Ideen om et tolket språk gjelder dermed ikke for interaktiv Common Lisp.

Språket skiller mellom lesetid, kompileringstid, lastetid og kjøretid, og lar brukerkoden også gjøre dette skillet for å utføre ønsket type behandling på ønsket trinn.

Noen spesielle operatører tilbys for å passe spesielt til interaktiv utvikling; for eksempel defvarvil bare tildele en verdi til den angitte variabelen hvis den ikke allerede var bundet, mens den defparameteralltid vil utføre tildelingen. Dette skillet er nyttig når du interaktivt evaluerer, kompilerer og laster inn kode i et levende bilde.

Noen funksjoner er også tilgjengelig for å skrive kompilatorer og tolker. Symboler består av objekter på første nivå og kan manipuleres direkte med brukerkode. Den progvspesielle operatøren tillater å lage leksikale bindinger programmatisk, mens pakker også er manipulerbare. Lisp -kompilatoren er tilgjengelig ved kjøretid for å kompilere filer eller individuelle funksjoner. Disse gjør det enkelt å bruke Lisp som en mellomkompiler eller tolk for et annet språk.

Kodeksempler

Bursdagsparadoks

Følgende program beregner det minste antallet personer i et rom som sannsynligheten for unike fødselsdager er mindre enn 50% ( bursdagsparadokset , hvor sannsynligheten for 1 person åpenbart er 100%, for 2 er det 364/365, etc. ). Svaret er 23.

Etter konvensjon er konstanter i Common Lisp vedlagt med + tegn.

(defconstant +year-size+ 365)

(defun birthday-paradox (probability number-of-people)
  (let ((new-probability (* (/ (- +year-size+ number-of-people)
                               +year-size+)
                            probability)))
    (if (< new-probability 0.5)
        (1+ number-of-people)
        (birthday-paradox new-probability (1+ number-of-people)))))

Kaller eksempelfunksjonen ved hjelp av REPL (Read Eval Print Loop):

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

Sortere en liste over personobjekter

Vi definerer en klasse personog en metode for å vise navn og alder på en person. Deretter definerer vi en gruppe personer som en liste over personobjekter. Deretter gjentar vi den sorterte listen.

(defclass person ()
  ((name :initarg :name :accessor person-name)
   (age  :initarg :age  :accessor person-age))
  (:documentation "The class PERSON with slots NAME and AGE."))

(defmethod display ((object person) stream)
  "Displaying a PERSON object to an output stream."
  (with-slots (name age) object
    (format stream "~a (~a)" name age)))

(defparameter *group*
  (list (make-instance 'person :name "Bob"   :age 33)
        (make-instance 'person :name "Chris" :age 16)
        (make-instance 'person :name "Ash"   :age 23))
  "A list of PERSON objects.")

(dolist (person (sort (copy-list *group*)
                      #'>
                      :key #'person-age))
  (display person *standard-output*)
  (terpri))

Den skriver ut de tre navnene med synkende alder.

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

Eksponentiserer ved å kvadrere

Bruk av LOOP -makroen er demonstrert:

(defun power (x n)
  (loop with result = 1
        while (plusp n)
        when (oddp n) do (setf result (* result x))
        do (setf x (* x x)
                 n (truncate n 2))
        finally (return result)))

Eksempel på bruk:

CL-USER > (power 2 200)
1606938044258990275541962092341162602522202993782792835301376

Sammenlign med den innebygde eksponentieringen:

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

Finn listen over tilgjengelige skjell

WITH-OPEN-FILE er en makro som åpner en fil og gir en strøm. Når skjemaet returnerer, lukkes filen automatisk. FUNCALL kaller et funksjonsobjekt. LOOP samler alle linjer som samsvarer med predikatet.

(defun list-matching-lines (file predicate)
  "Returns a list of lines in file, for which the predicate applied to
 the line returns T."
  (with-open-file (stream file)
    (loop for line = (read-line stream nil nil)
          while line
          when (funcall predicate line)
          collect it)))

Funksjonen AVAILABLE-SHELLS kaller over funksjonen LIST-MATCHING-LINES med et banenavn og en anonym funksjon som predikat. Predikatet returnerer banenavnet til et skall eller NIL (hvis strengen ikke er filnavnet til et skall).

(defun available-shells (&optional (file #p"/etc/shells"))
  (list-matching-lines
   file
   (lambda (line)
     (and (plusp (length line))
          (char= (char line 0) #\/)
          (pathname
           (string-right-trim '(#\space #\tab) line))))))

Eksempelresultater (på Mac OS X 10.6):

CL-USER > (available-shells)
(#P"/bin/bash" #P"/bin/csh" #P"/bin/ksh" #P"/bin/sh" #P"/bin/tcsh" #P"/bin/zsh")

Sammenligning med andre Lisps

Common Lisp blir oftest sammenlignet med og i kontrast til Scheme - hvis det bare er fordi de er de to mest populære Lisp -dialektene. Ordningen går foran CL, og kommer ikke bare fra den samme Lisp -tradisjonen, men fra noen av de samme ingeniørene - Guy L. Steele , som Gerald Jay Sussman designet Scheme med, ledet standardkomiteen for Common Lisp.

Common Lisp er et generelt programmeringsspråk, i motsetning til Lisp-varianter som Emacs Lisp og AutoLISP som er utvidelsesspråk innebygd i bestemte produkter (henholdsvis GNU Emacs og AutoCAD). I motsetning til mange tidligere lisps, Common Lisp (som Scheme ) bruker leksikalsk variabel omfang som standard for både tolket og kompilert kode.

De fleste av Lisp -systemene hvis design bidro til Common Lisp - for eksempel ZetaLisp og Franz Lisp - brukte dynamisk omfangede variabler i sine tolkere og leksisk omfangede variabler i kompilatorene. Ordningen introduserte den eneste bruken av leksisk omfangede variabler for Lisp; en inspirasjon fra ALGOL 68 . CL støtter også dynamisk omfangede variabler, men de må eksplisitt deklareres som "spesielle". Det er ingen forskjeller i omfang mellom ANSI CL -tolker og kompilatorer.

Common Lisp kalles noen ganger en Lisp-2 og Scheme a Lisp-1 , med henvisning til CLs bruk av separate navneområder for funksjoner og variabler. (Faktisk har CL mange navneområder, for eksempel de for go -tagger, blokknavn og loopsøkeord). Det er en langvarig kontrovers mellom CL og Scheme-talsmenn om avveiningene som er involvert i flere navnerom. I Scheme er det (stort sett) nødvendig å unngå å gi variabler navn som kolliderer med funksjoner; Ordningsfunksjoner har ofte argumenter navngitt lis, lsteller lystfor ikke å komme i konflikt med systemfunksjonen list. I CL er det imidlertid nødvendig å eksplisitt referere til funksjonsnavnområdet når du sender en funksjon som et argument - som også er en vanlig forekomst, som i sorteksemplet ovenfor.

CL skiller seg også fra Scheme i håndteringen av boolske verdier. Ordningen bruker de spesielle verdiene #t og #f for å representere sannhet og falskhet. CL følger den eldre Lisp -konvensjonen om å bruke symbolene T og NIL, og NIL står også for den tomme listen. I CL blir enhver ikke-NIL-verdi behandlet som sann av betingelser, for eksempel if, mens i skjema alle ikke-#f-verdier blir behandlet som sanne. Disse konvensjonene tillater noen operatører på begge språk å tjene både som predikater (svare på et boolsk-verdsatt spørsmål) og som å returnere en nyttig verdi for videre beregning, men i Scheme evalueres verdien '() som tilsvarer NIL i Common Lisp til true i et boolsk uttrykk.

Til slutt krever Scheme-standarddokumenter optimalisering av haleanrop , noe CL-standarden ikke gjør. De fleste CL-implementeringer tilbyr optimalisering av haleoppringing, men ofte bare når programmereren bruker et optimaliseringsdirektiv. Likevel, ikke vanlig CL koding stil ikke favorisere den allestedsnærværende bruk av rekursjon at ordningen stil foretrekker-hva en Scheme programmerer ville uttrykke med halen rekursjon, en CL brukeren vil vanligvis uttrykke med en iterativ uttrykk i do, dolist, loop, eller (mer nylig) med iteratepakke.

Implementeringer

Se kategorien Common Lisp -implementeringer .

Common Lisp er definert av en spesifikasjon (som Ada og C ) i stedet for av en implementering (som Perl ). Det er mange implementeringer, og standarddetaljene kan være gyldig forskjellige.

I tillegg har implementeringer en tendens til å komme med utvidelser, som gir funksjonalitet som ikke dekkes av standarden:

  • Interaktivt toppnivå (REPL)
  • Søppelsamling
  • Debugger, Stepper og Inspector
  • Svake datastrukturer (hashtabeller)
  • Utvidelige sekvenser
  • Utvidbar LOOP
  • Miljøtilgang
  • CLOS Meta-object Protocol
  • CLOS -baserte utvidbare bekker
  • CLOS -basert tilstandssystem
  • Nettverksstrømmer
  • Vedvarende CLOS
  • Unicode -støtte
  • Fremmedspråklig grensesnitt (ofte til C)
  • Operativsystem grensesnitt
  • Java -grensesnitt
  • Tråder og multiprosessering
  • Levering av applikasjoner (applikasjoner, dynamiske biblioteker)
  • Lagring av bilder

Gratis og åpen kildekode-programvarebibliotek har blitt opprettet for å støtte utvidelser til Common Lisp på en bærbar måte, og finnes spesielt i depotene til Common-Lisp.net og CLOCC (Common Lisp Open Code Collection) -prosjektene.

Vanlige Lisp -implementeringer kan bruke en hvilken som helst blanding av opprinnelig kodesamling, bytekodesamling eller tolkning. Common Lisp er designet for å støtte inkrementelle kompilatorer , filkompilatorer og blokkkompilatorer. Standarderklæringer for å optimalisere kompilering (for eksempel funksjonsinlining eller typespesialisering) foreslås i språkspesifikasjonen. De fleste vanlige Lisp -implementeringer kompilerer kildekoden til den opprinnelige maskinkoden . Noen implementeringer kan lage (optimaliserte) frittstående applikasjoner. Andre kompilerer til tolket bytekode , som er mindre effektiv enn opprinnelig kode, men letter binær-kode-portabilitet. Noen kompilatorer kompilerer Common Lisp -kode til C -kode. Misforståelsen om at Lisp er et rent tolket språk, er mest sannsynlig fordi Lisp-miljøer gir en interaktiv melding og at koden blir samlet en-for-en, trinnvis. Med Common Lisp er inkrementell kompilering mye brukt.

Noen Unix -baserte implementeringer ( CLISP , SBCL ) kan brukes som skriptspråk ; det vil si påkalles av systemet på en transparent måte som en Perl- eller Unix -skalletolker er.

Liste over implementeringer

Kommersielle implementeringer

Allegro Common Lisp
for Microsoft Windows, FreeBSD, Linux, Apple macOS og forskjellige UNIX -varianter. Allegro CL tilbyr et integrert utviklingsmiljø (IDE) (for Windows og Linux) og omfattende muligheter for applikasjonslevering.
Liquid Common Lisp
tidligere kalt Lucid Common Lisp . Bare vedlikehold, ingen nye utgivelser.
LispWorks
for Microsoft Windows, FreeBSD, Linux, Apple macOS, iOS, Android og forskjellige UNIX -varianter. LispWorks tilbyr et integrert utviklingsmiljø (IDE) (tilgjengelig for de fleste plattformer, men ikke for iOS og Android) og omfattende muligheter for applikasjonslevering.
mocl
for iOS, Android og macOS.
Åpen slekt
for DEC Alpha.
Scieneer Common Lisp
som er designet for vitenskapelig databehandling med høy ytelse.

Fritt distribuerbare implementeringer

Armed Bear Common Lisp (ABCL)
En CL -implementering som kjører på Java Virtual Machine . Den inkluderer en kompilator til Java -bytekode , og gir tilgang til Java -biblioteker fra CL. Det var tidligere bare en del av Armed Bear J Editor .
CLISP
En bytekode-kompilerende implementering, bærbar og kjører på flere Unix og Unix-lignende systemer (inkludert macOS ), samt Microsoft Windows og flere andre systemer.
Clozure CL (CCL)
Opprinnelig en gratis og åpen kildekode med Macintosh Common Lisp. Som historien antyder, ble CCL skrevet for Macintosh, men Clozure CL kjører nå på macOS , FreeBSD , Linux , Solaris og Windows . 32 og 64 bit x86 porter støttes på hver plattform. I tillegg er det Power PC -porter for Mac OS og Linux. CCL var tidligere kjent som OpenMCL, men det navnet brukes ikke lenger for å unngå forvirring med åpen kildekode -versjonen av Macintosh Common Lisp.
CMUCL
Opprinnelig fra Carnegie Mellon University , nå vedlikeholdt som gratis og åpen kildekode-programvare av en gruppe frivillige. CMUCL bruker en rask native-code-kompilator. Den er tilgjengelig på Linux og BSD for Intel x86; Linux for Alpha; macOS for Intel x86 og PowerPC; og Solaris, IRIX og HP-UX på sine opprinnelige plattformer.
Corman Common Lisp
for Microsoft Windows. I januar 2015 har Corman Lisp blitt publisert under MIT -lisens.
Embeddable Common Lisp (ECL)
ECL inkluderer en bytekode -tolk og kompilator. Den kan også kompilere Lisp -kode til maskinkode via en C -kompilator. ECL kompilerer deretter Lisp -koden til C, kompilerer C -koden med en C -kompilator og kan deretter laste den resulterende maskinkoden. Det er også mulig å bygge inn ECL i C -programmer og C -kode i Common Lisp -programmer.
GNU Common Lisp (GCL)
Den GNU Prosjektets Lisp kompilatoren. GCL er ikke fullt ut ANSI-kompatibel ennå, men er imidlertid implementering av valg for flere store prosjekter, inkludert de matematiske verktøyene Maxima , AXIOM og (historisk) ACL2 . GCL kjører på Linux under elleve forskjellige arkitekturer, og også under Windows, Solaris og FreeBSD .
Macintosh Common Lisp (MCL)
Versjon 5.2 for Apple Macintosh -datamaskiner med en PowerPC -prosessor som kjører Mac OS X er åpen kildekode. RMCL (basert på MCL 5.2) kjører på Intel-baserte Apple Macintosh-datamaskiner ved bruk av Rosetta binære oversetter fra Apple.
ManKai Common Lisp (MKCL)
En gren av ECL . MKCL legger vekt på pålitelighet, stabilitet og generell kodekvalitet gjennom et sterkt omarbeidet, innfødt multi-threaded, runtime-system. På Linux har MKCL et fullt POSIX -kompatibelt kjøretidssystem.
Movitz
Implementerer et Lisp -miljø for x86 -datamaskiner uten å stole på noe underliggende operativsystem.
Poplog
Poplog implementerer en versjon av CL, med POP-11 , og eventuelt Prolog og Standard ML (SML), som tillater programmering av blandet språk. For alle er implementeringsspråket POP-11, som kompileres trinnvis. Den har også en integrert Emacs -lignende editor som kommuniserer med kompilatoren.
Steel Bank Common Lisp (SBCL)
En gren fra CMUCL . "Stort sett skiller SBCL seg fra CMU CL ved større vekt på vedlikehold." SBCL kjører på plattformene CMUCL gjør, unntatt HP/UX; i tillegg kjører den på Linux for AMD64, PowerPC, SPARC, MIPS, Windows x86 og har eksperimentell støtte for å kjøre på Windows AMD64. SBCL bruker ikke tolk som standard; alle uttrykk kompileres til opprinnelig kode med mindre brukeren slår på tolken. SBCL -kompilatoren genererer rask opprinnelig kode i henhold til en tidligere versjon av The Computer Language Benchmarks Game .
Ufasoft Common Lisp
CLISP -port for Windows -plattform med kjerne skrevet i C ++.

Andre implementeringer

Austin Kyoto Common Lisp
en utvikling av Kyoto Common Lisp av Bill Schelter
Butterfly Common Lisp
en implementering skrevet i Scheme for BBN Butterfly multi-prosessor datamaskin
KLIKK
en vanlig Lisp til C -kompilator
CLOE
Vanlig Lisp for PC -er av Symbolics
Codemist Common Lisp
brukes til den kommersielle versjonen av datamaskinalgebra -systemet Axiom
Opplev Vanlig Lisp
en tidlig implementering for Apple Macintosh av ExperTelligence
Golden Common Lisp
en implementering for PCen av GoldHill Inc.
Ibuki Common Lisp
en kommersialisert versjon av Kyoto Common Lisp
Kyoto Common Lisp
den første Common Lisp -kompilatoren som brukte C som målspråk. GCL, ECL og MKCL stammer fra denne Common Lisp -implementeringen.
L
en liten versjon av Common Lisp for innebygde systemer utviklet av IS Robotics, nå iRobot
Lisp Machines (fra Symbolics , TI og Xerox)
ga implementeringer av Common Lisp i tillegg til deres opprinnelige Lisp -dialekt (Lisp Machine Lisp eller Interlisp). CLOS var også tilgjengelig. Symbolics gir en forbedret versjon Common Lisp.
Procyon Common Lisp
en implementering for Windows og Mac OS, brukt av Franz for deres Windows -port Allegro CL
Star Sapphire Common LISP
en implementering for PC
SubL
en variant av Common Lisp som brukes for implementering av det Cyc kunnskapsbaserte systemet
Top Level Common Lisp
en tidlig implementering for samtidig utførelse
WCL
en implementering av et delt bibliotek
VAX Common Lisp
Digital Equipment Corporation 's implementering som kjørte på VAX -systemer som kjører VMS eller ULTRIX
XLISP
en implementering skrevet av David Betz

applikasjoner

Common Lisp brukes til å utvikle forskningsprogrammer (ofte innen kunstig intelligens ), for rask utvikling av prototyper eller for distribuerte applikasjoner.

Common Lisp brukes i mange kommersielle applikasjoner, inkludert Yahoo! Store web-handelsside, som opprinnelig involverte Paul Graham og senere ble skrevet om i C ++ og Perl . Andre bemerkelsesverdige eksempler inkluderer:

Det finnes også programmer med åpen kildekode skrevet i Common Lisp, for eksempel:

Se også

Referanser

Bibliografi

En kronologisk liste over bøker som er utgitt (eller skal publiseres) om Common Lisp (språket) eller om programmering med Common Lisp (spesielt AI -programmering).

Eksterne linker