Lisp: Difference between revisions
(77 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
=Introduction= | =Introduction= | ||
This is a quick tour on lisp. I decided to look at this because of the build a compiler youtube by lens_r which made me feel I am missing something. This has been rabbit hole which I have fallen done | This is a quick tour on lisp. I decided to look at this because of the build a compiler youtube by lens_r which made me feel I am missing something. This has been rabbit hole which I have fallen done | ||
=Useful Links= | |||
Not gonna type forever whilst someone else already has. I remember by typing but here is a more professional approach [https://www.lispworks.com/documentation/HyperSpec/Front/Contents.htm Lisp Works] | |||
=Concepts= | =Concepts= | ||
This is my view and maybe wrong but to me the first thing I noticed was the huge amount of parentheses which are involved. It made the language look really ugly and made me feel don't do this. I think I have touched this in the past because of a gun to my head. That said, like people, you probably need to understand before having a view, and even then, not for you is a better view and it is negative. So here is the half day I spent | This is my view and maybe wrong but to me the first thing I noticed was the huge amount of parentheses which are involved. It made the language look really ugly and made me feel don't do this. I think I have touched this in the past because of a gun to my head. That said, like people, you probably need to understand before having a view, and even then, not for you is a better view and it is negative. So here is the half day I spent<br> | ||
<br> | |||
I have a lot of trouble reading the manuals for anything. YouTube has been a god send for me along with examples. If there are youtuber out there please make a video on how to read the manual. Probably all me but this is written in greek to me. I probably give up too easily<br><br> | |||
'''flet''' ((function-name lambda-list <nowiki>[[</nowiki>local-declaration'''*''' | local-documentation<nowiki>]]</nowiki> local-form'''*''')'''*''') declaration'''*''' form'''*''' | |||
=Comments= | =Comments= | ||
Line 260: | Line 266: | ||
This is why I am doing this because of the word continuation. Here are some lispy controls | This is why I am doing this because of the word continuation. Here are some lispy controls | ||
==If Statements== | ==If Statements== | ||
For if statements [[#Special Forms|Special Forms]] | |||
==Case Statements== | ==Case Statements== | ||
<syntaxhighlight lang="lisp"> | <syntaxhighlight lang="lisp"> | ||
Line 331: | Line 309: | ||
(t (format t "Not 1 or 2 ~%"))) | (t (format t "Not 1 or 2 ~%"))) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=Fun with Cars= | |||
==car== | |||
So for car this is simple | |||
<syntaxhighlight lang="lisp"> | |||
(defvar *my-list* (list 0 1 2 3 4 5)) | |||
; Returns first element in list i.e. 0 | |||
(format t "First element is ~a ~%" (car *my-list*)) | |||
</syntaxhighlight> | |||
==cdr (pronounced cudder)== | |||
So for cdr is just the rest | |||
<syntaxhighlight lang="lisp"> | |||
(defvar *my-list* (list 0 1 2 3 4 5)) | |||
; Returns rest of the elements in list i.e. 1 2 3 4 5 | |||
(format t "First element is ~a ~%" (cdr *my-list*)) | |||
</syntaxhighlight> | |||
==All the combinations== | |||
Found a page which seems to explain the unexplainable. To understand the terms now we need to understand | |||
<br> | |||
{| class="wikitable" | |||
|- | |||
|This place ... | |||
|Is equivalent to this place ... | |||
|- | |||
|(caar x) | |||
|(car (car x)) | |||
|- | |||
|(cadr x) | |||
|(car (cdr x)) | |||
|- | |||
|(cdar x) | |||
|(cdr (car x)) | |||
|- | |||
|(cddr x) | |||
|(cdr (cdr x)) | |||
|- | |||
|(caaar x) | |||
|(car (car (car x))) | |||
|- | |||
|(caadr x) | |||
|(car (car (cdr x))) | |||
|- | |||
|(cadar x) | |||
|(car (cdr (car x))) | |||
|- | |||
|(caddr x) | |||
|(car (cdr (cdr x))) | |||
|- | |||
|(cdaar x) | |||
|(cdr (car (car x))) | |||
|- | |||
|(cdadr x) | |||
|(cdr (car (cdr x))) | |||
|- | |||
|(cddar x) | |||
|(cdr (cdr (car x))) | |||
|- | |||
|(cdddr x) | |||
|(cdr (cdr (cdr x))) | |||
|- | |||
|(caaaar x) | |||
|(car (car (car (car x)))) | |||
|- | |||
|(caaadr x) | |||
|(car (car (car (cdr x)))) | |||
|- | |||
|(caadar x) | |||
|(car (car (cdr (car x)))) | |||
|- | |||
|(caaddr x) | |||
|(car (car (cdr (cdr x)))) | |||
|- | |||
|(cadaar x) | |||
|(car (cdr (car (car x)))) | |||
|- | |||
|(cadadr x) | |||
|(car (cdr (car (cdr x)))) | |||
|- | |||
|(caddar x) | |||
|(car (cdr (cdr (car x)))) | |||
|- | |||
|(cadddr x) | |||
|(car (cdr (cdr (cdr x)))) | |||
|- | |||
|(cdaaar x) | |||
|(cdr (car (car (car x)))) | |||
|- | |||
|(cdaadr x) | |||
|(cdr (car (car (cdr x)))) | |||
|- | |||
|(cdadar x) | |||
|(cdr (car (cdr (car x)))) | |||
|- | |||
|(cdaddr x) | |||
|(cdr (car (cdr (cdr x)))) | |||
|- | |||
|(cddaar x) | |||
|(cdr (cdr (car (car x)))) | |||
|- | |||
|(cddadr x) | |||
|(cdr (cdr (car (cdr x)))) | |||
|- | |||
|(cdddar x) | |||
|(cdr (cdr (cdr (car x)))) | |||
|- | |||
|(cddddr x) | |||
|(cdr (cdr (cdr (cdr x)))) | |||
|} | |||
=Functions 3= | =Functions 3= | ||
A while ago we did lists and they had this insane thing called caar, cadar, cddar | A while ago we did lists and they had this insane thing called caar, cadar, cddar | ||
as Car of Car and gets the '''car''' of '''car''' or the first item in the list | |||
*cadar is | *caar is (car (car x)) | ||
*cadar is (car (cdr (car x))) | |||
*cddar is (cdr (cdr (car x))) | |||
==caar== | |||
(car (car x))<br> | |||
From inside to outside<br> | |||
First item = (superman (6 ft 3 in) ( 230 lbs))<br> | |||
And first item of this = superman<br> | |||
==cadar== | |||
(car (cdr (car x)))<br> | |||
From inside to outside<br> | |||
First item = (superman (6 ft 3 in) ( 230 lbs))<br> | |||
rest of items = (6 ft 3 in) ( 230 lbs))<br> | |||
first item (6 ft 3 in)<br> | |||
==cddar== | |||
(cdr (cdr (car x)))<br> | |||
From inside to outside<br> | |||
First item = (superman (6 ft 3 in) ( 230 lbs))<br> | |||
rest of items = (6 ft 3 in) ( 230 lbs))<br> | |||
rest of items = ( 230 lbs))<br> | |||
<syntaxhighlight lang="lisp"> | <syntaxhighlight lang="lisp"> | ||
(defparameter *my-heroes* `( (superman (6 ft 3 in) ( 230 lbs)) | (defparameter *my-heroes* `( (superman (6 ft 3 in) ( 230 lbs)) | ||
Line 344: | Line 450: | ||
`(,(caar size) is ,(cadar size), (cddar size)))) | `(,(caar size) is ,(cadar size), (cddar size)))) | ||
; Heroes (cdr (cdr (car x)))(Superman Is (6 Ft 3 In) ((230 Lbs))) | |||
(get-heroes-data *my-heroes*) | (get-heroes-data *my-heroes*) | ||
</syntaxhighlight> | |||
=Mapping over a list= | |||
Here we map over a function, in this case numberp | |||
<syntaxhighlight lang='lisp'> | |||
;; mapcar example of using a function | |||
;; mapcar List is (T T T Nil Nil) | |||
(format t "mapcar List is ~a ~%" (mapcar #'numberp `(1 2 3 g d))) | |||
</syntaxhighlight> | |||
=Lambda= | |||
Lambda is a way of defining an anonymous function in lisp. We can pass functions to mapcar using the hash quote e.g. #` | |||
<syntaxhighlight lang='lisp'> | |||
; mapcar List is (3 6 9 12 15) | |||
(format t "mapcar List is ~a ~%" (mapcar #'(lambda (x) (* 3 x)) '(1 2 3 4 5))) | |||
</syntaxhighlight> | |||
<br> | |||
Got confused with this because on the use of print | |||
<syntaxhighlight lang='lisp'> | |||
(mapcar #'(lambda (x) (print (* 3 x))) '(1 2 3 4 5)) | |||
</syntaxhighlight> | |||
Guess this the equivalent of | |||
<syntaxhighlight lang='js'> | |||
[1, 2, 3, 4, 5].map((n)=> console.log(n)) | |||
</syntaxhighlight> | |||
And | |||
<syntaxhighlight lang='js'> | |||
// mapcar List is [1, 2, 3, 4, 5] | |||
console.log('mapcar List is %o', [1, 2, 3, 4, 5].map((n)=> n)) | |||
</syntaxhighlight> | |||
=multiple-value-bind= | |||
Not too sure what a macro is, but that is what this is. I suspect it is a way of extending existing functionality. This is an example of calling a function which returns two values. To do this we use the accessor values | |||
<syntaxhighlight lang='lisp'> | |||
(defun expose-me (num) | |||
(values (expt num 2) (expt num 3))) | |||
;; Example of multiple value call | |||
(multiple-value-bind (arg-a arg-b) (expose-me 2) | |||
(format t "2^2 = ~d 2^3 = ~d~%" arg-a arg-b)) | |||
</syntaxhighlight> | |||
=Macros= | |||
==Example 1== | |||
This is taken from [https://www.geeksforgeeks.org/macros-in-lisp/ geeksforgeeks] and does seem easy to understand however here is the quote from another page. A macro is an ordinary piece of Lisp code that operates on another piece of putative Lisp code, translating it into (a version closer to) executable Lisp. | |||
<syntaxhighlight lang='lisp'> | |||
(defmacro sumup (a b) | |||
; This line will be executed whenever | |||
; we use the macro sum | |||
; with two integer parameters | |||
(format t "~D + ~D : ~D" a b (+ a b)) | |||
) | |||
; Calling macro sumup with parameters 7 and 18 | |||
(sumup 7 18) | |||
(terpri) | |||
</syntaxhighlight> | |||
So done with all that and my good friend Derek Banas sorts the Greek out for me. He explained that a common problem my be that you have to often have | |||
multiple lines in an if statement and it can be annoying to use progn all of the time to do this. (When I think of C and macro usage in it, it smacks of the same problem. <br><br> | |||
Here is an example of the problem '''not''' the example | |||
<syntaxhighlight lang='lisp'> | |||
(defvar *num* 2) | |||
(defvar *num-2* 0) | |||
(if (= *num* 2) | |||
(progn | |||
(setf *num-2* 3) | |||
(format t "num-2 is ~d ~%" *num-2*) | |||
) | |||
(format t "Not equal to 2 ~%")) | |||
</syntaxhighlight> | |||
So with macros we need to have it generate code not be code like we do in C<br> | |||
<syntaxhighlight lang='lisp'> | |||
(defmacro ifit (condition &rest body) | |||
`(if ,condition | |||
(progn | |||
,@body) | |||
(format t "Can't Drive ~%")) | |||
) | |||
(setf *age* 18) | |||
(ifit (>= *age* 18) | |||
(format t "Line one of lisp ~%") | |||
(format t "Line two of Lisp ~%") | |||
(format t "Line three of Lisp ~%") | |||
(terpri) | |||
) | |||
</syntaxhighlight> | |||
In the macro we use quasi quoting which is where we switch between code and data using the comma for expression and ,@ for the multiple lines. This replacing the '''condition''' and '''body''' with the quoted value not the evaluated value. | |||
==Example 2== | |||
I am not feeling the love with second example. I will have to use some of this to appreciate it I think. It is just like C I think so just a way to generate repetitive code where the language is to granular. | |||
<syntaxhighlight lang='lisp'> | |||
;; Function to add two numbers | |||
(defun add (a b) | |||
(let ((sum (+ a b))) | |||
(format t "Sum of ~d and ~d is ~d ~%" a b sum) | |||
sum) | |||
) | |||
;; Create a macro to add two numbers | |||
(defmacro letx (var val &rest body) | |||
`(let ((,var ,val)) | |||
,@body) | |||
) | |||
;; define a function to use letx | |||
(defun addx (a b) | |||
(letx sum (+ a b) | |||
(format t "Sum of ~d and ~d is ~d ~%" a b sum) | |||
sum) | |||
) | |||
;; Call the function | |||
(addx 2 3) | |||
</syntaxhighlight> | |||
=Classes In Lisp= | |||
We can define classes with attributes/properties as we do in typescript. | |||
==Example from Copilot== | |||
This was the approach spat out by copilot | |||
<syntaxhighlight lang='lisp'> | |||
;; Define a clas for a dog which has a name and sound | |||
(defclass dog () | |||
((name :initarg :name :accessor name) | |||
(sound :initarg :sound :accessor sound)) | |||
) | |||
;; Create an instance of the dog class | |||
(defvar *dog* (make-instance 'dog :name "Rover" :sound "Woof")) | |||
;; Get the name of the dog | |||
(format t "Dog name is ~a ~%" (name *dog*)) | |||
;; Get the sound of the dog | |||
(format t "Dog sound is ~a ~%" (sound *dog*)) | |||
</syntaxhighlight> | |||
==Example from Derek== | |||
Here is the second attempt | |||
<syntaxhighlight lang='lisp'> | |||
;; Define dog class | |||
(defclass dog2 () | |||
((name) | |||
(sound)) | |||
) | |||
;; Use make-instance to create an instance of the dog class | |||
(defvar *dog2* (make-instance 'dog2)) | |||
;; Set the name of the dog | |||
(setf (slot-value *dog2* 'name) "Rover") | |||
;; Set the sound of the dog | |||
(setf (slot-value *dog2* 'sound) "Woof") | |||
;; Get the name of the dog | |||
(format t "Dog name is ~a ~%" (slot-value *dog2* 'name)) | |||
;; Get the sound of the dog | |||
(format t "Dog sound is ~a ~%" (slot-value *dog2* 'sound)) | |||
</syntaxhighlight> | |||
I did some homework, but sir!!!!, and it said | |||
==Example 3 with hello vera== | |||
This is another example with some options | |||
<syntaxhighlight lang='lisp'> | |||
;; This time we have | |||
;; an accessor name different value dog-name | |||
;; default sound of woof | |||
;; food we will be write only accessor | |||
;; home will be read only accessor | |||
(defclass dog3 () | |||
( | |||
(name | |||
:initarg :name :accessor dog-name) | |||
(sound | |||
:initarg :sound :accessor dog-sound :initform "woof" ) | |||
(food | |||
:initarg :sound :reader get-food :initform "bounce" ) | |||
) | |||
) | |||
;; Create an instance of the dog3 class | |||
(defvar *dog3* (make-instance 'dog3 :name "Rover")) | |||
;; Get the name of the dog | |||
(format t "Dog name is ~a ~%" (dog-name *dog3*)) | |||
(format t "Dog sound is ~a ~%" (dog-sound *dog3*)) | |||
(format t "Dog food is ~a ~%" (get-food *dog3*)) | |||
</syntaxhighlight> | |||
==Adding Methods== | |||
Methods do not belong to classes in lisp. Instead they belong to generic functions. Let create one which take a parameter | |||
<syntaxhighlight lang='lisp'> | |||
;; Let define a generic function for dog3 | |||
(defgeneric show-properties (dog)) | |||
;; Define a method for generic function to show properties of dog3 | |||
(defmethod show-properties ((dog dog3)) | |||
(format t "Property name is ~a ~%" (slot-value dog 'name)) | |||
(format t "Property sound is ~a ~%" (slot-value dog 'sound)) | |||
) | |||
(format t "---------------- ~%") | |||
;; Use show-properties to show the properties of dog3 | |||
(show-properties *dog3*) | |||
</syntaxhighlight> | |||
This outputs | |||
<syntaxhighlight lang='txt'> | |||
---------------- | |||
Property name is Rover | |||
Property sound is woof | |||
</syntaxhighlight> | |||
==Subclass a Class== | |||
We can do this too | |||
<syntaxhighlight lang='lisp'> | |||
;; To define a subclass of dog3 | |||
(defclass dog4 (dog3) | |||
( | |||
(breed | |||
:initarg :breed :accessor dog-breed) | |||
) | |||
) | |||
;; Create an instance of dog4 | |||
(defvar *dog4* (make-instance 'dog4 :name "Rover2" :breed "Labrador")) | |||
;; Use the method show-properties to show the properties of dog4 | |||
;; Not no breed is shown | |||
(show-properties *dog4*) | |||
</syntaxhighlight> | |||
This outputs | |||
<syntaxhighlight lang='txt'> | |||
---------------- | |||
Property name is Rover2 | |||
Property sound is woof | |||
</syntaxhighlight> | |||
=Data Structures= | |||
==Arrays== | |||
===Simple Array=== | |||
We do this like so | |||
<syntaxhighlight lang='lisp'> | |||
;; Define an array on names | |||
(defvar *names* (make-array 5 :initial-element "Fred")) | |||
;; Set the first element of the array | |||
(setf (aref *names* 0) "Fred") | |||
;; Set the second element of the array | |||
(setf (aref *names* 1) "Fred2") | |||
</syntaxhighlight> | |||
===3D Array=== | |||
We can do 3-dimensional arrays too | |||
<syntaxhighlight lang='lisp'> | |||
;; Make a 3x3 number array | |||
(defvar *numbers* (make-array '(3 3) | |||
:initial-contents '((0 1 2) (3 4 5) (6 7 8)))) | |||
;; Print the array with dotimes | |||
(dotimes (i 3) | |||
(dotimes (j 3) | |||
(format t "Number is ~d ~%" (aref *numbers* i j)) | |||
) | |||
) | |||
</syntaxhighlight> | |||
==Hashtables== | |||
Getting into this as they start talking my language. Here is creation and remove of hash table | |||
<syntaxhighlight lang='lisp'> | |||
;; Make a hash table | |||
(defvar *my-hash* (make-hash-table)) | |||
;; Set the value of the hash table | |||
(setf (gethash '101 *my-hash*) "Fred 101") | |||
(setf (gethash '102 *my-hash*) "Fred 102") | |||
(setf (gethash '103 *my-hash*) "Fred 103") | |||
;; Get the value of the hash table | |||
(format t "Hash value is ~a ~%" (gethash '101 *my-hash*)) | |||
;; Using maphash and lambda format the hash table | |||
(maphash #'(lambda (key value) | |||
(format t "Key is ~a Value is ~a ~%" key value) | |||
) *my-hash*) | |||
;; Delete a key from the hash table | |||
(remhash '101 *my-hash*) | |||
</syntaxhighlight> | |||
==Structures== | |||
We can create custom structure to store our data | |||
<syntaxhighlight lang='lisp'> | |||
;; Create a custom structure | |||
(defstruct person | |||
name | |||
age | |||
id) | |||
;; Create an instance of the person structure | |||
(defvar *person* (make-person :name "Fred" :age 21 :id 101)) | |||
;; Get the name of the person | |||
(format t "Person name is ~a ~%" (person-name *person*)) | |||
;; Change the id of the person | |||
(setf (person-id *person*) 102) | |||
</syntaxhighlight> | |||
=File-IO= | |||
And Finally Esther, And finally Cyril. We are able to read and write files with lisp | |||
==Writing a File== | |||
<syntaxhighlight lang='lisp'> | |||
;; An example of writing new data to a file checking if it exists | |||
(defun write-to-file (filename data) | |||
(with-open-file (stream filename | |||
:direction :output | |||
:if-exists :supersede) | |||
(princ data stream)) | |||
) | |||
;; Write some data to a file | |||
(write-to-file "test.txt" "Hello World") | |||
</syntaxhighlight> | |||
==Writing a File== | |||
<syntaxhighlight lang='lisp'> | |||
;; An example of reading data from a file that exists | |||
(defun read-from-file (filename) | |||
(with-open-file (stream filename) | |||
(let ((data (read-line stream))) | |||
(format t "Data is ~a ~%" data) | |||
data)) | |||
) | |||
;; Read some data from a file | |||
(read-from-file "test.txt") | |||
</syntaxhighlight> | |||
=Schema= | |||
The seems to be a battle even longer than the curly bracket between lisp people. So I have found something call schema from mit which has led me back to why I was looking at this - continuation. It took me a while (which I do not think it should do) to do | |||
<syntaxhighlight lang="bash"> | |||
echo | mit-scheme --load test.sch | |||
</syntaxhighlight> | |||
So to demonstrate the call/cc (which is not available in common lisp we have | |||
<syntaxhighlight lang="scm"> | |||
(let ( | |||
(my-val (call/cc | |||
(lambda (the-continuation) | |||
(display "This will be executed\n") | |||
(the-continuation 5) | |||
(display "Hello 2 not executed \n") ) ) ) ) | |||
(display my-val) ) | |||
</syntaxhighlight> | |||
This seems to amount to early return which seems a lot of noise about nothing. We shall see. | |||
=Special Forms= | |||
Here is a list of special forms and where used some detail | |||
{|class="wikitable" | |||
|block | |||
|let* | |||
|[[#return-from|return-from]] | |||
|- | |||
|catch | |||
|load-time-value | |||
|[[#setq|setq]] | |||
|- | |||
|eval-when | |||
|locally | |||
|symbol-macrolet | |||
|- | |||
|[[#flet|flet]] | |||
|macrolet | |||
|tagbody | |||
|- | |||
|function | |||
|multiple-value-call | |||
|the | |||
|- | |||
|go | |||
|multiple-value-prog1 | |||
|throw | |||
|- | |||
|[[#if|if]] | |||
|[[#progn|progn]] | |||
|unwind-protect | |||
|- | |||
|[[#labels|labels]] | |||
|progv | |||
|- | |||
|let | |||
|[[#quote|quote]] | |||
|} | |||
==quote== | |||
Quote is a special form which stops the lisp interpreter. So the thing is a value (number, symbol, string) rather than a variable in a function<br> | |||
<br> | |||
I had to read about this because of my stuffed up brain. In lisp there is some referred to as a quote. And they kept mentioning shorthand but they were incredible unclear what for. and it what the function quote. What they meant is '''`''' means quote () | |||
<syntaxhighlight lang="lisp"> | |||
(format t "Quote is ~a ~%" (+ 1 2)) ; Quote is 3 | |||
(format t "Quote is ~a ~%" (quote (+ 1 2))) ; Quote is (+ 1 2) | |||
(format t "Quote is ~a ~%" `(+ 1 2)) ; Quote is (+ 1 2) | |||
</syntaxhighlight> | |||
==setq== | |||
This appears to be identical to setf i.e. (setq x y) is exactly equivalent to (setf x y). Maybe legacy? | |||
<syntaxhighlight lang="lisp"> | |||
(defvar *test-data* "Fred") | |||
(format t "default ~a ~%" *test-data*) | |||
(setq *test-data* "Fredq") | |||
(format t "setq ~a ~%" *test-data*) | |||
(setf *test-data* "Fredf") | |||
(format t "setf ~a ~%" *test-data*) | |||
</syntaxhighlight> | |||
==return-from== | |||
By default the return value is the last statement executed. To override this we use the special form return-from and the function name | |||
<syntaxhighlight lang="lisp"> | |||
(defun get-average-return (a b) | |||
(return-from get-average-return (/ (+ a b) 2))) | |||
</syntaxhighlight> | |||
==if== | |||
With lisp the if statement has two lines by default to implement the else. For not we add an not operator | |||
<syntaxhighlight lang="lisp"> | |||
;; If statements are written as follows, if true then do this else do this | |||
(if (= 1 1) | |||
(format t "1 is equal to 1 ~%") | |||
(format t "1 is not equal to 1 ~%")) | |||
;; Not equal | |||
(if (not (= 1 1)) | |||
(format t "1 is equal to 1 ~%") | |||
(format t "1 is not equal to 1 ~%")) | |||
;; And | |||
(if (and (= 1 1) (= 2 2)) | |||
(format t "1 is equal to 1 and 2 is equal to 2 ~%") | |||
(format t "1 is not equal to 1 and 2 is not equal to 2 ~%")) | |||
;; And or haha | |||
(if (or (= 1 1) (= 2 2)) | |||
(format t "1 is equal to 1 or 2 is equal to 2 ~%") | |||
(format t "1 is not equal to 1 or 2 is not equal to 2 ~%")) | |||
</syntaxhighlight> | </syntaxhighlight> |
Latest revision as of 11:56, 10 September 2024
Introduction
This is a quick tour on lisp. I decided to look at this because of the build a compiler youtube by lens_r which made me feel I am missing something. This has been rabbit hole which I have fallen done
Useful Links
Not gonna type forever whilst someone else already has. I remember by typing but here is a more professional approach Lisp Works
Concepts
This is my view and maybe wrong but to me the first thing I noticed was the huge amount of parentheses which are involved. It made the language look really ugly and made me feel don't do this. I think I have touched this in the past because of a gun to my head. That said, like people, you probably need to understand before having a view, and even then, not for you is a better view and it is negative. So here is the half day I spent
I have a lot of trouble reading the manuals for anything. YouTube has been a god send for me along with examples. If there are youtuber out there please make a video on how to read the manual. Probably all me but this is written in greek to me. I probably give up too easily
flet ((function-name lambda-list [[local-declaration* | local-documentation]] local-form*)*) declaration* form*
Comments
;;;; Describe Program 4 semi colons
;;; Comments
;; Indented Comments
#|| Multiline
My Comments
||#
Format to terminal
(format t "Hello World ~%")
(print "Hello World")
Variables
To declare
(defvar *my-variable* "Hello World" )
It is common practice for local variable to have asterixes
We can declare variables for functions too, in this case read-data now is the built-in read function.
(defvar *read-data* (read))
Functions
We can of course define functions. So putting the above together we are prompted for a value and it prints it to the terminal. Not the ~% is the linefeed in list and ~a is a formatter like printf
(defvar *read-data* (read))
(defun read-and-print (name)
(format t "Read and Print value is ~a ~%" name))
(read-and-print *read-data*)
Changing values of variables
We use setf to change the values of existing variables
(defvar *read-data* (read))
;; You can change a value using setf
(setf *read-data* "Hello World 1")
(read-and-print *read-data*)
(setf *read-data* "Hello World 2")
(read-and-print *read-data*)
Formatting
This is like printf
(format t "PI to 5 characters is ~5f ~%" 3.14159)
(format t "PI to 4 decimal places is ~,4f ~%" 3.14159)
Cells
A Cell, I think is a set of parentheses and we can embed them inside each other. So
(format t "Text with this ~d ~%" (+ 5 4))
; Add a cell within a cell
(format t "Text with this ~d ~%" (+ 5 (+ 4 3)))
;; and again
(format t "Text with this ~d ~%" (+ 5 (+ 4 (+ 100 5))))
Maths Stuff
Just some examples. This is a whirlwind tour not a tutorial
(format t "Modulus of 10/3 is ~d ~%" (mod 10 3))
(format t "Remainder of 10/3 is ~d ~%" (rem 10 3))
;; All work the same way e.g. sqrt
(format t "Square root of 9 is ~d ~%" (sqrt 9))
;; and floor
(format t "Floor of 9.9 is ~d ~%" (floor 9.9))
The blasted quote
I had to read about this because of my stuffed up brain. In lisp there is some referred to as a quote. And they kept mentioning shorthand but they were incredible unclear what for. and it what the function quote. What they meant is ` means quote ()
(format t "Quote is ~a ~%" (+ 1 2)) ; Quote is 3
(format t "Quote is ~a ~%" (quote (+ 1 2))) ; Quote is (+ 1 2)
(format t "Quote is ~a ~%" `(+ 1 2)) ; Quote is (+ 1 2)
Equality
Equality in Lisp has three approaches
- For symbols we use eq
- For numbers we use =
- For strings we use equal
;; Symbols
(format t "Are 'hello' and 'hello' equal? ~a ~%" (eq 'hello 'hello))
;; Numbers
(format t "Are 1 and 1 equal? ~a ~%" (= 1 1)) ; T
(format t "Are 1 and 2 equal? ~a ~%" (= 1 2)) ; NIL
;; Strings
(format t "Are 'hello' and 'hello' equal? ~a ~%" (equal "hello" "hello"))
We can compare things with the same value but presented differently with equalp
;; equalp is used to compare numbers
(format t "Are 1 and 1.0 equal? ~a ~%" (equalp 1 1.0)) ;T
(format t "Are 1.01 and 1.010 equal? ~a ~%" (equalp 1.01 1.010)) ;T
(format t "Are 1.010 and 1.01 equal? ~a ~%" (equalp 1.010 1.01)) ;T
;; for strings for case insensitive
(format t "Are 'hello' and 'HELLO' equal? ~a ~%" (equalp "hello" "HELLO")) ;T
Lists
Seemed a big deal in Lisp. I guess most of this is self explanatory
Simple Lists
;; Create a list
(defvar *my-list* (list 0 1 2 3 4 5))
;; Print the list
(format t "List is ~a ~%" *my-list*)
;; Get the first element
(format t "First element is ~a ~%" (first *my-list*))
;; Get the rest of the list
(format t "Rest of the list is ~a ~%" (rest *my-list*))
;; Get the second element
(format t "Second element is ~a ~%" (second *my-list*))
;; Get the third element
(format t "Third element is ~a ~%" (third *my-list*))
;; Using nth
(format t "Third element is ~a ~%" (nth 2 *my-list*))
;; Using car
(format t "First element is ~a ~%" (car *my-list*))
;; Using cdr
(format t "Rest of the list is ~a ~%" (cdr *my-list*))
;; Using cadr (get first element of rest)
(format t "Second element is ~a ~%" (cadr *my-list*))
;; Using caddr (get second element of rest)
(format t "Third element is ~a ~%" (caddr *my-list*))
;; Using caddr (get third element of rest)
(format t "Fourth element is ~a ~%" (cadddr *my-list*))
;; Are we a list?
(format t "Is a list? ~a ~%" (listp *my-list*))
;; Is value a list?
(format t "Is a list? ~a ~%" (listp 1))
;; Append to a list
(format t "Append to list ~a ~%" (append *my-list* (list 6 7 8 9 10)))
;; Concatenate lists
(format t "Concatenate lists ~a ~%" (concatenate 'list *my-list* (list 6 7 8 9 10)))
;; Reverse a list
(format t "Reverse list ~a ~%" (reverse *my-list*))
;; Sort a list
(format t "Sort list ~a ~%" (sort *my-list* #'<))
;; Create a list of lists
(defvar *my-list-of-lists* '((1 2 3) (4 5 6) (7 8 9)))
;; Remove duplicates
(format t "Remove duplicates ~a ~%" (remove-duplicates (list 1 1 2 2 3 3 4 4 5 5)))
Splicing
Not sure my odds of remembering. The @ after a comma within quote - yes I know, then the form following the at-sign is evaluated to produce a list of objects
;; Create a list
(defvar *my-list* (list 0 1 2 3 4 5))
;; a reminder of rest which mean everything but the first like javascript
(format t "List with rest is ~a ~%" (rest *my-list*))
;; Splice a list
;; List with 0 is (0 1 2 3 4 5)
(format t "List with 0 is ~a ~%" `(0 ,@(rest *my-list*)))
;; List with 2 is (2 1 2 3 4 5)
(format t "List with 2 is ~a ~%" `(2 ,@(rest *my-list*)))
Association list or A-list
The minute they use a fancy word I get stuffed and have to google. So I think what they mean is a list with key value having a key and a value. They go on about car and cdr
(defparameter *my-heroes* `( (superman . "Clark Kent")
(batman . "Bruce Wayne")
(spiderman . "Peter Parker")))
; List is ((SUPERMAN . Clark Kent) (BATMAN . Bruce Wayne) (SPIDERMAN . Peter Parker))
(format t "List is ~a ~%" *my-heroes*)
; Assoc (SUPERMAN . Clark Kent)
(format t "Assoc ~a ~%" (assoc 'superman *my-heroes*))
Another approach to achieve the same thing
;; second approach wih cons
(defparameter *marks* (list (cons 'kishan 96) (cons 'saksham 92)))
;2 List is ((KISHAN . 96) (SAKSHAM . 92))
(format t "2 List is ~a ~%" *marks*)
;2 Assoc (KISHAN . 96)
(format t "2 Assoc ~a ~%" (assoc 'kishan *marks*))
Functions 2
Here some examples
Simple example
;; Get average
(defun get-average (a b)
(/ (+ a b) 2))
(format t "Average is ~a ~%" (get-average 10 20))
Multiple Arguments
We can simulate vargs
;; Rest parameters is like varargs
(defun get-average-rest (&rest numbers)
(/ (apply #'+ numbers) (length numbers)))
(format t "Average is ~a ~%" (get-average-rest 10 20 30 40 50))
Optional Arguments
Here we can either pass b or not
(defun get-average-optional (a &optional b)
(if b
(/ (+ a b) 2)
a))
; Average is with B 15
(format t "Average is with B ~a ~%" (get-average-optional 10 20))
; Average is without B 10
(format t "Average is without B ~a ~%" (get-average-optional 10))
Return value
By default the return value is the last statement executed. To override this we use the keyword return-from and the function name
(defun get-average-return (a b)
(return-from get-average-return (/ (+ a b) 2)))
Flow Control
This is why I am doing this because of the word continuation. Here are some lispy controls
If Statements
For if statements Special Forms
Case Statements
(case 1
(1 (format t "1 ~%"))
(2 (format t "2 ~%"))
(3 (format t "3 ~%"))
(t (format t "Not 1, 2 or 3 ~%")))
When, Unless and Loops
;; When
(when (= 1 1)
(format t "1 is equal to 1 ~%"))
;; Unless
(unless (= 1 1)
(format t "1 is not equal to 1 ~%"))
;; Loops
;; Loop with for
(loop for i from 1 to 10
do (format t "Hello ~%"))
;; Loop with repeat
(loop repeat 10
do (format t "Hello ~%"))
;; dotimes
(dotimes (i 10)
(format t "Hello ~%"))
cond
Like this one
;; Using cond
(cond
((= 1 1) (format t "1 is equal to 1 ~%"))
((= 2 2) (format t "2 is equal to 2 ~%"))
(t (format t "Not 1 or 2 ~%")))
Fun with Cars
car
So for car this is simple
(defvar *my-list* (list 0 1 2 3 4 5))
; Returns first element in list i.e. 0
(format t "First element is ~a ~%" (car *my-list*))
cdr (pronounced cudder)
So for cdr is just the rest
(defvar *my-list* (list 0 1 2 3 4 5))
; Returns rest of the elements in list i.e. 1 2 3 4 5
(format t "First element is ~a ~%" (cdr *my-list*))
All the combinations
Found a page which seems to explain the unexplainable. To understand the terms now we need to understand
This place ... | Is equivalent to this place ... |
(caar x) | (car (car x)) |
(cadr x) | (car (cdr x)) |
(cdar x) | (cdr (car x)) |
(cddr x) | (cdr (cdr x)) |
(caaar x) | (car (car (car x))) |
(caadr x) | (car (car (cdr x))) |
(cadar x) | (car (cdr (car x))) |
(caddr x) | (car (cdr (cdr x))) |
(cdaar x) | (cdr (car (car x))) |
(cdadr x) | (cdr (car (cdr x))) |
(cddar x) | (cdr (cdr (car x))) |
(cdddr x) | (cdr (cdr (cdr x))) |
(caaaar x) | (car (car (car (car x)))) |
(caaadr x) | (car (car (car (cdr x)))) |
(caadar x) | (car (car (cdr (car x)))) |
(caaddr x) | (car (car (cdr (cdr x)))) |
(cadaar x) | (car (cdr (car (car x)))) |
(cadadr x) | (car (cdr (car (cdr x)))) |
(caddar x) | (car (cdr (cdr (car x)))) |
(cadddr x) | (car (cdr (cdr (cdr x)))) |
(cdaaar x) | (cdr (car (car (car x)))) |
(cdaadr x) | (cdr (car (car (cdr x)))) |
(cdadar x) | (cdr (car (cdr (car x)))) |
(cdaddr x) | (cdr (car (cdr (cdr x)))) |
(cddaar x) | (cdr (cdr (car (car x)))) |
(cddadr x) | (cdr (cdr (car (cdr x)))) |
(cdddar x) | (cdr (cdr (cdr (car x)))) |
(cddddr x) | (cdr (cdr (cdr (cdr x)))) |
Functions 3
A while ago we did lists and they had this insane thing called caar, cadar, cddar as Car of Car and gets the car of car or the first item in the list
- caar is (car (car x))
- cadar is (car (cdr (car x)))
- cddar is (cdr (cdr (car x)))
caar
(car (car x))
From inside to outside
First item = (superman (6 ft 3 in) ( 230 lbs))
And first item of this = superman
cadar
(car (cdr (car x)))
From inside to outside
First item = (superman (6 ft 3 in) ( 230 lbs))
rest of items = (6 ft 3 in) ( 230 lbs))
first item (6 ft 3 in)
cddar
(cdr (cdr (car x)))
From inside to outside
First item = (superman (6 ft 3 in) ( 230 lbs))
rest of items = (6 ft 3 in) ( 230 lbs))
rest of items = ( 230 lbs))
(defparameter *my-heroes* `( (superman (6 ft 3 in) ( 230 lbs))
(batman (6 ft 0 in) ( 190 lbs))
(spiderman (6 ft 2 in) ( 210 lbs))))
(defun get-heroes-data (size)
(format t "Heroes ~a ~%"
`(,(caar size) is ,(cadar size), (cddar size))))
; Heroes (cdr (cdr (car x)))(Superman Is (6 Ft 3 In) ((230 Lbs)))
(get-heroes-data *my-heroes*)
Mapping over a list
Here we map over a function, in this case numberp
;; mapcar example of using a function
;; mapcar List is (T T T Nil Nil)
(format t "mapcar List is ~a ~%" (mapcar #'numberp `(1 2 3 g d)))
Lambda
Lambda is a way of defining an anonymous function in lisp. We can pass functions to mapcar using the hash quote e.g. #`
; mapcar List is (3 6 9 12 15)
(format t "mapcar List is ~a ~%" (mapcar #'(lambda (x) (* 3 x)) '(1 2 3 4 5)))
Got confused with this because on the use of print
(mapcar #'(lambda (x) (print (* 3 x))) '(1 2 3 4 5))
Guess this the equivalent of
[1, 2, 3, 4, 5].map((n)=> console.log(n))
And
// mapcar List is [1, 2, 3, 4, 5]
console.log('mapcar List is %o', [1, 2, 3, 4, 5].map((n)=> n))
multiple-value-bind
Not too sure what a macro is, but that is what this is. I suspect it is a way of extending existing functionality. This is an example of calling a function which returns two values. To do this we use the accessor values
(defun expose-me (num)
(values (expt num 2) (expt num 3)))
;; Example of multiple value call
(multiple-value-bind (arg-a arg-b) (expose-me 2)
(format t "2^2 = ~d 2^3 = ~d~%" arg-a arg-b))
Macros
Example 1
This is taken from geeksforgeeks and does seem easy to understand however here is the quote from another page. A macro is an ordinary piece of Lisp code that operates on another piece of putative Lisp code, translating it into (a version closer to) executable Lisp.
(defmacro sumup (a b)
; This line will be executed whenever
; we use the macro sum
; with two integer parameters
(format t "~D + ~D : ~D" a b (+ a b))
)
; Calling macro sumup with parameters 7 and 18
(sumup 7 18)
(terpri)
So done with all that and my good friend Derek Banas sorts the Greek out for me. He explained that a common problem my be that you have to often have
multiple lines in an if statement and it can be annoying to use progn all of the time to do this. (When I think of C and macro usage in it, it smacks of the same problem.
Here is an example of the problem not the example
(defvar *num* 2)
(defvar *num-2* 0)
(if (= *num* 2)
(progn
(setf *num-2* 3)
(format t "num-2 is ~d ~%" *num-2*)
)
(format t "Not equal to 2 ~%"))
So with macros we need to have it generate code not be code like we do in C
(defmacro ifit (condition &rest body)
`(if ,condition
(progn
,@body)
(format t "Can't Drive ~%"))
)
(setf *age* 18)
(ifit (>= *age* 18)
(format t "Line one of lisp ~%")
(format t "Line two of Lisp ~%")
(format t "Line three of Lisp ~%")
(terpri)
)
In the macro we use quasi quoting which is where we switch between code and data using the comma for expression and ,@ for the multiple lines. This replacing the condition and body with the quoted value not the evaluated value.
Example 2
I am not feeling the love with second example. I will have to use some of this to appreciate it I think. It is just like C I think so just a way to generate repetitive code where the language is to granular.
;; Function to add two numbers
(defun add (a b)
(let ((sum (+ a b)))
(format t "Sum of ~d and ~d is ~d ~%" a b sum)
sum)
)
;; Create a macro to add two numbers
(defmacro letx (var val &rest body)
`(let ((,var ,val))
,@body)
)
;; define a function to use letx
(defun addx (a b)
(letx sum (+ a b)
(format t "Sum of ~d and ~d is ~d ~%" a b sum)
sum)
)
;; Call the function
(addx 2 3)
Classes In Lisp
We can define classes with attributes/properties as we do in typescript.
Example from Copilot
This was the approach spat out by copilot
;; Define a clas for a dog which has a name and sound
(defclass dog ()
((name :initarg :name :accessor name)
(sound :initarg :sound :accessor sound))
)
;; Create an instance of the dog class
(defvar *dog* (make-instance 'dog :name "Rover" :sound "Woof"))
;; Get the name of the dog
(format t "Dog name is ~a ~%" (name *dog*))
;; Get the sound of the dog
(format t "Dog sound is ~a ~%" (sound *dog*))
Example from Derek
Here is the second attempt
;; Define dog class
(defclass dog2 ()
((name)
(sound))
)
;; Use make-instance to create an instance of the dog class
(defvar *dog2* (make-instance 'dog2))
;; Set the name of the dog
(setf (slot-value *dog2* 'name) "Rover")
;; Set the sound of the dog
(setf (slot-value *dog2* 'sound) "Woof")
;; Get the name of the dog
(format t "Dog name is ~a ~%" (slot-value *dog2* 'name))
;; Get the sound of the dog
(format t "Dog sound is ~a ~%" (slot-value *dog2* 'sound))
I did some homework, but sir!!!!, and it said
Example 3 with hello vera
This is another example with some options
;; This time we have
;; an accessor name different value dog-name
;; default sound of woof
;; food we will be write only accessor
;; home will be read only accessor
(defclass dog3 ()
(
(name
:initarg :name :accessor dog-name)
(sound
:initarg :sound :accessor dog-sound :initform "woof" )
(food
:initarg :sound :reader get-food :initform "bounce" )
)
)
;; Create an instance of the dog3 class
(defvar *dog3* (make-instance 'dog3 :name "Rover"))
;; Get the name of the dog
(format t "Dog name is ~a ~%" (dog-name *dog3*))
(format t "Dog sound is ~a ~%" (dog-sound *dog3*))
(format t "Dog food is ~a ~%" (get-food *dog3*))
Adding Methods
Methods do not belong to classes in lisp. Instead they belong to generic functions. Let create one which take a parameter
;; Let define a generic function for dog3
(defgeneric show-properties (dog))
;; Define a method for generic function to show properties of dog3
(defmethod show-properties ((dog dog3))
(format t "Property name is ~a ~%" (slot-value dog 'name))
(format t "Property sound is ~a ~%" (slot-value dog 'sound))
)
(format t "---------------- ~%")
;; Use show-properties to show the properties of dog3
(show-properties *dog3*)
This outputs
----------------
Property name is Rover
Property sound is woof
Subclass a Class
We can do this too
;; To define a subclass of dog3
(defclass dog4 (dog3)
(
(breed
:initarg :breed :accessor dog-breed)
)
)
;; Create an instance of dog4
(defvar *dog4* (make-instance 'dog4 :name "Rover2" :breed "Labrador"))
;; Use the method show-properties to show the properties of dog4
;; Not no breed is shown
(show-properties *dog4*)
This outputs
----------------
Property name is Rover2
Property sound is woof
Data Structures
Arrays
Simple Array
We do this like so
;; Define an array on names
(defvar *names* (make-array 5 :initial-element "Fred"))
;; Set the first element of the array
(setf (aref *names* 0) "Fred")
;; Set the second element of the array
(setf (aref *names* 1) "Fred2")
3D Array
We can do 3-dimensional arrays too
;; Make a 3x3 number array
(defvar *numbers* (make-array '(3 3)
:initial-contents '((0 1 2) (3 4 5) (6 7 8))))
;; Print the array with dotimes
(dotimes (i 3)
(dotimes (j 3)
(format t "Number is ~d ~%" (aref *numbers* i j))
)
)
Hashtables
Getting into this as they start talking my language. Here is creation and remove of hash table
;; Make a hash table
(defvar *my-hash* (make-hash-table))
;; Set the value of the hash table
(setf (gethash '101 *my-hash*) "Fred 101")
(setf (gethash '102 *my-hash*) "Fred 102")
(setf (gethash '103 *my-hash*) "Fred 103")
;; Get the value of the hash table
(format t "Hash value is ~a ~%" (gethash '101 *my-hash*))
;; Using maphash and lambda format the hash table
(maphash #'(lambda (key value)
(format t "Key is ~a Value is ~a ~%" key value)
) *my-hash*)
;; Delete a key from the hash table
(remhash '101 *my-hash*)
Structures
We can create custom structure to store our data
;; Create a custom structure
(defstruct person
name
age
id)
;; Create an instance of the person structure
(defvar *person* (make-person :name "Fred" :age 21 :id 101))
;; Get the name of the person
(format t "Person name is ~a ~%" (person-name *person*))
;; Change the id of the person
(setf (person-id *person*) 102)
File-IO
And Finally Esther, And finally Cyril. We are able to read and write files with lisp
Writing a File
;; An example of writing new data to a file checking if it exists
(defun write-to-file (filename data)
(with-open-file (stream filename
:direction :output
:if-exists :supersede)
(princ data stream))
)
;; Write some data to a file
(write-to-file "test.txt" "Hello World")
Writing a File
;; An example of reading data from a file that exists
(defun read-from-file (filename)
(with-open-file (stream filename)
(let ((data (read-line stream)))
(format t "Data is ~a ~%" data)
data))
)
;; Read some data from a file
(read-from-file "test.txt")
Schema
The seems to be a battle even longer than the curly bracket between lisp people. So I have found something call schema from mit which has led me back to why I was looking at this - continuation. It took me a while (which I do not think it should do) to do
echo | mit-scheme --load test.sch
So to demonstrate the call/cc (which is not available in common lisp we have
(let (
(my-val (call/cc
(lambda (the-continuation)
(display "This will be executed\n")
(the-continuation 5)
(display "Hello 2 not executed \n") ) ) ) )
(display my-val) )
This seems to amount to early return which seems a lot of noise about nothing. We shall see.
Special Forms
Here is a list of special forms and where used some detail
block | let* | return-from |
catch | load-time-value | setq |
eval-when | locally | symbol-macrolet |
flet | macrolet | tagbody |
function | multiple-value-call | the |
go | multiple-value-prog1 | throw |
if | progn | unwind-protect |
labels | progv | |
let | quote |
quote
Quote is a special form which stops the lisp interpreter. So the thing is a value (number, symbol, string) rather than a variable in a function
I had to read about this because of my stuffed up brain. In lisp there is some referred to as a quote. And they kept mentioning shorthand but they were incredible unclear what for. and it what the function quote. What they meant is ` means quote ()
(format t "Quote is ~a ~%" (+ 1 2)) ; Quote is 3
(format t "Quote is ~a ~%" (quote (+ 1 2))) ; Quote is (+ 1 2)
(format t "Quote is ~a ~%" `(+ 1 2)) ; Quote is (+ 1 2)
setq
This appears to be identical to setf i.e. (setq x y) is exactly equivalent to (setf x y). Maybe legacy?
(defvar *test-data* "Fred")
(format t "default ~a ~%" *test-data*)
(setq *test-data* "Fredq")
(format t "setq ~a ~%" *test-data*)
(setf *test-data* "Fredf")
(format t "setf ~a ~%" *test-data*)
return-from
By default the return value is the last statement executed. To override this we use the special form return-from and the function name
(defun get-average-return (a b)
(return-from get-average-return (/ (+ a b) 2)))
if
With lisp the if statement has two lines by default to implement the else. For not we add an not operator
;; If statements are written as follows, if true then do this else do this
(if (= 1 1)
(format t "1 is equal to 1 ~%")
(format t "1 is not equal to 1 ~%"))
;; Not equal
(if (not (= 1 1))
(format t "1 is equal to 1 ~%")
(format t "1 is not equal to 1 ~%"))
;; And
(if (and (= 1 1) (= 2 2))
(format t "1 is equal to 1 and 2 is equal to 2 ~%")
(format t "1 is not equal to 1 and 2 is not equal to 2 ~%"))
;; And or haha
(if (or (= 1 1) (= 2 2))
(format t "1 is equal to 1 or 2 is equal to 2 ~%")
(format t "1 is not equal to 1 or 2 is not equal to 2 ~%"))