Gemeenschappelijke Lisp

Van Wikipedia, de gratis encyclopedie
Spring naar navigatie Spring naar zoeken
Gemeenschappelijke Lisp
logo

Onofficieel Lisp-logo
Basis data
paradigma's : multiparadigmatisch: functioneel , procedureel , modulair , objectgeoriënteerd , reflexief
Jaar van uitgave: 1984, 1994 voorANSI Common Lisp
Ontwerper: Scott E. Fahlman , Richard P. Gabriel , David Moon
Ontwikkelaar: ANSI X3J13 commissie
Typen : dynamisch
Belangrijke implementaties : Allegro Common Lisp , Armed Bear Common Lisp , CLISP , Clozure CL, CMUCL , Embeddable Common Lisp , GNU Common Lisp , LispWorks , Movitz , Poplog , Scieneer Common Lisp , Steel Bank Common Lisp
dialecten: CLtL1, CLtL2, ANSI Gemeenschappelijk Lisp
Beïnvloed door: Lisp Machine Lisp, Maclisp , Symboliek ZetaLisp , Schema , Interlisp
Aangedaan: Dylan , Eulisp , ISLisp , Ruby , SubL
common-lisp.net

Common Lisp (vaak afgekort tot CL ) is een programmeertaal met meerdere paradigma 's binnen de Lisp-talenfamilie . Het kwam voort uit een poging die in 1981 onder leiding van Scott Fahlman [1] werd gelanceerd om een ​​gestandaardiseerd dialect te vinden als de opvolger van Maclisp (en zijn varianten) [2] en werd in 1994 gestandaardiseerd als ANSI Common Lisp . [3]

verhaal

In 1984 verscheen de eerste editie van het boek Common Lisp: The Language ( kortweg CLTL1 ), dat de eerste specificatie van de taal bevatte. Kort daarna werd deANSI -subcommissie X3J13 opgericht om op basis van de specificatie een standaard te creëren. De tweede druk van het boek ( CLTL2 ) uit 1990 bevatte wijzigingen door de subcommissie en beschrijft een tussentijdse status . De definitieve versie van de standaard verscheen in 1994 ( ANSI/X3.226-1994 ). [4] [2]

syntaxis

Common Lisp gebruikt S-expressies om zowel de broncode als de gegevens weer te geven. Functie- en macro-aanroepen worden geschreven als lijsten die de naam van de functie of macro als eerste element bevatten. Opmerkingen zijn met ; geïntroduceerd of met #| |# ingesloten.

 ;; Voeg 2 en 2 . toe
( + 2 2 )

;; Definieer de variabele * p * als 3.1415
( defparameter * p * 3.1415 )

;; Stel de eerder gedefinieerde variabele * p * in op 42
( setq * p * 42 )

;; Een functie die zijn argument kwadrateert
( defun vierkant ( x )
  ( * x x ))

;; Vierkant 3
( vierkant 3 )

Gegevenstypen

Common Lisp ondersteunt een breed scala aan gegevenstypen, meer dan veel andere talen. Deze typen zijn hiërarchisch gerangschikt.

scalaire typen

Nummers in Common Lisp zijn van het type nummer . Subtypes van getallen zijn onder meer integer (hele getallen), ratio (rationele getallen of breuken), reëel ( drijvende-kommagetallen ) en complex (complexe getallen). Rekenen met gehele getallen en breuken is willekeurig nauwkeurig. Een klasse programmafouten in talen als C, die worden veroorzaakt door overlopende gehele getallen, wordt dus praktisch geëlimineerd in Common Lisp.

De Common Lisp lettertype is niet beperkt tot ASCII , dat is niet verwonderlijk, omdat Lisp is ouder dan ASCII. Veel implementaties ondersteunen Unicode . [5]

Het symbooltype is een speciaal kenmerk van bijna alle Lisp-dialecten, maar is grotendeels onbekend in andere taalfamilies. Een symbool in Common Lisp is vergelijkbaar met een identifier in andere talen omdat het als variabele wordt gebruikt en er waarden aan kunnen worden toegewezen. Maar omdat het eersteklas objecten zijn, kunnen ze ook voor andere doeleinden worden gebruikt.

Data structuren

Common Lisp kent een aantal voorgedefinieerde datastructuren. Deze omvatten hashtabellen, datanetwerken (zogenaamde structuren ), klassen, streams, padnamen, multidimensionale arrays en reeksen. De laatste omvatten klassieke Lisp-lijsten, maar ook bitvectoren, algemene vectoren en tekenreeksen.

  • Hashtabellen slaan toewijzingen van objecten op. Er kan bijvoorbeeld een nummer worden toegewezen aan een tekenreeks of een functie kan worden toegewezen aan een symbool.
  • Gegevensnetwerken zijn een willekeurig aantal geneste structuren die gegevens van elk type kunnen bevatten. Een enkel netwerk slaat waarden op in een vast aantal zogenaamde slots die later naar believen kunnen worden gewijzigd en geëxtraheerd. Structuren vormen een eenvoudige overervingshiërarchie.
  • Arrays zijn n-dimensionale zoeklijsten die ofwel allemaal van hetzelfde type moeten zijn (in zogenaamde homogene arrays ) of - in het geval van heterogene arrays - op enigerlei wijze gecombineerd kunnen worden. Ze ondersteunen de gebruikelijke matrixrekenkundige bewerkingen en kunnen ook in dynamisch veranderlijke grootten voorkomen. Compilers kunnen doorgaans berekeningen met homogene arrays die qua grootte vastliggen zeer efficiënt vertalen.
  • Vectoren zijn het speciale geval van arrays met slechts één dimensie. Er zijn vooraf gedefinieerde vectortypen, zoals de tekenreeks, een vector van tekens of de bitvector, een handige en efficiënte manier om bits op te slaan en ermee te rekenen.

Klassieke Lisp- lijsten bestaan ​​uit zogenaamde CONS-cellen. Dit betekent geordende paren waarvan het eerste element, de zogenaamde CAR, een waarde bevat, terwijl het tweede element, de zogenaamde CDR, verwijst naar de volgende cel of, in het geval van het einde van de lijst, naar het symbool NIL . In principe zijn Lisp-lijsten gewoon gekoppelde lijsten. Tegenwoordig worden ze voornamelijk gebruikt om code weer te geven en te manipuleren met behulp van macro's - een mechanisme dat Common Lisp-programmeurs een mate van vrijheid biedt die is voorbehouden aan de taalontwerper in andere programmeertalen. Complexere gegevens worden meestal opgeslagen in klassen of groepen, en gegevens waarmee u efficiënt wilt berekenen in arrays of vectoren.

Pakketsysteem:

Common Lisp bevat een pakketsysteem waarmee u naamruimten voor symbolen kunt definiëren en deze laatste kunt importeren en exporteren. Daarnaast kunnen voor het gemak en de leesbaarheid afkortingen of bijnamen aan pakketten worden toegekend.

Symbolen die zich in andere naamruimten dan de huidige bevinden, kunnen worden geadresseerd door de respectieve pakketnaam voor hun naam te schrijven, gescheiden door een dubbele punt. De naam cl: format verwijst bijvoorbeeld altijd naar het FORMAT-symbool in het CL-pakket, ongeacht of een functie die ook FORMAT wordt genoemd, in het huidige pakket mag zijn gedefinieerd.

Functies

In Common Lisp zijn functies normale objecten die als parameters kunnen worden doorgegeven of door een functie kunnen worden geretourneerd. Hierdoor kunnen zeer algemene bewerkingen worden uitgedrukt.

Het evaluatieschema voor functies is heel eenvoudig. Als er een uitdrukking is van de vorm ( F A1 A2 ) , kan worden aangenomen dat het symbool met de naam F:

  1. is een bijzondere exploitant.
  2. is een reeds gedefinieerde macro.
  3. is de naam van een functie.

Als F de naam van een functie is, dan worden de argumenten A1, A2, ... van links naar rechts geëvalueerd en als parameters naar de betreffende functie overgedragen.

Definieer nieuwe functies

De defun macro definieert functies. Een functiedefinitie beschrijft de naam van de functie, de namen van alle argumenten en de hoofdtekst van de functie:

 ( defun vierkant ( x )
   ( * x x ))

Functiedeclaraties kunnen declaraties bevatten die de compiler informatie geven over optimalisatie, onder andere met betrekking tot typen. Daarnaast kunnen zogenaamde documentatiestrings, of kortweg docstrings , worden gespecificeerd die het runtime-systeem kan gebruiken om interactieve hulp aan de gebruiker te bieden.

 ( defun vierkant ( x )
   "Bereken het kwadraat van X."
   ( declareer ( fixnum x ) ; type specificatie voor een argument
            ( optimaliseren ( snelheid 3 )
                      ( debug 0 )
                      ( veiligheid 1 )))
   ( het fixnum ( * x x ))) ; Typespecificatie voor de retourwaarde

Anonieme functies (functies zonder expliciete namen) worden gegenereerd met behulp van de lambda operator (volgens de lambda-calculus ). Veel Common Lisp-programma's gebruiken functies van een hogere orde waarbij het handig is om anonieme functies als argumenten door te geven.

Lezen, compileren en runtime

Als een speciale functie in vergelijking met andere talen, stelt Common Lisp de programmeur in staat zijn eigen code uit te voeren, niet alleen tijdens runtime, maar ook in drie andere fasen, lezen, compileren en laden.

Tijd lezen en macro's lezen

Code die tijdens het lezen wordt uitgevoerd, kan de parser besturen en zo zijn eigen syntaxis definiëren. Meestal is dit in de vorm van zogenaamde macro's lezen (lees macro's of reader macro's), de individuele karakters worden toegewezen en kunnen elke tekst in code omzetten. Er zijn bijvoorbeeld leesmacro's die de bekende infix-syntaxis voor rekenkundige uitdrukkingen bieden en uitdrukkingen zoals 3 * ( 4 + 3 ) geldig maken in Common Lisp.

Een programmeur kan ook code ad hoc uitvoeren tijdens het lezen door gebruik te maken van de standaard leesmacro #. geserveerd. De uitdrukking ( + #. ( print 10 ) 20 ) bijvoorbeeld, geeft niet alleen 30 terug wanneer deze wordt uitgevoerd, maar drukt ook het getal 10 op het scherm af terwijl de uitdrukking wordt gelezen. Aangezien deze functionaliteit het louter inlezen van uitingen, bijvoorbeeld ontvangen via een netwerk, al een veiligheidsprobleem maakt, kan deze via de variabele *read-eval* worden in- en uitgeschakeld.

Compileer tijd en Lisp macro's

Zoals bijna alle Lisp-dialecten en in tegenstelling tot de meeste andere talen, biedt Common Lisp de mogelijkheid om code uit te voeren tijdens het compileren en zo de S-expressies die de programmacode vertegenwoordigen naar wens te manipuleren, genereren en transformeren. De taal levert hiervoor vooral de zogenaamde macro's . Aangezien Lisp-macro's werken op het niveau van de programmastructuur en dus perfect in de taal zelf passen, zijn ze niet te vergelijken met de macrosystemen van andere talen en zouden ze dus eigenlijk een eigen naam moeten verdienen.

Macro's hebben toegang tot alle Common Lisp-functionaliteiten, inclusief zelfgedefinieerde functies, waardoor ze een breed scala aan opties hebben voor het converteren van code. Common Lisp-programmeurs gebruiken vaak macro's om applicatiespecifieke talen in Common Lisp te bouwen die naadloos aansluiten op de taal en deze expressiever maken. Het is ook mogelijk om macro's te gebruiken om nieuwe programmeerparadigma's te bieden.

Het op klassen gebaseerde objectsysteem CLOS in de ANSI-standaard kan bijvoorbeeld op een metacirculaire manier worden geïmplementeerd als een set generieke functies en klassen waarover een laag macro's wordt gelegd om de gemakkelijke definitie van klassen, generieke functies mogelijk te maken. en andere CLOS-taalelementen. Hoe de implementatie van CLOS er in werkelijkheid uitziet, hangt echter af van het gebruikte Lisp-systeem en wordt niet voorgeschreven door de norm.

Variabele binding

Net als de meeste andere talen, heeft Common Lisp het concept om waarden aan namen te koppelen. Het breidt dit concept echter uit met twee aspecten: enerzijds kunnen variabelenamen als normale objecten worden gebruikt en bijvoorbeeld worden doorgegeven of opgeslagen, en anderzijds zijn er twee zeer verschillende vormen van variabele binding beschikbaar. Het eerste aspect werd al uitgelegd in de paragraaf over datatypes.

Lexicale binding en lexicale variabelen

Common Lisp gebruikt standaard lexicale scoping (lexicale scoping), een concept dat Scheme in zijn tijd introduceerde in de Lisp-wereld. Namen zijn altijd geldig in een specifiek, op zichzelf staand deel van de brontekst, zodat de binding niet kan overlappen met eerder of toekomstige gedefinieerde bindingen.

Aangezien koppelingen in Common Lisp op elk moment kunnen worden gemaakt en overlappingen zijn uitgesloten, is het mogelijk om ze te gebruiken om gegevensstructuren te creëren door alleen functiedeclaraties te maken. Daarbij profiteert men van een effect dat bekend staat als sluiting : het automatisch vastleggen van banden. Sluitingen zijn functies en hun relaties.

Als u bijvoorbeeld de my-cons operator wilt definiëren die een paar van twee waarden genereert, kunt u het idee krijgen om deze een anonieme functie te laten retourneren (d.w.z. een lambda expressie) die de twee waarden heeft vastgelegd, en op request, dwz als het wordt aangeroepen met een argument num , retourneert het een van de twee (namelijk de eerste waarde voor num = 0 en anders de tweede):

 ( niet mijn nadelen ( x y )
   ( lambda ( aantal )
     ( if ( nul num )
         x
         j )))

De waarden die door deze operator worden gegenereerd, kunnen bijvoorbeeld als volgt worden gebruikt (let op: funcall is de operator voor het aanroepen van functies die zijn opgeslagen in variabelen):

 ( defvar * paar-A * ( mijn nadelen 100 200 )) ; => * paar-A *
 ( defvar * paar-B * ( mijn-tegens * paar-A * 42 )) ; => * paar-B *
 ( print ( funcall * paar-A * 0 )) ; => 100
 ( print ( funcall * paar-A * 1 )) ; => 200
 ( print ( funcall * paar-B * 0 )) ; => (LAMBDA ...)
 ( print ( funcall ( funcall * paar-B * 0 ) 1 )) ; => 200

Houd er rekening mee dat de verschillende bindingen van *paar-A* en *paar-B* voor de namen X en Y elkaar niet overlappen, maar voor elke oproep van my-cons opnieuw worden gemaakt. Het is ook mogelijk om dergelijke bindingen achteraf te wijzigen, zoals het volgende codefragment laat zien, dat een verborgen binding met de naam zähler definieert:

 ( laat (( teller 0 ))
   ( defun counter + () ( incl. counter ))
   ( defun counter- () ( decf counter ))
   (Defun gat teller () teller))

 ( get-counter ) ; => 0
 ( teller + ) ; => 1
 ( teller + ) ; => 2
 ( get-counter ) ; => 2
 ( teller- ) ; => 1
 ( get-counter ) ; => 1

 ( setf teller 100 ) ; => fout, omdat teller niet globaal is gedefinieerd.

Dynamische binding en speciale variabelen

Common Lisp ondersteunt nog steeds dynamische binding die werd gebruikt in eerdere Lisp-dialecten, die globale namen gebruikten maar bindingen met beperkte waarde in de tijd. Vanwege de mogelijke naamconflicten wordt dit soort waardebinding alleen in speciale gevallen gebruikt, daarom in deze context, ook gesproken door speciale variabele, dus speciale variabelen.

Het is gebruikelijk om de namen van speciale variabelen tussen twee asterisken te zetten om hun aard in één oogopslag duidelijk te maken voor de lezer en om conflicten met lexicale variabelen te voorkomen.

Het voordeel van speciale variabelen ligt juist in hun tijdelijke maar globale effecten. Ze kunnen ook worden gezien als een vorm van impliciete argumenten die door de hele gespreksboom worden doorgegeven zonder dat ze op elk niveau expliciet genoemd hoeven te worden.

Een voorbeeld maakt het principe begrijpelijker. Laten we aannemen dat een functie print-date vooraf print-date gedefinieerd (bijvoorbeeld in een bibliotheek van derden) die de huidige datum in een bijzonder mooie vorm op de standaarduitvoer schrijft. Nu willen we deze onvervangbare functie gebruiken, maar in plaats daarvan het resultaat naar een bestand schrijven. Als de ontwikkelaar van de functie niet aan deze mogelijkheid heeft gedacht en een overeenkomstige parameter heeft ingebouwd, kunnen we gebruik maken van het feit dat de standaarduitvoer als een speciale variabele is gedefinieerd:

 ( let (( * standard-output * output- file ))
   ( formaat t "~ & De huidige datum is:" )
   ( afdrukdatum ))

Het feit dat we *standard-output* dynamisch binden aan ons ausgabedatei zorgt ervoor dat elke standaard *standard-output* komt waar we hem hebben willen. let zorgt ervoor dat de binding wordt teruggezet naar de oorspronkelijke staat nadat het blok is beëindigd.

Speciale variabelen hebben nog een ander interessant aspect dat aan het licht komt in het geval van concurrency (taaluitbreiding naar ANSI Common Lisp) en het onmogelijk of op zijn minst zeer moeilijk maakt om ze boven het werkelijke taalniveau te implementeren: hun bindingen zijn thread-local. Dit betekent dat twee parallelle programmareeksen die toegang hebben tot dezelfde naam, verschillende bindingen voor deze naam kunnen gebruiken, hoewel namen van speciale variabelen globaal zijn. Bovenstaande code zou dus ook werken als een andere thread het idee zou krijgen tussen de tweede en de derde regel om *standard-output* aan zijn eigen bestand te binden. Aangezien de twee bindingen onafhankelijk van elkaar zijn, interfereren de twee draden niet met elkaar. Men spreekt hier van bindende superpositie, omdat de bestaande binding niet wordt gewijzigd, maar nieuwe bindingen altijd over de oude worden gelegd en weer worden verwijderd wanneer het let blok wordt beëindigd.

Vergelijking met andere Lisp-dialecten

Common Lisp is voornamelijk gebaseerd op het Maclisp- dialect dat is ontwikkeld aan hetMIT , maar werd ook sterk beïnvloed door Symbolics ZetaLisp , InterLisp en Scheme . Guy Steele , de voorzitter van de Common Lisp Committee, ontwierp de regeling samen met Gerald Jay Sussman in de jaren zeventig.

In tegenstelling tot de meeste andere Lisp-dialecten, die traditioneel alleen dynamische variabele binding gebruikten, gebruikt Common Lisp voornamelijk de lexicale variabele binding (lexicale reikwijdte) die in Schema is geïntroduceerd. Zie sectie Variabele binding .

Beschikbaarheid van pakketten en bibliotheken

Net als andere programmeertalen (bijvoorbeeld CPAN in het geval van Perl ), is ASDF-Install [6] een verzameling Common Lisp-modules die automatisch kan worden geïnstalleerd.

Er zijn talloze pakketten voor de meest uiteenlopende toepassingsgebieden, bijvoorbeeld voor webontwikkeling, 3D-graphics, tekstverwerking en nog veel meer. Bindingen voor GUI- toolkits zoals GTK+ , Cocoa , Microsoft Windows , Tk en andere zijn ook beschikbaar, evenals implementaties van de Common Lisp Interface Manager (kortweg CLIM ) en een binding voor .NET .

Ontwikkelomgevingen

Talen met op S-expressie gebaseerde syntaxis zoals Common Lisp zijn dankzij hun syntactische eenvoud bijzonder geschikt voor het gebruik van krachtige code-analyse- en beheerprogramma's. Bovendien kunnen ontwikkelomgevingen er op verschillende manieren mee worden geïntegreerd, inclusief het mixen van programma's en IDE-code indien nodig.

Een veelgebruikte gratis ontwikkelomgeving is SLIME , [7] de Superior Lisp Interaction Mode for Emacs , die het gebruikelijke interactieve softwareontwikkelingsproces in Common Lisp goed ondersteunt. Het ontbreken van refactoring-tools wordt slechts door enkele lispers als een groot probleem gezien, vooral omdat de taal zelf met zijn macro's hiervoor op broncodeniveau goede mogelijkheden biedt.

Er zijn ook tal van commerciële Common Lisp-ontwikkelingssystemen, zoals Allegro Common Lisp van Franz Inc. en LispWorks van LispWorks Ltd.

literatuur

Duitstalige boeken

  • RA Brooks: Programmeren in Common Lisp , Oldenbourg Wissenschaftsverlag, 1987, ISBN 3-486-20269-3
  • Herbert Stoyan: programmeermethoden van kunstmatige intelligentie, deel 1 , Springer, 1988, ISBN 978-3-540-19418-7
  • Rüdiger Esser, Elisabeth Feldmar: LISP, case studies met toepassingen in kunstmatige intelligentie , Vieweg, 1989, ISBN 3-528-04585-X
  • Herbert Stoyan: programmeermethoden van kunstmatige intelligentie, deel 2 , Springer, 1991, ISBN 978-3-540-52469-4
  • Otto Mayer: Programmeren in Common Lisp , Spektrum Akademischer Verlag, 1995, ISBN 3-86025-710-2
  • Paul Graham: ANSI Gemeenschappelijke Lisp . Markt + Technik Verlag, 1997, ISBN 3-827-29543-2
  • Conrad Barski: Land of Lisp: leer eenvoudig Lisp-programmeren en programmeer originele spellen , mitp, 2011, ISBN 978-3-8266-9163-8
  • Patrick M. Krusenotto: Functioneel programmeren en metaprogrammeren, interactief in Common Lisp , Springer Fachmedien Wiesbaden 2016, ISBN 978-3-658-13743-4

web links

Individueel bewijs

  1. Richard P. Gabriel noemt Scott Fahlman als de leider van het Common Lisp Project
  2. ^ Een b Guy Steele Jr:.Common Lisp: The Language. Prentice Hall, ISBN 0-13-152414-3 .
  3. Geschiedenis in Common Lisp HyperSpec, geraadpleegd op 6 november 2016
  4. ^ Paul Graham : Op Lisp . Prentice Hall, 1993, ISBN 0-13-030552-9
  5. zie cliki.net/Unicode Support (Engels)
  6. cliki.net/asdf
  7. common-lisp.net