common-lispAan de slag met common-lisp


Opmerkingen

Dit is een eenvoudige hallo wereldfunctie in Common Lisp. In voorbeelden wordt de tekst Hello, World! (zonder aanhalingstekens; gevolgd door een nieuwe regel) naar de standaarduitvoer.

Common Lisp is een programmeertaal die grotendeels interactief wordt gebruikt met behulp van een interface die bekend staat als een REPL. Met de REPL (Read Eval Print Loop) kunt u code typen, deze laten evalueren (uitvoeren) en de resultaten onmiddellijk zien. De prompt voor de REPL (op welk punt men de uit te voeren code typt) wordt aangegeven door CL-USER> . Soms verschijnt er iets anders dan CL-USER vóór de > maar dit is nog steeds een REPL.

Na de prompt komt een code, meestal een enkel woord (bijvoorbeeld een variabelenaam) of een formulier (een lijst met woorden / formulieren tussen ( en ) ) (bijvoorbeeld een functieaanroep of verklaring, enz.). Op de volgende regel staat elke uitvoer die het programma afdrukt (of niets als het programma niets afdrukt) en vervolgens de waarden die worden geretourneerd door de uitdrukking te evalueren. Normaal retourneert een uitdrukking één waarde, maar als het meerdere waarden retourneert, verschijnen deze eenmaal per regel.

versies

Versie Publicatiedatum
Gemeenschappelijke Lisp 1984/01/01
ANSI Common Lisp 1994/01/01

Basisuitdrukkingen

Laten we een basisuitdrukking proberen in de REPL:

CL-USER> (+ 1 2 3)
6
CL-USER> (- 3 1 1)
1
CL-USER> (- 3)
-3
CL-USER> (+ 5.3 (- 3 2) (* 2 2))
10.3
CL-USER> (concatenate 'string "Hello, " "World!")
"Hello, World!"
CL-USER> 
 

De basisbouwsteen van een Common Lisp-programma is de vorm . In deze voorbeelden hebben we functievormen , dat wil zeggen uitdrukkingen, geschreven als lijst, waarin het eerste element een operator (of functie) is en de rest van de elementen de operanden zijn (dit wordt "Prefix Notation" of "Polish Notation" genoemd) “). Formulieren schrijven in de REPL veroorzaakt hun evaluatie. In de voorbeelden ziet u eenvoudige uitdrukkingen waarvan de argumenten constante getallen, strings en symbolen zijn (in het geval van 'string , wat de naam van een type is). Je kunt ook zien dat rekenkundige operatoren een willekeurig aantal argumenten kunnen aannemen.

Het is belangrijk op te merken dat haakjes een integraal onderdeel van de syntaxis zijn en niet vrij kunnen worden gebruikt zoals in andere programmeertalen. Het volgende is bijvoorbeeld een fout:

(+ 5 ((+ 2 4)))
> Error: Car of ((+ 2 4)) is not a function name or lambda-expression. ...
 

In Common Lisp-formulieren kunnen ook gegevens, symbolen, macroformulieren, speciale formulieren en lambda-formulieren zijn. Ze kunnen worden geschreven om te worden geëvalueerd, nul, één of meer waarden retourneren, of kunnen worden ingevoerd als invoer voor een macro, die ze in andere vormen transformeren.

Gemeenschappelijke Lisp leermiddelen

Online boeken

Dit zijn boeken die online vrij toegankelijk zijn.

Online referenties

Offline boeken

Dit zijn boeken die u waarschijnlijk zult moeten kopen of uit een bibliotheek lenen.

Online communities

bibliotheken

Voorverpakte Lisp-omgevingen

Dit zijn Lisp-bewerkingsomgevingen die eenvoudig te installeren zijn en waarmee u aan de slag kunt, omdat alles wat u nodig hebt vooraf is verpakt en vooraf is geconfigureerd.

  • Portacle is een draagbare en multiplatform Common Lisp-omgeving. Het levert een licht aangepaste Emacs met Slime, SBCL (een populaire Common Lisp-implementatie), Quicklisp en Git. Geen installatie nodig, dus het is een zeer snelle en gemakkelijke manier om aan de slag te gaan.
  • Lispbox is een IDE (Emacs + SLIME), Common Lisp-omgeving (Clozure Common Lisp) en library manager (Quicklisp), voorverpakt als archieven voor Windows, Mac OSX en Linux. Afstammeling van "Lisp in a Box" Aanbevolen in het praktische Common Lisp-boek.
  • Niet voorverpakt, maar SLIME verandert Emacs in een Common Lisp IDE en heeft een gebruikershandleiding om u op weg te helpen. Vereist een afzonderlijke Common Lisp-implementatie.

Gemeenschappelijke Lisp-implementaties

In dit gedeelte vindt u enkele veelvoorkomende CL-implementaties en hun handleidingen. Tenzij anders vermeld, zijn dit gratis software-implementaties. Zie ook de Cliki's lijst met gratis software Common Lisp Implementations en Wikipedia's lijst met commerciële Common Lisp Implementations .

Hallo Wereld

Wat volgt is een fragment uit een REPL-sessie met Common Lisp waarin een "Hallo wereld!" functie wordt gedefinieerd en uitgevoerd. Zie de opmerkingen onderaan deze pagina voor een meer gedetailleerde beschrijving van een REPL.

CL-USER> (defun hello ()
           (format t "Hello, World!~%"))
HELLO
CL-USER> (hello)
Hello, World!
NIL
CL-USER> 
 

Dit definieert de "functie" van nul argumenten genaamd hello , die de tekenreeks "Hello, World!" gevolgd door een nieuwe regel naar standaarduitvoer en retour NIL .

Om een functie te definiëren, schrijven we

(defun name (parameters...)
  code...)
 

In dit geval wordt de functie hello , zijn er geen parameters nodig en de code die wordt uitgevoerd, is één functie-aanroep uitvoeren. De geretourneerde waarde van een lisp-functie is het laatste stukje code in de functie dat wordt uitgevoerd, dus hello retourneert alles (format t "Hello, World!~%") .

In lisp om een functie aan te roepen schrijft men (function-name arguments...) waarbij function-name de naam van de functie is en arguments... is de (door spaties gescheiden) lijst met argumenten voor de aanroep. Er zijn enkele speciale gevallen die op functieaanroepen lijken, maar die bijvoorbeeld in de bovenstaande code geen defun functie bevatten die wordt aangeroepen, deze wordt speciaal behandeld en definieert in plaats daarvan een functie.

Bij de tweede prompt van de REPL, nadat we de hello functie hebben gedefinieerd, noemen we deze zonder parameters door te schrijven (hello) . Dit roept op zijn beurt de format op met de parameters t en "Hello, World!~%" . De format produceert opgemaakte uitvoer op basis van de argumenten die worden gegeven (een beetje zoals een geavanceerde versie van printf in C). Het eerste argument vertelt het waar het naartoe moet, waarbij t standaarduitvoer betekent. Het tweede argument vertelt het wat moet worden afgedrukt (en hoe eventuele extra parameters moeten worden geïnterpreteerd). De richtlijn (speciale code in het tweede argument) ~% vertelt het formaat om een nieuwe regel af te drukken (dwz op UNIX kan het schrijven \n en op windows \r\n ). Formaat retourneert meestal NIL (een beetje zoals NULL in andere talen).

Na de tweede prompt zien we dat Hello, World is afgedrukt en op de volgende regel dat de geretourneerde waarde NIL .

Hallo naam

Dit is een iets geavanceerder voorbeeld dat nog een paar kenmerken van common lisp toont. We beginnen met een eenvoudige Hello, World! functie en demonstreren enige interactieve ontwikkeling op de REPL. Merk op dat alle tekst uit een puntkomma ; , aan de rest van de regel staat een opmerking.

CL-USER> (defun hello ()
       (format t "Hello, World!~%")) ;We start as before
HELLO
CL-USER> (hello)
Hello, World!
NIL
CL-USER> (defun hello-name (name) ;A function to say hello to anyone
       (format t "Hello, ~a~%" name)) ;~a prints the next argument to format
HELLO-NAME
CL-USER> (hello-name "Jack")
Hello, Jack
NIL
CL-USER> (hello-name "jack") ;doesn't capitalise names
Hello, jack
NIL
CL-USER> (defun hello-name (name) ;format has a feature to convert to title case
       (format t "Hello, ~:(~a~)~%" name)) ;anything between ~:( and ~) gets it
WARNING: redefining COMMON-LISP-USER::HELLO-NAME in DEFUN
HELLO-NAME
CL-USER> (hello-name "jack")
Hello, Jack
NIL
CL-USER> (defun hello-name (name)
       (format t "Hello, ~:(~a~)!~%" name))
WARNING: redefining COMMON-LISP-USER::HELLO-NAME in DEFUN
HELLO-NAME
CL-USER> (hello-name "jack") ;now this works
Hello, Jack!
NIL
CL-USER> (defun hello (&optional (name "world")) ;we can take an optional argument
       (hello-name name)) ;name defaults to "world"
WARNING: redefining COMMON-LISP-USER::HELLO in DEFUN
HELLO
CL-USER> (hello)
Hello, World!
NIL
CL-USER> (hello "jack")
Hello, Jack!
NIL
CL-USER> (hello "john doe") ;note that this capitalises both names
Hello, John Doe!
NIL
CL-USER> (defun hello-person (name &key (number))
       (format t "Hello, ~a ~r" name number)) ;~r prints a number in English
HELLO-PERSON
CL-USER> (hello-person "Louis" :number 16) ;this doesn't quite work
Hello, Louis sixteen
NIL
CL-USER> (defun hello-person (name &key (number))
       (format t "Hello, ~:(~a ~:r~)!" name number)) ;~:r prints an ordinal
WARNING: redefining COMMON-LISP-USER::HELLO-PERSON in DEFUN
HELLO-PERSON
CL-USER> (hello-person "Louis" :number 16)
Hello, Louis Sixteenth!
NIL
CL-USER> (defun hello-person (name &key (number))
       (format t "Hello, ~:(~a ~@r~)!" name number)) ;~@r prints Roman numerals
WARNING: redefining COMMON-LISP-USER::HELLO-PERSON in DEFUN
HELLO-PERSON
CL-USER> (hello-person "Louis" :number 16)
Hello, Louis Xvi!
NIL
CL-USER> (defun hello-person (name &key (number)) ;capitalisation was wrong
       (format t "Hello, ~:(~a~) ~:@r!" name number))
WARNING: redefining COMMON-LISP-USER::HELLO-PERSON in DEFUN
HELLO-PERSON
CL-USER> (hello-person "Louis" :number 16) ;thats better
Hello, Louis XVI!
NIL
CL-USER> (hello-person "Louis") ;we get an error because NIL is not a number
Hello, Louis ; Evaluation aborted on #<SB-FORMAT:FORMAT-ERROR {1006641AB3}>.
CL-USER> (defun say-person (name &key (number 1 number-p)
                                      (title nil) (roman-number t))
       (let ((number (if number-p
                 (typecase number
                   (integer
                (format nil (if roman-number " ~:@r" " ~:(~:r~)") number))
                   (otherwise
                (format nil " ~:(~a~)" number)))
                 "")) ; here we define a variable called number
         (title (if title 
                (format nil "~:(~a~) " title)
                ""))) ; and here one called title
         (format nil "~a~:(~a~)~a" title name number))) ;we use them here

SAY-PERSON
CL-USER> (say-person "John") ;some examples
"John"
CL-USER> (say-person "john doe")
"John Doe"
CL-USER> (say-person "john doe" :number "JR")
"John Doe Jr"
CL-USER> (say-person "john doe" :number "Junior")
"John Doe Junior"
CL-USER> (say-person "john doe" :number 1)
"John Doe I"
CL-USER> (say-person "john doe" :number 1 :roman-number nil) ;this is wrong
"John Doe First"
CL-USER> (defun say-person (name &key (number 1 number-p)
                                      (title nil) (roman-number t))
       (let ((number (if number-p
                 (typecase number
                   (integer
                (format nil (if roman-number " ~:@r" " the ~:(~:r~)") number))
                   (otherwise
                (format nil " ~:(~a~)" number)))
                 ""))
         (title (if title 
                (format nil "~:(~a~) " title)
                "")))
         (format nil "~a~:(~a~)~a" title name number)))
WARNING: redefining COMMON-LISP-USER::SAY-PERSON in DEFUN
SAY-PERSON
CL-USER> (say-person "john doe" :number 1 :roman-number nil) ;thats better
"John Doe the First"
CL-USER> (say-person "louis" :title "king" :number 16 :roman-number nil)
"King Louis the Sixteenth"
CL-USER> (say-person "louis" :title "king" :number 16 :roman-number t)
"King Louis XVI"
CL-USER> (defun hello (&optional (name "World") &rest arguments) ;now we will just
       (apply #'hello-name name arguments)) ;pass all arguments to hello-name
WARNING: redefining COMMON-LISP-USER::HELLO in DEFUN
HELLO
CL-USER> (defun hello-name (name &rest arguments) ;which will now just use
       (format t "Hello, ~a!" (apply #'say-person name arguments))) ;say-person
WARNING: redefining COMMON-LISP-USER::HELLO-NAME in DEFUN
HELLO-NAME
CL-USER> (hello "louis" :title "king" :number 16) ;this works now
Hello, King Louis XVI!
NIL
CL-USER>
 

Deze hoogtepunten een aantal van de geavanceerde functies van Common Lisp's format functie evenals een aantal functies, zoals optionele parameters en zoekwoord argumenten (bijvoorbeeld :number ). Dit geeft ook een voorbeeld van interactieve ontwikkeling bij een REPL in common lisp.

Lambda-uitdrukkingen en anonieme functies

Een anonieme functie kan worden gedefinieerd zonder een naam via een Lambda-expressie . Voor het definiëren van dit soort functies wordt het trefwoord lambda gebruikt in plaats van het trefwoord defun . De volgende regels zijn allemaal equivalent en definiëren anonieme functies die de som van twee getallen uitvoeren:

(lambda (x y) (+ x y))
(function (lambda (x y) (+ x y)))
#'(lambda (x y) (+ x y))
 

Hun bruikbaarheid is merkbaar bij het maken van Lambda-formulieren , dat wil zeggen een vorm die een lijst is waarbij het eerste element de lambda-expressie is en de overige elementen de argumenten van de anonieme functie zijn. Voorbeelden ( online uitvoering ):

(print ((lambda (x y) (+ x y)) 1 2)) ; >> 3

(print (mapcar (lambda (x y) (+ x y)) '(1 2 3) '(2 -5 0))) ; >> (3 -3 3)
 

Som van de lijst met gehele getallen

(defun sum-list-integers (list)
    (reduce '+ list))

; 10
(sum-list-integers '(1 2 3 4))

; 55
(sum-list-integers '(1 2 3 4 5 6 7 8 9 10))
 

Het eenvoudige Hello World-programma in REPL

Common Lisp REPL is een interactieve omgeving. Elk formulier dat na de prompt wordt geschreven, wordt geëvalueerd en de waarde ervan wordt achteraf afgedrukt als resultaat van de evaluatie. Dus de eenvoudigst mogelijke "Hallo wereld!" programma in Common Lisp is:

CL-USER> "Hello, World!"
"Hello, World!"
CL-USER>
 

Wat hier gebeurt, is dat een string costant wordt gegeven als invoer voor de REPL, deze wordt geëvalueerd en het resultaat wordt afgedrukt. Wat te zien is aan dit voorbeeld, is dat tekenreeksen, zoals getallen, speciale symbolen zoals NIL en T en een paar andere letterlijke vormen, zelfevaluerende vormen zijn, dat wil zeggen dat ze zichzelf evalueren.