Common Lisp (forkortet CL) er en specifikation af Lisp- sproget standardiseret af ANSI .
Common Lisp er en dialekt af Lisp standardiseret af ANSI X3.226-1994. Udviklet til at standardisere de divergerende varianter af Lisp, der kom før den, er det ikke en implementering, men en specifikation, som Lisp-implementeringer forsøger at overholde. Det forkortes ofte som CL .
Common Lisp er et programmeringssprog til generelle formål, i modsætning til dialekter af Lisp som Emacs Lisp og AutoLisp , som er udvidelsessprog indlejret i bestemte produkter. I modsætning til mange ældre Lisps, men ligesom Scheme , bruger Common Lisp det standard leksikale omfang til variabler.
Common Lisp er et multi-paradigme programmeringssprog, der:
Som ethvert sprog i Lisp-familien bruger Common Lisp S-udtryk til at betegne både kode og bestemte datastrukturer. Påkald af funktioner, specialformularer og makroer skrives i bogstavelig liste-syntaks med funktionsnavnet (hhv. Speciel form og makro) som i disse eksempler:
(+ 2 2) ; ajoute 2 et 2, renvoie 4 (defvar e) ; définit la variable e (setf e 2.7182817) ; assigne 2.7182817 à la variable e (setf e 'rouge) ; assigne le mot rouge à la variable e nil ; valeur booléenne false et liste vide t ; valeur booléenne true (if (< x y) y ; résultat si la condition est vérifiée (ici, si x < y) x) ; résultat par défaut (defun carre (x) (* x x)) ; définit une fonction qui met un nombre au carré (carre 3) ; exécution de la fonction : retourne 9Taltyper inkluderer heltal , rationelle tal, flydende tal og komplekse tal . Common Lisp bruger store tal til at repræsentere numeriske værdier af vilkårlig størrelse og præcision. Den rationelle type repræsenterer fraktioner nøjagtigt. Common Lisp konverterer automatisk numeriske værdier mellem disse typer korrekt. Her er det numeriske tårn , det vil sige hierarkiet med numeriske typer Common Lisp:
complex ratio fixnum / / / number <--+-real--+-rational--+-integer--+-bignum \ \ \ (1) signed-byte--unsigned-byte--bit \ float--+-short-float \-single-float \-double-float \-long-float(1) heltal og signeret byte er usammenhængende typespecifikationer; dog er domænerne de samme.
For eksempel udtrykket
(+ (sqrt -2) (/ 6 4))Vend tilbage
#C(3/2 1.4142135)det vil sige et komplekst tal, hvis imaginære del er float 1.4142135, og den reelle del er den rationelle 3/2.
TegnCommon Lisp- karaktertypen er ikke begrænset til ASCII- tegn ; dette er ikke overraskende, da Lisp er ældre end ASCII. Nogle moderne implementeringer understøtter Unicode- tegn .
SymbolerDet symbol typen er fælles for Lisp sprog, men stort set ukendt udenfor. Et symbol er et navngivet, unikt objekt i forhold til et navneområde . Bogstavelige symboler i Lisp bruges som variable identifikatorer; de er dog mere generelle og kan også bruges alene som genstande. Når et symbol evalueres, returneres dets værdi som en variabel.
Eksistensen af symboler som datatype gør det let at skrive makroer (som udfører kodetransformationer på kompileringstidspunktet) og implementere symbolske beregningssystemer. Faktisk var Lisp det første sprog til implementering af computeralgebrasystemer. Meget komplette computeralgebra-systemer er skrevet i Lisp ( Maxima og Axiom , og for symbolske transformationer bærer de generelt sammenligning med den populære Mathematica og Maple ).
Der er specielle tilfælde:
Eksempler:
foo ;; -> La variable FOO n'a pas de valeur. (function foo) ;; -> La fonction FOO n'est pas définie.QUOTE-operatøren beskytter symboler mod evaluering (når vi vil bruge et symbol til sig selv):
(quote foo) ;; -> FOO 'foo ;; -> FOODu kan spørge, om et symbol er knyttet til en værdi eller en funktion:
(boundp 'foo) ;; -> NIL (pas de valeur liée) (fboundp 'foo) ;; -> NIL (aucune fonction nommée FOO n'existe)Symbol-værdi tilknytning:
(defparameter foo 77) ;; -> FOO foo ;; -> 77Symbol-funktionssammenslutning:
(defun foo (bar) (1+ bar)) ;; -> FOOKaldelse af FOO-funktionen med værdien FOO (illustrerer, at et symbol har separate slots til værdier og funktioner):
(foo foo) ;; -> 78 (boundp 'foo) ;; -> T (fboundp 'foo) ;; -> T (function foo) ;; -> #<CLOSURE FOO (BAR) (DECLARE (SYSTEM::IN-DEFUN FOO)) (BLOCK FOO (1+ BAR))>Rekursion:
(defun factoriel (n) (if (= n 0) 1 (* n (factoriel(- n 1)))))Andet:
(first '(7 3 10)) ;;-> 7 (rest '(20 2 45)) ;;-> (2 45) (endp '()) ;;-> T (endp '(5)) ;;-> NIL liste non videDe sekvenser er en abstrakt, som repræsenterer et ordnet samling af elementer. Konkrete typer afledt af sekvens er lister og vektorer (inklusive bitvektorer og strenge). Mange funktioner er tilgængelige for sekvenser. Det er dog ikke muligt for programmøren at definere sine egne sekvensundertyper. Der foreslås en udvidelse af standarden, der tillader definition af nye sekvenser (ca. 2007).
Par, listerDen liste over Common Lisp er ikke en datatype, men er lavet af Conses (flertal), også kaldet celle idioter eller par . En ulempe er en struktur med to elementer, der er tilgængelige med funktionerne bil og cdr (eller endda først og hvile ). En liste er derfor en kæde af sammenkædede conses , hvor cdr for hvert cons peger på det næste element, og det sidste cdr peger på NIL-værdien. De Conses let kan anvendes til at implementere træerne eller komplekst datastrukturer; selv om det i sidstnævnte tilfælde anbefales at bruge strukturer eller klasser.
Træet (1 (2/7 3.14) A "foo") er repræsenteret af følgende streng af CONS:
Det kan bygges på forskellige måder, vi nævner to:
(list 1 (list 2/7 3.14) 'a "foo") (cons 1 (cons (cons 2/7 (cons 3.14 NIL)) (cons 'a (cons "foo" NIL))))Lister eller cykliske strukturer konstrueret af par har ikke en bogstavelig gengivelse, men de kan udskrives:
(setf *print-circle* t) ; active l'affichage des structures circulaires (évite le bouclage infini) (let ((list (copy-list '(a b c)))) (setf (cdr (last list)) list)) ; => #1=(A B C . #1#) MalerierCommon Lisp understøtter arrays med vilkårlige dimensioner og kan også dynamisk ændre størrelsen på arrays. Disse flerdimensionelle arrays kan for eksempel bruges til matrixberegning. Kun endimensionelle arrays (kaldet vektorer) er en sekvensundertype.
Tabeller kan specialiseres efter typen af elementer, de indeholder. Især tilvejebringes bitvektorer og tegnvektorer (strenge) som standard af sproget.
Eksempel på vektoroprettelse:
(setf v (make-array 3)) ; creation du vecteur (setf (aref v 0) 1) ; v[0]:= 1 (aref v 0) ; -> 1Eksempel på oprettelse af et tredimensionelt array (4 x 2 x 3) og initialiseret:
(make-array '(4 2 3) :initial-contents '(((a b c) (1 2 3)) ((d e f) (3 1 2)) ((g h i) (2 3 1)) ((j k l) (0 0 0))))... dette vender tilbage:
#3A(((A B C) (1 2 3)) ((D E F) (3 1 2)) ((G H I) (2 3 1)) ((J K L) (0 0 0))) RegistrererEksempler:
(defstruct livre auteur titre) ; construit une structure "livre" contenant deux champs (make-livre) ; construit la structure en mémoire (setf l (make-livre :auteur 'Hugo)) ; crée un livre dont l'auteur est Hugo et l'associe à la variable l (setf (livre-titre l) 'Miserables) ; associe un titre au livre de Hugo (livre-titre l) ;;-> Miserables Hash-tabellerDe hash tabeller gemmer associationer mellem objekter. Enhver type objekt kan bruges som en nøgle eller værdi. Hash-tabeller, som arrays, ændres automatisk, hvis det er nødvendigt.
PakkerPakkerne ( pakker ) er samlinger af symboler, der primært bruges til at opdele et program i navneområder . En pakke kan eksportere bestemte symboler og markere dem som en del af en offentlig grænseflade. De såkaldte private variabler og metoder til klassiske objektsprog ( indkapslingsprincip ) opnås i Lisp ved at erklære dem i et navneområde uden at eksportere dem.
StrukturerDe strukturer , der ligner structs C og optegnelser (records) Pascal, betegner vilkårlige komplekse datastrukturer med et vilkårligt antal og enhver form for felter (kaldet spalter ). Strukturer understøtter en begrænset form for arv. Med henblik på objektorienteret programmering henviser vi til CLOS .
Klasser og objekterCommon Lisp var det første standardiserede objektsprog (i 1994 af ANSI ). Den del af sproget, der beskæftiger sig med objekter, kaldes CLOS for Common Lisp Object System . De fremtrædende træk ved CLOS er som følger:
CLOS giver dig også mulighed for at definere metaklasser og klasser, for at ændre klassen for et objekt ved kørselstid .
Den fælles Lisp betingelse system bruger CLOS at definere de typer af forhold, der kan opstå på runtime.
Nogle implementeringer tilbyder som en udvidelse af CLOS en meta-objekt-protokol beskrevet i bogen The Art of the Metaobject Protocol .
I Common Lisp er funktioner en type data. For eksempel er det muligt at skrive funktioner, der tager andre funktioner som parametre, og returnere funktioner (vi kalder dem højere orden eller førsteklasses funktioner ). Dette gør det muligt at skrive meget generelle operatører.
EksempelF.eks. Tager funktionen sortering (sortering) en sekvens og en sammenligningsoperator som parameter. Det kan ikke kun bruges til at sortere alle typer data, men også til at sortere datastrukturer efter nøgle.
(sort (list 5 2 6 3 1 4) #'>) ;; -> (6 5 4 3 2 1), en utilisant la fonction > comme opérateur de comparaison (sort `((9 a) (3 b) (4 c)) (lambda (x y) (< (first x) (first y)))) ;; -> ((3 b) (4 c) (9 a)), i.e. la liste triée sur le premier élémentVi kan anvende FOO-funktionen defineret ovenfor til en sekvens:
(mapcar #'foo (list 1 2 3 4 5)) ;; -> (2 3 4 5 6) NavneområderCommon Lisp har et navneområde til henholdsvis funktioner og variabler (i modsætning til f.eks. Scheme , som siges at være "Lisp-1"). Lisp-2 (eller højere) har den fordel, at ingen variabelnavn kan skjule en funktion navn: du kan navngive en variabel ulemper eller endda hvis uden problemer. For at henvise til en funktion som en variabel skal man dog bruge funktionen (funktion ...) eller den tilsvarende notation # 'som i eksemplerne ovenfor. Og for at kalde en funktion, der er bestået som en parameter, skal du bruge funcall .
Ud over funktionerne og variablerne er der et separat navneområde for parrene af blok / retur-fra og tagbody / go-operatorer.
Lad os endelig tilføje, at navneområdet for funktioner faktisk deles mellem selve funktionerne og de forskellige slags makroer.
EvalueringEvalueringsmodellen er enkel: når evaluatoren støder på et udtryk (F A1 A2 ... An), kan symbolet F repræsentere et af disse emner:
Hvis F er en funktion, evalueres parametrene successivt fra venstre mod højre, og funktionen påkaldes med de beregnede værdier for parametrene. For specielle operatører eller makroer afhænger det . Disse operatører har tendens til at kontrollere evalueringen af deres parametre. For eksempel, hvis operatøren ikke evaluerer alle dens parametre, skal den evaluere dens tilstand og derefter en gren af alternativet afhængigt af resultatet.
Lexikal fangstEn leksikal lukning er en funktion, hvis frie variabler fanger linkene til det leksikale miljø, hvori de er defineret. Dette gør det muligt at opbygge funktioner med en intern tilstand (i C bruger vi det statiske nøgleord til at opnå den interne tilstand, men leksikalsk indfangning er ikke mulig). Du kan opbygge enkle genstande fra lukninger, for eksempel en meterfabrik:
(defun fabriquer-compteur () ; fabriquer-compteur renvoie une fonction qui incrémente et affiche sa valeur interne (let ((valeur 0)) ; dans l'environnement de la fabrique, on crée la valeur du compteur (lambda () ; le nouveau compteur lui-même (incf valeur)))) ; ici, la référence à "valeur" capture sa définition dans la fabriqueAndre Common Lisp-datatyper inkluderer:
Common Lisp indeholder også et værktøjssæt til objektorienteret programmering , Common Lisp Object System eller CLOS. Det er derfor muligt at tilføje et uendeligt antal typer.
En makro i Lisp ligner overfladisk en funktion. Makroer tillader Lisp-programmøren at oprette nye syntaktiske former på sproget ved at omdanne deres parametre, der repræsenterer kildekoden. For eksempel giver den følgende makro form af loop until(loop ... to), som er kendt på et sprog som Perl .
Makroer evalueres derfor ikke ved runtime-lignende funktioner, men på kompileringstidspunktet. Returværdien af en makro er dens makroekspansion , det vil sige den kodetransformation, den udførte; dette evalueres under udførelsen af programmet for hvert opkald til makroen.
Makroer kan betragtes som funktioner, der accepterer og returnerer abstrakte syntaks træer (S-udtryk), men styrer evalueringen af deres parametre. Faktisk i et kald til en makro (min-makro (+ 1 2)) vurderes udtrykket (+ 1 2) ikke først, og resultatet sendes som argument, det sendes ligesom til makroen, som kan genbruge det intakt , interpolere det med andre kodefragmenter eller transformere det mere eller mindre fuldstændigt.
Ligesom funktioner kan makroer bruge hele Common Lisp-sproget (og tredjepartsbiblioteker) til at udføre deres transformationsarbejde (til dette kaldes de proceduremakroer ), i modsætning til C-sprogmakroer, som kun tillader erstatning af strenge på kildeniveau uden adgang til hele C-sproget selv.
Hovedinteressen for makroer ligger ikke i små hjælpeprogrammer som eksemplet ovenfor, hvis spredning i Lisp-programmer kan føre til en offuscation- effekt , men i muligheden for at definere indlejrede sprog i Lisp, der er målrettet mod et bestemt applikationsområde. Et klassisk eksempel er inkluderingen på sproget af en udvidelse, der tillader logisk programmering på samme måde som Prolog eller endda begrænsningsprogrammering (tilføjelse af et nyt programmeringsparadigme). Det er faktisk muligt at bygge, ved hjælp af makroer og Lisp-funktioner, en kompilator til et sprog på et højere niveau end basissproget, men som forbliver integreret i sidstnævnte.
Alle applikationsfelter kan drage fordel af teknikken til at skrive et program ved hjælp af en top-down-tilgang , hvor problemet, der skal løses, nedbrydes, indtil det kan udtrykkes i basissproget, og en tilgang stigende ( nedenfra og op ) , hvor det udvider kernesproget med begreber, der letter problemets udtryk. Da der ikke kan leveres noget programmeringssprog til generelle formål med operatører, der er specifikke for en uendelig række specifikke applikationsdomæner, er muligheden for at opbygge et tilpasset sprog ( bottom-up tilgang ) ved forlængelse og genbrug af basen en stor fordel ved Lisp over andre universelle sprog . Makroer er fra dette synspunkt en unik fordel ved sprogene i Lisp-familien. Den lette skrivning af makroer skyldes brugen af S-udtryk til Lisp-syntaks.
Makromekanismen ville ikke være praktisk nok at bruge uden en mekanisme, der tillader at repræsentere Lisp-kode i form af et mønster (model eller endda skabelon ), hvor elementer beregnet eller givet som parametre kan injiceres. Common Lisp tilbyder kvasi-citat , repræsenteret af tegnet `(nævnte backcote ). Selvom backquote-operatøren kan bruges i funktioner, er det i makroer, at det er vigtigt: det forbedrer læsbarheden af koden, der produceres af en makro i betydelige proportioner.
I eksemplet ovenfor blev backquotes brugt til at beregne resultatet. Kroppen af indtil makroen er fuldstændigt repræsenteret af en kodeskabelon ved hjælp af gør iterationsoperatoren . Husk, at DO accepterer tre argumenter: et sæt iterationsvariabler, der er defineret lokalt og itereret parallelt, et sæt klausuler, der gør det muligt at definere betingelserne for iterering af variablerne og stop af sløjfen, og en gruppe - af enhver størrelse - vilkårlige operationer eller handlinger:
do ({var | (var [init-form [[step-form]])}*) (end-test-form result-form*) declaration* {tag | statement}* => result*For indtil-sløjfen er brugen af DO enkel: der er ingen iterationsvariabler, der er nøjagtigt en testklausul, og vi kan tillade, at en gruppe operationer udføres på hver iteration. Den skabelon, der skal produceres, afspejler dette:
`(do () (, test) ,@body))Vi bemærker backquoten forud for DO-udtrykket. I et backquote-udtryk kan vi indsprøjte information på to måder: med operatøren , ( uncote ), der injicerer objektet straks til højre, eller operatøren @ ( splice-uncote ), der tillader en liste til højre og injicerer indholdet på listen på plads.
Resultatet af:
(macroexpand-1 '(until (= (random 10) 0) (write-line "Hello")))er ekspansion
(DO NIL ((= (RANDOM 10) 0)) (WRITE-LINE "Hello"))hvor vi finder skabelonen kombineret med parametrene for opkaldet til makroen. Den endelige udvidelse, produceret af (macroexpand…), indeholder udvidelsen af DO-operatøren selv med hensyn til operatører på lavere niveau. Da DO er en operator, der er defineret af Common Lisp-sproget, er operatorens semantik altid den samme. Men denne udvidelse kan variere fra compiler til compiler. Vi kan se, at makroer allerede er meget udbredt i implementeringen af et rigt og komplekst sprog som Common Lisp.
Almindelige Lisp-makroer er i stand til variabel opsamling , en situation hvor symboler placeret i kroppen af makroekspansionen falder sammen med symboler i den kaldende kontekst. Af denne grund omtales de undertiden som “uhygiejniske” makroer sammenlignet med Schemes “hygiejniske makroer” -system , som sikrer adskillelse mellem disse sæt af symboler.
Nogle gange er variabel indfangning en ønsket effekt; når dette ikke er tilfældet, bør det undgås ved brug af gensymmer eller symboler, hvis unikhed er garanteret.
Common Lisp defineres ved en specifikation (som Ada og C ) snarere end en enkelt implementering (som Perl eller Python ). Der er mange implementeringer, og standarden beskriver, hvor de kan være forskellige af gode grunde.
Derudover leveres implementeringer normalt med forskellige sæt biblioteks "pakker", som giver funktionalitet, der ikke er dækket af standarden. Nogle af disse funktioner blev senere introduceret i standarden, såsom CLOS og LOOP- formularen ; andre forbliver specifikke for disse implementeringer. Mange bekvemmeligheder for den moderne programmør - såsom adgang til TCP / IP- netværk - forbliver uden for standarden, men leveres af forskellige implementeringer med undertiden mindre forskelle. En proces kaldet CLRFI (Common Lisp Request For Improvement), der svarer til Schemes SRFI'er , sigter mod at samle de nyttige funktioner, der er tilbage uden for ANSI-standarden for 1995.
Der er en almindelig misforståelse om, at Common Lisp implementeringer alle er tolke. Faktisk er kompilering en del af sprogspecifikationen . De mest almindelige Lisp-implementeringer kompilerer funktioner til maskinkode. Andre kompilerer for at modsætte koden eller endda til applikationen virtuel maskinkode , hvilket reducerer hastigheden, men forbedrer bærbarheden.
Nogle implementeringer i et UNIX- miljø , som CLISP, kan bruges som script-tolke (eller endda som en shell).
Common Lisp bruges med succes i mange kommercielle applikationer:
Der er også open source-applikationer skrevet i Common Lisp, såsom:
Common Lisp bruges også af mange regeringer og institutioner fra 1901, der handler af typen. Eksempler på brug af NASA inkluderer: