156 lines
5.5 KiB
Common Lisp
156 lines
5.5 KiB
Common Lisp
;; Remember that that a list is a string of cons cells
|
|
|
|
;; (cons 1 (cons 2 (cons 3 nil)))
|
|
;; >> (1 2 3)
|
|
|
|
;; but if we do it a bit different:
|
|
;;(cons 1 (cons 2 3))
|
|
;; >> (1 2 . 3) A bit different
|
|
|
|
;; This dot notation is lisp saying:
|
|
;; I tried to print this structure you entered using list notation
|
|
;; but the last item in the list didn't contain the usual nil
|
|
;; I expected; instead, it contained 3
|
|
|
|
;; A list that ends in something other than nil is refeered to as a doted list
|
|
|
|
;; dotted lists aren't that useful of a tool
|
|
;; It would be unusual for a lisp programmer to store data in one
|
|
;; However, given the pervasiveness of cons cells in Lisp,
|
|
;; you will frequently encounter a non-nil value at the end of a chain of
|
|
;; cons cells.
|
|
;; That's why you should become familiar with dotted lists, even if you never
|
|
;; Use them directly
|
|
|
|
;; A proper list could be written in dot notation
|
|
;; '(1 . (2 . (3 . nil)))
|
|
|
|
;; Thinking of it like this shows us why lisp is forced to show
|
|
;; the final cons cell
|
|
|
|
;; One common use for dotted lists is to elegantly represent pairs
|
|
;; (cons 2 3)
|
|
;; >> (2 . 3)
|
|
|
|
;; Creating pairs like this is conveient and efficient
|
|
;; we can extract members from the pair using standard car and cdr commands
|
|
;; efficient because Lisp only needs a single cons cell to connect two items
|
|
|
|
;; These types of pairs are commonly used in Lisp programs
|
|
;; For instance, it could be used to store x/y coors of a point or a key/value
|
|
;; pair in a complex data structure
|
|
|
|
;; Circular lists are a thing. A cons cell can point to an upstream
|
|
;; cons cell of a list
|
|
|
|
;; Before messing with cirular lists, we should do this
|
|
|
|
(setf *print-circle* t)
|
|
|
|
;; This let's list know we are doing stuff with self-referential data structs
|
|
;; and that it needs to be careful when printing on the screen
|
|
|
|
;; A straightforward way to do this is to use setf to put extra stuff
|
|
;; in the first parameter
|
|
|
|
;; (defparameter foo '(1 2 3))
|
|
;; (setf (cadddr foo) foo)
|
|
;; >> #1=(1 2 3 . #1#)
|
|
|
|
;; Here we created an infinite list of '(1 2 3 1 2 3 1 2 3 ...)
|
|
;; by replacing the nil at the end of a simple list with a reference to the
|
|
;; list itself
|
|
|
|
;;Association Lists -- We've used them a bit
|
|
|
|
;; alist for short
|
|
|
|
;; An alist consists of key/value pairs stored in a list
|
|
|
|
;; if a key appears multiple times in a list, it is assumed that the first
|
|
;; appearance of the key contains the desired value
|
|
|
|
;; (defparameter *drink-order* '((bill . double-espresso)
|
|
;; (lisa . small-drip coffee)
|
|
;; (john . medium-latte)))
|
|
|
|
;; To look up the order for a person...
|
|
|
|
;;(assoc 'lisa *drink-order*)
|
|
|
|
;; >> (LISA . SMALL-DRIP-COFFEE
|
|
|
|
;; The function searches the list from the beginning to find the desired key
|
|
;; Let's say Lisa wants to change her order so...
|
|
|
|
;; (push '(lisa. large-mocha-with-whipped-cream) *drink-order*)
|
|
|
|
;; >> ((LISA . LARGE-MOCHA-WITH-WHIPPED-CREAM)
|
|
;; (BILL . DOUBLE-ESPRESSO)
|
|
;; (LISA . SMALL-DRIP-COFFEE)
|
|
;; (JOHN . MEDIUM-LATTE))
|
|
|
|
;; Because, by default, the first reference to a key in an alist takes
|
|
;; precedence over later references to the same key,
|
|
;; the order lisa placed for a small drip is superseded by her more recent one
|
|
|
|
;; (assoc 'lisa *drink-order*)
|
|
;; >> (LISA . LARGE-MOCHA-WITH-WHIPPED-CREAM)
|
|
|
|
;; However, there is an issue with alists.
|
|
;; They are not very efficient way to store and retrieve data
|
|
;; Unless dealing with short lists under a doezn items or so
|
|
;; alists are typically one of the first tools in the Lispers toolbox
|
|
;; they may be replaced by other data structures as a program matures
|
|
;; later chapter (9) will explain more
|
|
|
|
;; Lisp programs are represented with syntax expressions
|
|
;; In this format, data is represented using nexted lists
|
|
;; often with Lisp symbols at the front of each list
|
|
;; explaining the structure of the data
|
|
;; Suppose we want to represent the component parts of a house
|
|
|
|
(defparameter *house* '((walls (mortar (cement)
|
|
(water)
|
|
(sand))
|
|
(bricks))
|
|
(windows (glass)
|
|
(frame)
|
|
(curtains))
|
|
(roof (shingles)
|
|
(chimney))))
|
|
|
|
;; This data structure elegantly captures the hierarchical nature of the parts
|
|
;; That make up a house.
|
|
|
|
;; Since it's structured as a Lisp syntax expression, we can see the lists
|
|
;; that make up the levels of the hierarch
|
|
;; Also, it follows the convention of a syntax expression by putting a
|
|
;; symbol at the front of each list
|
|
|
|
;; For example, here we have the windows symbol that is then followed by three
|
|
;; items representing the glass, frame, and curtains
|
|
|
|
;; data that is hierarchical and tree-like in nature can be naturally
|
|
;; expressed in this way
|
|
|
|
;; If we move beyond tree-like structures, data stored in a syntax
|
|
;; expression can become hard to visualize
|
|
|
|
;; in mathematics a graph consists of a bunch of nodes connected
|
|
;; by deges
|
|
;; Such graphs can be stored in cons cells, but they are difficult
|
|
;; to visualize. We saw this in Ch 5 when we stored the map of the Wizard's
|
|
;; house (which consisted of a directed graph) in two alists
|
|
;; One containing the node info, and one containing the edge info
|
|
|
|
;; It's hard to get a decent understanding of such structs
|
|
;; Unfortunaily, data that has the shape of a graph or contains other
|
|
;; properties that go beyond simple tree structs are common.
|
|
;; Fortunatly, there is an open source tool that optimally arranges this data
|
|
;; to create a pretty drawing of a graph
|
|
|
|
;; see seperate file for graphviz stuff
|
|
|
|
;; Let's create a graph drawing library, again, see other file
|