Lisp and its derivatives are unlike any other group of languages. Lisp is a concatenation of the phrase “List Processing.” Lisp was developed in the late 1950s at the Massachusetts Institute of Technology (MIT). A brief introduction to its early history is at Lisp History (GNU Emacs Lisp Reference Manual). After FORTRAN, it is the second older high-level language still in common use. The Wikipedia summary of the language is at Lisp (programming language) – Wikipedia.
I first used Lisp when I was earning my Master of Science in Computer Science at Indiana University. Before starting at IU, I wrote two large FORTRAN IV programs, including a High School Registration system and a Vietnamese Refugee locator system of the Red Cross, while teaching at George Washington High School in Guam. On the first day of class, we were assigned 5 LISP programs to write by Wednesday. I had only used punch cards, so I had to learn how to use a terminal, open and save files, learn Lisp, and submit all five files in three days.
Those three days were fun, but it made me appreciate Lisp. Lisp was the most used language by the professors at IU. Our major projects were assigned primarily in Lisp. One project was to write a Pascal interpreter in Lisp. Another was to find the shortest path (both distance and time) between two cities, based on a Lisp data file. Lisp data files look a lot like Lisp program files, everything is in parenthesis.
The real meaning of Lisp is the phrase “List Processing.” My alternate meaning is “Lots of Insipid Stupid Parenthesis.
Course Material
I have a student whom I am teaching Lisp and with whom I am relearning Lisp. I am using Practical Common Lisp (gigamonkeys.com) for learning and teaching Lisp. Some other locations that might be helpful:
- Why I Still ‘Lisp’ (and You Should Too) | by Anurag Mendhekar | Better Programming
- Lispbox (common-lisp.dev) – downloadable file used in book we are using
- Common Lisp – Visual Studio Marketplace – downloaded additional VS Code
- Added automatic REPL config and startup by smashedtoatoms · Pull Request #58 · nobody-famous/alive · GitHub – perhaps helpful information on configuring VS Code Common Lisp.
Installing LispBox
- Go to LispBox (common-lisp.dev) and select the appropriate version for your system’s OS.
- Download
- For Windows, using 7-zip to unzip the file.
- Place the parent directory “lispbox-0.7-ccl-1.6-windowsx86” in a place easy to access. For example, I used my user account directory: C:\Users\ww_co\lispbox-0.7-ccl-1.6-windowsx86
- Go to lispbox-0.7 and run “lispbox” to bring up an emacs editor. If all starts well, you should have a prompt like: CL-USER> <where you can type your first commands.
- Read Lather, Rinse, Repeat: A Tour of the REPL (gigamonkeys.com) for an introduction.
VS Code Common Lisp
As I discover information about how to actually use VS Code Common Lisp add-on, I will add to this section.
Learning Lisp lessons
As I develop this section, it will contain the lessons I am doing with Brendan. When LispBox is started, it brings up the REPL environment within an EMACS editor. The combination helps you create Lisp commands and files and use them within your session. Commands not saved by the end of a session will be lost for any future sessions.
Using EMACS
You can edit Lisp files in EMACS. In order to move around the edit buffer there are a few terms you need to know. A more complete list of Control commands is at Moving Point (GNU Emacs Manual). EMACS was developed before Windows and has its own set of definitions. For example, Control-c does not copy the selected text. The basics EMACS controls are below:
Movement
Control-a - beginning of line
Control-e - end of line
Control-p - previous line (same position in the previous line)
Control-n - next line (same position in the next line)
Control-x - Two step sequence, usually controls files
Control-f - Create a file, asks for name in bottom of window, opens file buffer
Control-s - Save the created file with the previously specified name
Control-c - Two step sequence, usually controls processes
Control-c - compiles function defined by "defun"
Colorado State University Computer Science Department also created a guide at Basic Emacs Editor Commands (colostate.edu).
Cut, Copy, and Paste
These are the “official versions” of Cut, Copy, and Paste. However, I started using the entries under the Edit tab instead to actually do copies and pastes.
Control-w - cut selected text
Control-y - copy selected text
Meta-w - past to selected location
Meta keys do not exist on most keyboards, they are often replaced by either the Alt key or pressing and releasing the Esc key. See How keystrokes are denoted in Emacs (iu.edu) for more information.
Lisp Definitions
Lisp provides methods to define functions and variables. Since Lisp is a List Processor, all lists reside within parentheses. Lisp variables and functions include:
Defining | Name | Form | Example | |
variable | defvar | (defvar <name> <value>) | (defvar *db* nil) | |
function | defun | (defun <name> (<params>) (<func>)) | (defun add-record (cd) (push cd *db*)) | |
load | load | (load “<file>”) | (load “cd-db.lisp”) | |
loop | loop | (loop for i in list <do>) | loops through elements of a list | |
dolist | dolist | (dolist (<elem> <list>) statements | takes each list element & executes statements. | |
Later | Commands | Coming Soon | ||
first | car | (car <list>) | returns first element | |
rest | cdr | (cdr <list>> | returns remaining elements |
Note: A variable starting with ‘*’ indicates a global variable. The define variable function shown defines a global variable *db* as an empty (nil) database. The define function example creates the function add-record, which takes the defined variable cd and adds it to the created database. By convention, functions and variables start with an alphabetic character and have alphabetic characters and hyphens in the rest of their name. Numbers can be added to distinguish similar variables.
Lisp Learning 1
The first lesson is to install LispBox and do all the exercises in Lather, Rinse, Repeat: A Tour of the REPL (gigamonkeys.com). I would like to add a short assignment at the end of each chapter. The book has very good instructions, Type the first commands exactly like described in the text and you should receive the same results. I will start my example with formatting. LispBox uses the EMACS editor. It has a block cursor whether you are in Insert or Overtype mode.
CL-USER> (format t "Hello World!")
Hello World!
NIL
CL-USER>
Eventually functions need to be defined, that can be executed later. I will give the definition and function results here:
CL-USER> (defun hello-world () (format t "Hello world!"))
HELLO-WORLD
CL-USER> (hello-world)
Hello world!
NIL
Notice that all commands are surrounded by parenthesis. Data and code have the same format in Common Lisp. Also, Lisp is case insensitive. I type a mixed or all lower-case string and the computer always responds with a capitalized name.
Saving and Using a File
From the REPL frame of your EMACS editor, type Control-x Control-f to bring up a “Slime-REPL” one line prompt at the bottom of the EMACS editor. Then type the name of your file like “hello.lisp” at the end of the line. It should look like:
Find file: C:\Users\s00026361\lispbox-0.7-ccl-1.6-windowsx86\lispbox-0.7/hello.lisp
You should have a blank form in which to type the code. I usually start with a format statement to make sure the file is found and is loaded correctly. The code I typed was:
(format T "Loading 'Hello World'")
(defun hello-world ()
(format T "Hello World"))
Then type Control-x Control-s to save the file. The line at the bottom should either say “Wrote” with the name of the file or “No changes to save” if nothing needs to be saved. There are many ways to bet back to the REPL buffer, but I just press the “X” on the menu bar. Then load the file and try the command. Your REPL commands should look like:
CL-USER> (load "hello.lisp")
Loading 'Hello World'
#P"c:/Users/s00026361/lispbox-0.7-ccl-1.6-windowsx86/lispbox-0.7/hello.lisp"
CL-USER> (hello-world)
Hello World
NIL
CL-USER>
The assignment is to change the phrase “Hello World” to anything you would like and test the program again. You can type Control-x Control-f and retype “hello.lisp” at the end of the line. When you press [Enter], the file will be brought up in your EMACS editor with the most recent changes.
Lisp Lesson 2
You are now ready to start with a new chapter. Please read Practical: A Simple Database (gigamonkeys.com). This chapter introduces functions, macros, and special operators. For their first use, the three items are almost interchangeable. The exercise is to create a database for CDs and LPs. Examples are given, but you may wish to choose your own items to make it more interesting. The document describes lists and labeling elements in a list thoroughly, so I will start with defining a function (defun) to create a CD record. Create the command:
(defun make-cd (title artist rating ripped) (list :title title :artist artist :rating rating :ripped ripped))
This command creates a record with four entries. The title, artist, and rating entries are straight forward. The rating can either be your own evaluation or the general consensus of a particular album. Ripped simply means that it is (T) or is not (F) copied to your computer. An entry to your CD database can be created as follows:
CL-USER> (make-cd "Roses" "Kathy Mattea" 7 t)
(:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 7 :RIPPED T)
CL-USER>
Lisp has a tendency to capitalize everything. On the line after your (make-cd command, the system will print the elements of the diabase entry with their appropriate label. This is good for one CD, but that does not make a database. You can go through the exercises and create a database in your EMACS Lisp section, or you can go straight to creating a file to contain your database. This example will do the latter. I would put them all into one file:
;;; Set up the database for CDs
(format t "Define what items are in each CD description")
(defun make-cd (title artist rating ripped)
(list :title title :artist artist :rating rating :ripped ripped))
(format t "~%Define a Lisp database *db* containing nothing")
(defvar *db* nil)
(format t "~%Define how to add a CD to the database")
(defun add-record (cd) (push cd *db*))
(format t "~%Now add database entries")
(format t "~%For Kathy Mattea Roses")
(add-record (make-cd "Roses" "Kathy Mattea" 7 t))
(format t "~%For Dicie Chicks Fly")
(add-record (make-cd "Fly" "Dixie Chicks" 8 t))
(format t "~%For Dicie Chicks Home")
(add-record (make-cd "Home" "Dixie Chicks" 9 t))
I saved this information in cd-db.lisp and then loaded the file into my environment. The results of that load should produce:
CL-USER> (load "cd-db.lisp")
Define what items are in each CD description
Define a Lisp database *db* containing nothing
Define how to add a CD to the database
Now add database entries
For Kathy Mattea Roses
For Dicie Chicks Fly
For Dicie Chicks Home
#P"c:/Users/ww_co/lispbox-0.7-ccl-1.6-windowsx86/lispbox-0.7/cd-db.lisp"
CL-USER>
You can create a global variable by placing an asterisk at the start of the name. *db* is a global variable and is accessible after “cd-db.lisp” was loaded. When I type *db*, I get the following results:
CL-USER> *db*
((:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T) (:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T) (:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 7 :RIPPED T))
CL-USER>
The chapter gives two different formatting for printing the data base. Go to this reference chapter to see what the results should be. The chapter also has definitions to allow the user to enter database entries from the console. I put these function definitions in manipulate.lisp:
;;; Formatting the database into a reasonable form.
(format t "First rendition of dump-db-1")
(defun dump-db-1 ()
C
(format t "~%Second rendition of dump-db-2")
(defun dump-db-2 ()
(format t "~{~{~a:~10t~a~%~}~%~}" *db*))
(format t "~%Prepare a way to have entries to the DB made easily.")
(defun prompt-read (prompt)
(format *query-io* "~a: " prompt)
(force-output *query-io*)
(read-line *query-io*))
(format t "~%Ask the user for input for each field. -> prompt-for-cd")
(defun prompt-for-cd ()
(add-record(make-cd
(prompt-read "Title")
(prompt-read "Artist")
(prompt-read "Rating")
(prompt-read "Ripped [T/F]"))))
I then can load manipulate.lisp after I loaded cd-db.lisp.
CL-USER> (load "manipulate.lisp")
First rendition of dump-db-1
Second rendition of dump-db-2
Prepare a way to have entries to the DB made easily.
Ask the user for input for each field. -> prompt-for-cd
#P"c:/Users/s00026361/lispbox-0.7-ccl-1.6-windowsx86/lispbox-0.7/manipulate.lisp"
CL-USER>
At this point, the first two dump-db commands produce:
CL-USER> (dump-db-1)
TITLE: Home
ARTIST: Dixie Chicks
RATING: 9
RIPPED: T
TITLE: Fly
ARTIST: Dixie Chicks
RATING: 8
RIPPED: T
TITLE: Roses
ARTIST: Kathy Mattea
RATING: 7
RIPPED: T
NIL
CL-USER> (dump-db-2)
TITLE: Home
ARTIST: Dixie Chicks
RATING: 9
RIPPED: T
TITLE: Fly
ARTIST: Dixie Chicks
RATING: 8
RIPPED: T
TITLE: Roses
ARTIST: Kathy Mattea
RATING: 7
RIPPED: T
NIL
CL-USER>
All functions need to be in matched sets of parentheses to work. Try adding a record and see if the addition works.
CL-USER> (prompt-for-cd)
Title: Rubber Soul
Artist: Beatles
Rating: 10
Ripped [T/F]: t
((:TITLE "Rubber Soul" :ARTIST "Beatles" :RATING "10" :RIPPED "t") (:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T) (:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T) (:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 7 :RIPPED T))
CL-USER> (dump-db-2)
TITLE: Rubber Soul
ARTIST: Beatles
RATING: 10
RIPPED: t
TITLE: Home
ARTIST: Dixie Chicks
RATING: 9
RIPPED: T
TITLE: Fly
ARTIST: Dixie Chicks
RATING: 8
RIPPED: T
TITLE: Roses
ARTIST: Kathy Mattea
RATING: 7
RIPPED: T
NIL
CL-USER>
You have now completed your section on databases. Congratulations!
Lisp Lesson 3
You have learned quite a bit about Lisp and are now ready to learn about Syntax and Semantics. Read Syntax and Semantics (gigamonkeys.com) to prepare for this lesson. I describe functions in the Lisp Definitions block above. I will add new functions to that list, when needed. Lisp has two processes to interpret code, a reader that finds the Lisp objects and an evaluator that uses those objects to implement the semantics of the language. There is a whole section the Syntax and Semantics (gigamonkeys.com) chapter. Please read that section thoroughly.
S-Expressions are the basic elements of Lisp (List Processor). They can be either lists, surrounded by a set of parentheses, or a single item called an atom. Lists consist of atoms of other lists separated by white space. Atoms can have letters, numbers, or other special characters. Atoms are either strings (in quotes), variables, or functions. An example of a function name is prompt-for-cd. This is a single atom. All data and programs have the same format. Learn to love parentheses. In college, I created a phrase based on the name LISP, Lots of Insipid Stupid Parentheses, as a reminder that everything in Lisp is in Parentheses.
You need to be aware of a few key ideas. Strings are always within quotes, like “This is a sentence.” A group of letters, numbers, and special characters that create an atom is always a function or variable name.
This is a short, but important section. Please review Chapter 4: Syntax and Semantics (gigamonkeys.com) so that you are comfortable with lists, atoms, and parentheses.
Lisp Lesson 4 – Functions and Variables
We have worked with functions and variables since Lisp Lesson 2. This section builds on that knowledge. You will learn to define and change the value of variables, as well as use parameters in your function calls. Please review Chapter 5 Functions (gigamonkeys.com) and Chapter 6 Variables (gigamonkeys.com).
Functions
defun is the key word to indicate you are defining a function. The form:
(defun <name> (<parameters>)
"String documenting what is being accomplished>"
<commands to be executed>)
There are four types of parameters which can be used in a function call. The required and optional are the most commonly used parameters. These are the two types of parameters which will be used in this section. The parameter types must appear in the following order.
Type | Flag | Purpose |
Required | <None> | Must have matching parameters for every listed parameter. |
Optional | &optional | Value defaults to NIL but can be assigned default values. optional values must be listed. |
rest | &rest | Sweeps up any parameters not in first two |
key | &key | keyword associated with parameter (like ‘:a’) |
&key parameters Provide binding of names to other parameters in the function call. Please see the Chapter 5 Functions (gigamonkeys.com) for more detail. Any combination, in the right order, is permissible. It is recommended that the &optional and &key types are not used in the same function definition. The results of such a combination are not always predictable.
An example of as call using required and optional parameters is:
(defun poly (sides &optional (length 1.0))
(format t "~%For s polygon with ~d sides of length ~d" sides length)
With poly as the name of the function, sides as a required parameter, and length as an optional parameter with 1.0 as its default value if no value is specified in the call. As a reminder for formatting, ~% indicates a new line and ~d holds the place for the value of a variable. In this example, the value of sides and length will be printed.
Polish Notation
All mathematical operations use Polish notation written as (<operand> <argument(s)>). Examples include:
(+ 3 4) ; Add 3 plus 4 and take the value 7 for future use.
(/ 5 2) ; Divide 5 by 2 and take the value 2.5 for future use
(- 3 5) ; Subtract 5 from 3 and take the value -2 for future use
(* 5 2) ; Multiply 5 by 2 and take the value 10 for future use
Variables
Unless all values used in a program are specified constants, variables are a critical part of any program language. In most languages, a variable’s type must be specified before the variable is used. In Common Lisp, this is not the case. Assigning a variable a value is done in a list of the form:
(variable <value>)
(length 1.0) % Assign the variable length with the value 1.0
(*degrees* (/ 180 sides)) % Divide 180 by sides and assign to global variable
length is a local variable with no ‘*’ surrounding the name. *degrees* is a global variable locally assigned the value of 180 divided by the number of sides of the polygon. We will cover four ways of setting the value of a variable.
Name | When | Set Value | |
defvar | Initial | Name required, value not required | |
defparameter | Initial | Name required, value not required | |
let | Body | form: (let (var val) (var2 val2)) (let ((p (* len sides)) (d (/ 180 sides))) code) Code is supposed to use let variable definitions | |
setf | Body | form (setf variable value) (setf p (* len sides)) (setf d (/ 180 sides)) code follows, but not in setf parenthesis |
Using (let)
Variables | Common Lisp (lisp-lang.org) has an interesting description of (let). I tried to give an assignment of calculating the area and perimeter of a regular polygon, but I could not get the (let) command to work properly. The program I tried looked like:
(defvar *pi-degrees* 1.0)
(defvar *degrees* 1.0)
(defvar *radians* 1.0)
(defvar *tangent* 1.0)
(defvar *apothem* 1.0)
(defvar *perimeter* 1.0)
(defvar *area* 2.0)
(defun poly (sides &optional (len 1.0))
(format t "~%For s polygon with ~d sides of length ~d" sides len)
(let ((*perimeter* (* len sides))
(*degrees* (/ 180 sides))
(*pi-degrees* (* pi *degrees*))
(*radians* (/ *pi-degrees* 180))
(*tangent* (tan *radians*))
(*apothem* ( / len (* 2.0 *tangent*)))
(*area* ( / ( * *perimeter* *apothem*)2)))
(format t "~%The Perimeter is ~d" *perimeter*)
(format t "~%The Degrees of the angle is ~d" *degrees*)
(format t "~%Pi ~d times ~d degrees is ~d" pi *degrees* *pi-degrees*)
(format t "~%The equivalent radians of ~d degrees is ~d" *degrees* *radians*)
(format t "~%The Tangent is ~d" *tangent*)
(format t "~%The Apothem is ~d" *apothem*)
(format t "~%The Area is ~d~%" *area*)
)
)
(format T "Calculate area and perimeter of five-sided polygon")
(poly 4)
(poly 6 3)
The math was divided into sections to help me debug the problem, when the second call started repeating values from the first call. The value for *pi-degrees* is calculated the first time through the program but stays the same for the second execution. The reults are:
CL-USER> (load "polygon.lisp")
Calculate area and perimeter of five sided-polygon
For s polygon with 4 sides of length 1.0
The Perimeter is 4.0
The Degrees of the angle is 45
Pi 3.141592653589793D0 times 45 degrees is 3.141592653589793D0
The equivalent radians of 45 degrees is 0.0055555557
The Tangent is 1.5574077
The Apothem is 0.5
The Area is 0.5
For s polygon with 6 sides of length 3
The Perimeter is 18
The Degrees of the angle is 30
Pi 3.141592653589793D0 times 30 degrees is 3.141592653589793D0
The equivalent radians of 30 degrees is 0.0055555557
The Tangent is 1.5574077
The Apothem is 1.5
The Area is 0.5
#P"c:/Users/ww_co/lispbox-0.7-ccl-1.6-windowsx86/lispbox-0.7/polygon.lisp"
CL-USER>
Using (setf)
Since let did not allow me to change the value of variables, I decided to try setf. The structure of the program changed some, simplifying the use of parenthesis. The revised code looks like:
(defvar *pi-degrees* 1.0)
(defvar *degrees* 1.0)
(defvar *radians* 1.0)
(defvar *tangent* 1.0)
(defvar *apothem* 1.0)
(defvar *perimeter* 1.0)
(defvar *area* 2.0)
(defun poly (sides &optional (len 1.0))
(format t "~%For s polygon with ~d sides of length ~d" sides len)
(setf *perimeter* (* len sides))
(format t "~%The Perimeter is ~d" *perimeter*)
(setf *degrees* (/ 180 sides))
(format t "~%The Degrees of the angle is ~d" *degrees*)
(setf *pi-degrees* (* pi *degrees*))
(format t "~%Pi ~d times ~d degrees is ~d" pi *degrees* *pi-degrees*)
(setf *radians* (/ *pi-degrees* 180))
(format t "~%The equivalent radians of ~d degrees is ~d" *degrees* *radians*)
(setf *tangent* (tan *radians*))
(format t "~%The Tangent is ~d" *tangent*)
(setf *apothem* ( / len (* 2.0 *tangent*)))
(format t "~%The Apothem is ~d" *apothem*)
(setf *area* ( / ( * *perimeter* *apothem*)2))
(format t "~%The Area is ~d~%" *area*)
)
(format T "Calculate area and perimeter of multiple polygons.")
(poly 4)
(poly 6 3)
Using setf makes the lines a bit simpler and allows the format lines to be immediately after any setf command within the poly function. Now, the results from the two executions of the poly function produce different results, which is good. The results are:
CL-USER> (load "polygon-setf-1.lisp")
Calculate area and perimeter of multiplke polygons.
For s polygon with 4 sides of length 1.0
The Perimeter is 4.0
The Degrees of the angle is 45
Pi 3.141592653589793D0 times 45 degrees is 141.3716694115407D0
The equivalent radians of 45 degrees is 0.7853981633974483D0
The Tangent is 0.9999999999999999D0
The Apothem is 0.5000000000000001D0
The Area is 1.0000000000000002D0
For s polygon with 6 sides of length 3
The Perimeter is 18
The Degrees of the angle is 30
Pi 3.141592653589793D0 times 30 degrees is 94.24777960769379D0
The equivalent radians of 30 degrees is 0.5235987755982988D0
The Tangent is 0.5773502691896257D0
The Apothem is 2.598076211353316D0
The Area is 23.382685902179844D0
#P"c:/Users/ww_co/lispbox-0.7-ccl-1.6-windowsx86/lispbox-0.7/polygon-setf-1.lisp"
CL-USER>
This way, the area of the second polygon is calculated correctly.
Consolidation
Each calculation does not need to be in its own code-line. However, be careful of errors creeping in if too many lines are combined. I found that calculating the tangent and then using that in later calculations gave me a result that agreed with keeping each calculation on its own line. Any further consolidation calculated an area larger than expected. The new code is:
; Combine calculations to simplify problem
(defvar *tangent* 1.0)
(defvar *apothem* 1.0)
(defvar *perimeter* 1.0)
(defvar *area* 2.0)
(defun poly (sides &optional (len 1.0))
(format t "~%For s polygon with ~d sides of length ~d" sides len)
(setf *perimeter* (* len sides))
(format t "~%The Perimeter is ~d" *perimeter*)
(setf *tangent* (tan (/ (* pi (/ 180 sides))180)))
(format t "~%The Tangent is ~d" *tangent*)
(setf *apothem* ( / len (* 2.0 *tangent*)))
(format t "~%The Apothem is ~d" *apothem*)
(setf *area* ( / ( * *perimeter* *apothem*)2))
(format t "~%The Area is ~d~%" *area*)
)
(format T "Calculate area and perimeter of multiple polygons")
(poly 4)
(poly 6 3)
I also moved the format statements to be right after the setf calculations. This allowed me to see exactly where any mistake occurred. The output is:
CL-USER> (load "polygon-setf.lisp")
Calculate area and perimeter of multiple polygons
For s polygon with 4 sides of length 1.0
The Perimeter is 4.0
The Tangent is 0.9999999999999999D0
The Apothem is 0.5000000000000001D0
The Area is 1.0000000000000002D0
For s polygon with 6 sides of length 3
The Perimeter is 18
The Tangent is 0.5773502691896257D0
The Apothem is 2.598076211353316D0
The Area is 23.382685902179844D0
#P"c:/Users/ww_co/lispbox-0.7-ccl-1.6-windowsx86/lispbox-0.7/polygon-setf.lisp"
CL-USER>
The final results are much close to the desired answers than further consolidation. When I consolidated any further, the results were:
CL-USER> (load "polygon-setf.lisp")
Calculate area and perimeter of multiple polygons
For s polygon with 4 sides of length 1.0
The Perimeter is 4.0
The Apothem is 0.6366197723675814D0
The Area is 1.2732395447351628D0
For s polygon with 6 sides of length 3
The Perimeter is 18
The Apothem is 2.8647889756541165D0
The Area is 25.783100780887047D0
#P"c:/Users/ww_co/lispbox-0.7-ccl-1.6-windowsx86/lispbox-0.7/polygon-setf.lisp"
CL-USER>
The apothem is off and thus causes the area to be a little larger than the correct calculations.
Problem
Originally, I assigned the problem to find the area and perimeter of a regular polygon. Since it took me a while to develop a program with the correct results, the assignment has changed. Find the area and perimeter (circumference) of a circle using the parameter radius.
Lisp Lesson 5 – Macros: Standard Control Constructs
For this section, read Macros: Standard Control Constructs (gigamonkeys.com). Basic functions and variable manipulation have been covered. This section covers special commands available in lisp that provide variable testing and looping.