You are on page 1of 7

Chapter 4: Specialized DataStructures

1. Arrays
2. Example: Binary Search
3. Strings And Characters
4. Sequences
5. Example: Parsing Dates
6. Structures
7. Examples: Binary Search Trees
8. Hashtables

Notes:

other (than lists) data structures - arrays (including vectors and strings),
structures and hashtables.
Not as flexible as lists, but can make access faster, and take up less space.

CL has one other datastructure, the instance. Covered in Chapter 11, which
describes CLOS.

1. Arrays

In CL you make an array by calling (make-array ..) with the dimensions of the
required array as the first parameter.

[1]> (setf arr (make-array '(2 3) :initial-element nil))


#2A((NIL NIL NIL) (NIL NIL NIL))
[2]> arr
#2A((NIL NIL NIL) (NIL NIL NIL))

The :initial-element is optional. If provided, the whole array will be initialized


to that value.
To retrieve an array element use aref

[3]> (aref arr 0 0)


NIL

To set an array element, use setf with aref to specify the position
[4]> (setf (aref arr 0 0) 2)
2
[5]> arr
#2A((2 NIL NIL) (NIL NIL NIL))
[6]> (aref arr 0 0)
2

To make a one dimensional array just use a single integer instead of a list of
dimensions.

[9]> (setf arr (make-array 4 :initial-element 3))


#(3 3 3 3)
[10]> arr
#(3 3 3 3)
[13]> (aref arr 0 )
3

To make a one dimensional array


use

[16]> (vector 1 2 3)
#(1 2 3)

Note: literal form of vector

[17]> #( 1 2 3)
#(1 2 3)

to retrieve an element from a vector

[18]> (setf myvec #( 1 3 4 ))


#(1 3 4)
[19]> myvec
#(1 3 4)
[20]> (svref myvec 2)
4

2. Example: Binary Search

Algorithm skipped revisit when doing ds-alg.

Comments in lisp

;; this is a comment

for extensive comments #| many lines of comment #|

3. Strings And Characters

Strings are vectors of characters.


A constant string is a list of characters surrounded by "".
Each character has an associated integer, often but not always the ascii number.
char-code and code-char can be used to convert between characters and integers.

[21]> #\A
#\A
[22]> (char-code #\A)
65
[23]> (code-char 65)
#\A

[25]> (sort "phantom" #'char<)


"ahmnopt"

[26]> (sort "phantom" #'char>)


"tponmha"

you can use aref or the faster char to replace characters in string.

[27]> (setf mystr "hello")


"hello"
[28]> mystr
"hello"
[29]> (aref mystr 1)
#\e
[30]> (char mystr 1)
#\e

[34]> (setf (char mystr 3) #\k)


#\k
[35]> mystr
"helko"

to compare strings, can use equal, but string-equal is faster


[36]> (string-equal "hello" "hello")
T
[37]> (string-equal "hello" "HELLO")
T

concatenate appends many strings together, with the first parameter indicating the
type.

[38]> (concatenate 'string "hello " " there" " and bye")
"hello there and bye"

4. Sequences

In common lisp, the type sequence includes both vectors and lists (so also strings)

some of the functions we have been using are actually sequence functions, including
remove, length, subseq, reverse, sort, every, and some.
All these functions work for vectors as well.

[41]> (length #(1 2 3 4))


4

So far we have seen four functions that work to retrieve elements of sequences.

nth for lists, aref or svref for vectors, and char for strings.
CL provides a function elt that works for any sequence.

[41]> (length #(1 2 3 4))


4
[42]> (elt '(a b c) 1)
B
[43]> (elt #(1 2 3) 1)
2
[44]> (elt "hello" 1)
#\e

many sequence functions take one or more keyword arguments from the standard set
listed below

parameter purpose default


:key a function to apply to each element. identity
:test the test function for comparison eql
:from-end if true work backwards nil
:start position at which to start 0
:end position at which, if any, to end nil
One function that takes the full set is position, which returns the position of an
element in the sequence, or nil if it is found.
We will use position to illustrate the roles of the keyword arguments.

[1]> (position #\a "fantasia")


1
[2]> (position #\a "fantasia" :start 3 :end 5)
4
[3]> (position #\a "fantasia" :from-end t)
7

[6]> (position 'a '((a b) ( c d)) :key #'car)


0
[7]> (position 'a '((c d) ( a b)) :key #'car)
1

:test takes a function of two arguments that it uses for equality testing. The
default is eql.
To match a list, you can use equal instead

[8]> (position '(a b) '((a b) (c d)))


NIL
[9]> (position '(a b) '((a b) (c d)) :test #'equal)
0

The test function can be *any* function (_ that takes two elements and returns a
boolean).
so we could find the first position of an element of a sequence less than a given
value by

[15]> (position '4 '(9 7 5 3 2 1) :test #'>)


3

To find the position of an element in the sequence satisfying a predicate with a


single argument, we use position if

[18]> (position-if #'oddp '(8 7 5 3 2 1) )


1

it takes all keywords except test.

Functions similar to member, member-if for sequences are find and find-if. Unlike
the member functions they return only the object they were looking for.

find takes all keyword arguments. find-if takes all keyword arguments except :test.

[20]> (find-if #'characterp '( 1 2 3 #\h "am"))


#\h

find with a keyword argument is often clearer than find-if

the functions remove and remove-if work on sequences.

remove-duplicates preserves only the last of each occurrence of every element in a


sequence.

[21]> (remove-duplicates "abracadabra")


"cdbra"
The function reduce is for boiling down a sequence into a single value.
It takes at *least* two arguments a function and a sequence.
The function must take two arguments.

In the simplest case, it will be called with the first two elements of the
sequence, and therafter with successive elements of the list as the second
argument, and the value it returned the last time as the first argument.

so

(reduce #'fn '( a b c d)) is equivalent to (fn (fn (fn a b) c) d)

We can use reduce to extend functions that take two arguments.

[25]> (reduce #'intersection '( (h e r n i a) (c a n c e r) (s t r o k e)))


(E R)

5. Example: Parsing Dates

parse function- better to do functional parsing here I think, or an explicit


recursive descent parser.
PG's code parses explicitly with position-if s and subseq

6. Structures

A structure is considered a deluxe kind of vector.

Suppose you had to keep track of a number of rectangular solids.

you might consider representing them as a vector of three quantities - length,


width, height.

and then you define functions like

(defun block-height (b) (sv-ref b 2))

You can think of a structure as a vector with all these functions defined for you.

we use

(defstruct solid
l
w
h)

This defines the structure solid with three fields , l, w, h.


It also defines the functions make-solid, solid-l, solid-b, solid-h

Section 2.3 mentioned that lisp programs can write programs. defstruct is an
example.
When you call defstruct it automatically writes code defining several other
functions.
With macros, we can do the same, and reconstruct defstruct if necessary.

to initialize a struct (_ note: this is different from scheme etc where calling the
constructor function directly is enough, here we use setf
(setf p (make-solid :l 0 :w 0 :h 0))

[26]> (defstruct solid l w h)


SOLID

[27]> (setf s (make-solid :l 0 :w 0 :h 0))


#S(SOLID :L 0 :W 0 :H 0)

[28]> s
#S(SOLID :L 0 :W 0 :H 0)

[29]> (solid-l s)
0

Defining a structure also creates a type of that name.


So, in addition to being a structure, an instance is also a struct, then atom, then
t

[30]> (solid-p s)
T
[31]> (typep s 'solid)
T

We can specify default values for the structure's fields by having a list of a
field name and a default-value generating expression instead of just the field name
in the original definition.

[32]> (defstruct polemic (ptype (progn (format t "what type of polemic was it?")
(read) )) (effect "none"))
POLEMIC
[33]> (make-polemic)
what type of polemic was it?scathing
#S(POLEMIC :PTYPE SCATHING :EFFECT "none")

customizing what prefix to add for fields (default is the struct name) and printing
function elided.

7. Examples: Binary Search Trees

skipped. Revisit after segdalg work.

8. Hashtables

[34]> (setf ht (make-hash-table))


#S(HASH-TABLE :TEST FASTHASH-EQL)

To retrieve the value associated with a key we use gethash with a key and a
hashtable

[35]> (gethash 'color ht)


NIL ;
NIL
An expression can return more than one value. gethash returns two values. the first
is the value associated with the key, and the second says whether the hash has any
value stored under that key.

(here) because the second value is nil we know that there is no value held under
this key and the first nil is 'just a default'.

To associate a value with a key we do

[36]> (setf (gethash 'color ht) 'red)


RED
[37]> (gethash 'color ht)
RED ;
T

remhash removes a key-value pair from a hashtable.

[40]> (setf fruit (make-hash-table))


#S(HASH-TABLE :TEST FASTHASH-EQL)
[41]> (setf (gethash 'apricot fruit) t)
T
[42]> fruit
#S(HASH-TABLE :TEST FASTHASH-EQL (APRICOT . T))
[43]> (remhash 'apricot fruit)
T
[44]> fruit
#S(HASH-TABLE :TEST FASTHASH-EQL)
[45]> (remhash 'apricot fruit)
NIL

You might also like