Lisp
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
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
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
;; 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 ~%"))
;; Multiple statements with progn
(progn
(format t "Hello ~%")
(format t "World ~%"))
;; Used with if
(if (and (= 1 1) (= 2 2))
(progn
(format t "1 is equal to 1 and 2 is equal to 2 ~%")
(format t "Hello ~%")
(format t "World ~%"))
(format t "1 is not equal to 1 and 2 is not equal to 2 ~%"))
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)))
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