You are on page 1of 574

Land of Lisp

T a ble of Conte nts


Ac knowle dgm e nts
Introduc tion
W ha t Ma ke s L isp So Cool a nd Unusua l?
If L isp Is So Gre a t, W hy Don't More Pe ople Use It?
W he re Did L isp Com e From ?
W he re Doe s L isp Ge t Its Powe r?
I. L isp is Powe r
1. Ge tting Sta rte d with L isp
L isp Dia le c ts
A T a le of T wo L isps
Up-a nd-Com ing L isps
L isp Dia le c ts Use d for Sc ripting
ANSI Com m on L isp
Ge tting Sta rte d with CL ISP
Insta lling CL ISP
Sta rting Up CL ISP
W ha t You've L e a rne d
2. Cre a ting Your First L isp Progra m
T he Gue ss-My-Num be r Ga m e
De fining Globa l Va ria ble s in L isp
De fining the sm a ll a nd big Va ria ble s
An Alte rna tive Globa l Va ria ble De finition Func tion
Ba sic L isp E tique tte
De fining Globa l Func tions in L isp
De fining the gue ss-m y-num be r Func tion
De fining the sm a lle r a nd bigge r Func tions
De fining the sta rt-ove r Func tion
De fining L oc a l Va ria ble s in L isp
De fining L oc a l Func tions in L isp
W ha t You've L e a rne d
3. E xploring the Synta x of L isp Code
Synta x a nd Se m a ntic s
T he Building Bloc ks of L isp Synta x
Sym bols
Num be rs
Strings
How L isp Distinguishe s Be twe e n Code a nd Da ta
Code Mode
Da ta Mode
L ists in L isp
Cons Ce lls
L ist Func tions
Ne ste d L ists
W ha t You've L e a rne d
II. L isp is Sym m e try
4. Ma king De c isions with Conditions
T he Sym m e try of nil a nd ()
E m pty E qua ls Fa lse
T he Four Disguise s of ()
T he Conditiona ls: if a nd Be yond
One T hing a t a T im e with if
Going Be yond if: T he whe n a nd unle ss Alte rna tive s
T he Com m a nd T ha t Doe s It All: c ond
Bra nc hing with c a se
Cool T ric ks with Conditions
Using the Ste a lth Conditiona ls a nd a nd or
Using Func tions T ha t Re turn More tha n Just the T ruth
Com pa ring Stuff: e q, e qua l, a nd More
W ha t You've L e a rne d
5. Building a T e xt Ga m e E ngine
T he W iz a rd's Adve nture Ga m e
Our Ga m e W orld
Ba sic Re quire m e nts
De sc ribing the Sc e ne ry with a n Assoc ia tion L ist
De sc ribing the L oc a tion
De sc ribing the Pa ths
How Qua siquoting W orks
De sc ribing Multiple Pa ths a t Onc e
De sc ribing Obje c ts a t a Spe c ific L oc a tion
L isting Visible Obje c ts
De sc ribing Visible Obje c ts
De sc ribing It All
W a lking Around in Our W orld
Pic king Up Obje c ts
Che c king Our Inve ntory
W ha t You've L e a rne d
6. Inte ra c ting with the W orld: Re a ding a nd Printing in L isp
Printing a nd Re a ding T e xt
Printing to the Sc re e n
Sa ying He llo to the Use r
Sta rting with print a nd re a d
Re a ding a nd Printing Stuff the W a y Hum a ns L ike It
T he Sym m e try Be twe e n Code a nd Da ta in L isp
Adding a Custom Inte rfa c e to Our Ga m e E ngine
Se tting Up a Custom RE PL
W riting a Custom re a d Func tion
W riting a ga m e -e va l Func tion
W riting a ga m e -print Func tion
T rying Out Our Fa nc y Ne w Ga m e Inte rfa c e
T he Da nge rs of re a d a nd e va l
W ha t You've L e a rne d
6. 5. la m bda : A Func tion So Im porta nt It De se rve s Its Own Cha pte r
W ha t la m bda Doe s
W hy la m bda Is So Im porta nt
W ha t You've L e a rne d
7. Going Be yond Ba sic L ists
E xotic L ists
Dotte d L ists
Pa irs
Circ ula r L ists
Assoc ia tion L ists
Coping with Com plic a te d Da ta
Visua liz ing T re e -like Da ta
Visua liz ing Gra phs
Cre a ting a Gra ph
Ge ne ra ting the DOT Inform a tion
T urning the DOT File into a Pic ture
Cre a ting a Pic ture of Our Gra ph
Cre a ting Undire c te d Gra phs
W ha t You've L e a rne d
8. T his Ain't Your Da ddy's W um pus
T he Gra nd T he ft W um pus Ga m e
De fining the E dge s of Conge stion City
Ge ne ra ting Ra ndom E dge s
L ooping with the loop Com m a nd
Pre ve nting Isla nds
Building the Fina l E dge s for Conge stion City
Building the Node s for Conge stion City
Initia liz ing a Ne w Ga m e of Gra nd T he ft W um pus
Dra wing a Ma p of Our City
Dra wing a City from Pa rtia l Knowle dge
W a lking Around T own
L e t's Hunt Som e W um pus!
W ha t You've L e a rne d
9. Adva nc e d Da ta type s a nd Ge ne ric Progra m m ing
Arra ys
W orking with Arra ys
Using a Ge ne ric Se tte r
Arra ys vs. L ists
Ha sh T a ble s
W orking with Ha sh T a ble s
Re turning Multiple Va lue s
Ha sh T a ble Pe rform a nc e
A Fa ste r Gra nd T he ft W um pus Using Ha sh T a ble s
Com m on L isp Struc ture s
W orking with Struc ture s
W he n to Use Struc ture s
Ha ndling Da ta in a Ge ne ric W a y
W orking with Se que nc e s
Cre a ting Your Own Ge ne ric Func tions with T ype Pre dic a te s
T he Orc Ba ttle Ga m e
Globa l Va ria ble s for the Pla ye r a nd Monste rs
Ma in Ga m e Func tions
Pla ye r Ma na ge m e nt Func tions
He lpe r Func tions for Pla ye r Atta c ks
Monste r Ma na ge m e nt Func tions
T he Monste rs
T o Ba ttle !
W ha t You've L e a rne d
III. L isp is Ha c king
10. L ooping with the loop Com m a nd
T he loop Ma c ro
Som e loop T ric ks
E ve rything You E ve r W a nte d to Know About loop
Using loop to E volve !
Growing Pla nts in Our W orld
Cre a ting Anim a ls
Sim ula ting a Da y in Our W orld
Dra wing Our W orld
Cre a ting a Use r Inte rfa c e
L e t's W a tc h Som e E volution!
E xpla ining the E volution
W ha t You've L e a rne d
11. Printing T e xt with the form a t Func tion
Ana tom y of the form a t Func tion
T he De stina tion Pa ra m e te r
T he Control String Pa ra m e te r
Va lue Pa ra m e te rs
Control Se que nc e s for Printing L isp Va lue s
Control Se que nc e s for Form a tting Num be rs
Control Se que nc e s for Form a tting Inte ge rs
Control Se que nc e s for Form a tting Floa ting-Point Num be rs
Printing Multiple L ine s of Output
Justifying Output
Ite ra ting T hrough L ists Using Control Se que nc e s
A Cra z y Form a tting T ric k for Cre a ting Pre tty T a ble s of Da ta
Atta c k of the Robots!
W ha t You've L e a rne d
12. W orking with Stre a m s
T ype s of Stre a m s
Stre a m s by T ype of Re sourc e
Stre a m s by Dire c tion
W orking with File s
W orking with Soc ke ts
Soc ke t Addre sse s
Soc ke t Conne c tions
Se nding a Me ssa ge ove r a Soc ke t
T idying Up Afte r Ourse lve s
String Stre a m s: T he Oddba ll T ype
Se nding Stre a m s to Func tions
W orking with L ong Strings
Re a ding a nd De bugging
W ha t You've L e a rne d
13. L e t's Cre a te a W e b Se rve r!
E rror Ha ndling in Com m on L isp
Signa ling a Condition
Cre a ting Custom Conditions
Inte rc e pting Conditions
Prote c ting Re sourc e s Aga inst Une xpe c te d Conditions
W riting a W e b Se rve r from Sc ra tc h
How a W e b Se rve r W orks
Re que st Pa ra m e te rs
Pa rsing the Re que st He a de r
T e sting ge t-he a de r with a String Stre a m
Pa rsing the Re que st Body
Our Gra nd Fina le : T he se rve Func tion!
Building a Dyna m ic W e bsite
T e sting the Re que st Ha ndle r
L a unc hing the W e bsite
W ha t You've L e a rne d
13. 5. Func tiona l Progra m m ing Is Be a utiful
IV. L isp is Sc ie nc e
14. Ra m ping L isp Up a Notc h with Func tiona l Progra m m ing
W ha t Is Func tiona l Progra m m ing?
Ana tom y of a Progra m W ritte n in the Func tiona l Style
Highe r-Orde r Progra m m ing
Code Com position with Im pe ra tive Code
Using the Func tiona l Style
Highe r-Orde r Progra m m ing to the Re sc ue
W hy Func tiona l Progra m m ing Is Cra z y
W hy Func tiona l Progra m m ing Is Fa nta stic
Func tiona l Progra m m ing Re duc e s Bugs
Func tiona l Progra m s Are More Com pa c t
Func tiona l Code Is More E le ga nt
W ha t You've L e a rne d
15. Dic e of Doom , a Ga m e W ritte n in the Func tiona l Style
T he Rule s of Dic e of Doom
A Sa m ple Ga m e of Dic e of Doom
Im ple m e nting Dic e of Doom , Ve rsion 1
De fining Som e Globa l Va ria ble s
Re pre se nting the Ga m e Boa rd
De c oupling Dic e of Doom 's Rule s from the Re st of the Ga m e
Ge ne ra ting a Ga m e T re e
Ca lc ula ting Pa ssing Move s
Ca lc ula ting Atta c king Move s
Finding the Ne ighbors
Atta c king
Re inforc e m e nts
T rying Out Our Ne w ga m e -tre e Func tion
Pla ying Dic e of Doom Aga inst Anothe r Hum a n
Cre a ting a n Inte llige nt Com pute r Oppone nt
T he Minim a x Algorithm
T urning Minim a x into Ac tua l Code
Cre a ting a Ga m e L oop with a n AI Pla ye r
Pla ying Our First Hum a n vs. Com pute r Ga m e
Ma king Dic e of Doom Fa ste r
Closure s
Me m oiz a tion
T a il Ca ll Optim iz a tion
A Sa m ple Ga m e on the 3-by-3 Boa rd
W ha t You've L e a rne d
16. T he Ma gic of L isp Ma c ros
A Sim ple L isp Ma c ro
Ma c ro E xpa nsion
How Ma c ros Are T ra nsform e d
Using the Sim ple Ma c ro
More Com ple x Ma c ros
A Ma c ro for Splitting L ists
Avoiding Re pe a te d E xe c ution in Ma c ros
Avoiding Va ria ble Ca pture
A Re c ursion Ma c ro
Ma c ros: Da nge rs a nd Alte rna tive s
W ha t You've L e a rne d
17. Dom a in-Spe c ific L a ngua ge s
W ha t Is a Dom a in?
W riting SVG File s
Cre a ting XML a nd HT ML with the ta g Ma c ro
Cre a ting SVG-Spe c ific Ma c ros a nd Func tions
Building a More Com plic a te d SVG E xa m ple
Cre a ting Custom Ga m e Com m a nds for W iz a rd's Adve nture Ga m e
Cre a ting Ne w Ga m e Com m a nds by Ha nd
L e t's T ry the Com ple te d W iz a rd's Adve nture Ga m e !
W ha t You've L e a rne d
18. L a z y Progra m m ing
Adding L a z y E va lua tion to L isp
Cre a ting the la z y a nd forc e Com m a nds
Cre a ting a L a z y L ists L ibra ry
Conve rting Be twe e n Re gula r L ists a nd L a z y L ists
Ma pping a nd Se a rc hing Ac ross L a z y L ists
Dic e of Doom , Ve rsion 2
Ma king Our AI W ork on L a rge r Ga m e Boa rds
T rim m ing the Ga m e T re e
Applying He uristic s
W inning by a L ot vs. W inning by a L ittle
Alpha Be ta Pruning
W ha t You've L e a rne d
19. Cre a ting a Gra phic a l, W e b-Ba se d Ve rsion of Dic e of Doom
Dra wing the Ga m e Boa rd Using the SVG Form a t
Dra wing a Die
Dra wing a T ile
Dra wing the Boa rd
Building the W e b Se rve r Inte rfa c e
W riting Our W e b Re que st Ha ndle r
L im ita tions of Our Ga m e W e b Se rve r
Initia liz ing a Ne w Ga m e
Announc ing a W inne r
Ha ndling the Hum a n Pla ye r
Ha ndling the Com pute r Pla ye r
Dra wing the SVG Ga m e Boa rd from W ithin the HT ML
Pla ying Ve rsion 3 of Dic e of Doom
W ha t You've L e a rne d
20. Ma king Dic e of Doom More Fun
Inc re a sing the Num be r of Pla ye rs
Rolling the Dic e
Building Cha nc e Node s
Doing the Ac tua l Dic e Rolling
Ca lling the Dic e Rolling Code from Our Ga m e E ngine
Upda ting the AI
Im proving the Dic e of Doom Re inforc e m e nt Rule s
Conc lusion
A. E pilogue
Func tiona l Guild Cruise r
Synopsis
How It Kills Bugs
E xpla na tion
W e a kne ss
Ma c ro Guild Me le e Fighte rs
Synopsis
How It Kills Bugs
E xpla na tion
W e a kne ss
Re sta rt Guild Arm ore d Fighte r
Synopsis
How It Kills Bugs
E xpla na tion
W e a kne ss
Ge ne ric Se tte r Guild Supply Ship
Synopsis
How It Kills Bugs
E xpla na tion
W e a kne ss
DSL Guild Hot Rods
Synopsis
E xpla na tion
W e a kne ss
CL OS Guild Ba ttle ship
Synopsis
How It Kills Bugs
E xpla na tion
E xpla na tion
W e a kne ss
T he Continua tion Guild Roc ke t Pods
Synopsis
How It Kills Bugs
E xpla na tion
W e a kne ss
Bre vity Guild Mic ro Fighte r
Synopsis
How It Kills Bugs
E xpla na tion
W e a kne ss
Multic ore Guild Form a tion Fighte rs
Synopsis
How It Fights Bugs
E xpla na tion
W e a kne ss
T he L a z y Guild Friga te
L isp Dia le c t
Synopsis
How It Kills Bugs
E xpla na tion
W e a kne ss
Inde x
Land of Lisp

Conrad Barski M. D.

Copyright © 2010
All rights re se rve d. No pa rt of this work m a y be re produc e d or tra nsm itte d in a ny form or by a ny m e a ns, e le c tronic or
m e c ha nic a l, inc luding photoc opying, re c ording, or by a ny inform a tion stora ge or re trie va l syste m , without the prior writte n
pe rm ission of the c opyright owne r a nd the publishe r.
No Sta rc h Pre ss a nd the No Sta rc h Pre ss logo a re re giste re d tra de m a rks of No Sta rc h Pre ss, Inc . Othe r produc t a nd c om pa ny na m e s
m e ntione d he re in m a y be the tra de m a rks of the ir re spe c tive owne rs. Ra the r tha n use a tra de m a rk sym bol with e ve ry oc c urre nc e of
a tra de m a rke d na m e , we a re using the na m e s only in a n e ditoria l fa shion a nd to the be ne fit of the tra de m a rk owne r, with no
inte ntion of infringe m e nt of the tra de m a rk.
T he inform a tion in this book is distribute d on a n “ As Is” ba sis, without wa rra nty. W hile e ve ry pre c a ution ha s be e n ta ke n in the
pre pa ra tion of this work, ne ithe r the a uthor nor No Sta rc h Pre ss, Inc . sha ll ha ve a ny lia bility to a ny pe rson or e ntity with re spe c t to
a ny loss or da m a ge c a use d or a lle ge d to be c a use d dire c tly or indire c tly by the inform a tion c onta ine d in it.
No Sta rc h Pre ss
De dic ation
For L a ure n
Ac knowle dgme nts
First of a ll, I’d like to tha nk m y wife L a ure n for le tting m e spe nd so m a ny we e ke nds on this book proje c t. I a m a lso pa rtic ula rly
gra te ful to Philip Fom inykh, the m a in te c hnic a l re vie we r for this book, whose e xte nsive e xpe rie nc e with prope r Com m on L isp form
a nd style he lpe d to re ign in m a ny (but not too m a ny) quirks in the sourc e c ode a nd in the disc ussions of L isp philosophy. I a lso owe
a gre a t de a l to He ow E ide -Goodm a n, who re vie we d the fina l c ha pte rs a nd he lpe d c om ple te this proje c t.
Ma ny folks a t No Sta rc h Pre ss ha d to wre stle with the idiosync ra tic prose a nd struc ture of this book to bring it into publisha ble
form . First a nd fore m ost, I wa nt to tha nk Ke ith Fa nc he r, m y prim a ry e ditor for m ost of this proje c t. Gre a t e ffort wa s a lso put in by
Bill Polloc k, Se re na Ya ng, Anse l Sta ton, Rile y Hoffm a n, Alison Pe te rse n, Ma gnolia Molc a n, Ka thle e n Mish, Don Ma rti, T yle r
Ortm a n, a nd Ada m W right. I’d a lso like to tha nk Aa ron Fe ng for e a rly fe e dba c k on the style of the book.
T his book proje c t origina lly be ga n a s a n e xpa nsion of m y “ Ca sting SPE L s in L isp” we b tutoria l. I wa nt to tha nk e ve ryone who
e m a ile d or ta lke d with m e a bout this tutoria l a nd he lpe d m e e xpa nd m y unde rsta nding of L isp a long the wa y. rm s (Ric ha rd
Sta llm a n), in pa rtic ula r, ga ve m e a lot of fe e dba c k on L isp style a nd he lpe d m e put toge the r the E m a c s L isp ve rsion of the
tutoria l. Ple a se c onside r dona ting to the Fre e Softwa re Founda tion (http://www. fsf. org/), a nonprofit orga niz a tion he founde d to
support the de ve lopm e nt of ope n sourc e a nd fre e softwa re , whic h inc lude s m a ny gre a t L isp tools. Ja m e s W e bb a lso he lpe d gre a tly
with the E m a c s L isp ve rsion. And a m ong the c ountle ss folks who ga ve fe e dba c k a nd/or c orre c tions on “ Ca sting SPE L s in L isp, ” I’d
e spe c ia lly like to tha nk Ke nny T ilton, Ma rsha ll Qua nde r, W e i-Ju W u, Christophe r Brown, Ste phe n Gra vroc k, Pa ul Sc hulz , Andy
Cowe ll, a nd Joha n Boc kgå rd.
Intr oduc tion
So, you’ve de c ide d to pic k up a book on L isp a nd re a d the introduc tion. Pe rha ps you we re surprise d to se e som e thing tha t looks
like a c om ic book m ixe d in with the othe r c om pute r progra m m ing books on the she lf. W ho would bothe r writing a c om ic book
a bout a we ird a c a de m ic progra m m ing la ngua ge like L isp? Or m a ybe you’ve he a rd othe r pe ople ra ving a bout the L isp la ngua ge a nd
thought, “ Boy, L isp sure sounds diffe re nt from othe r la ngua ge s pe ople ta lk a bout. Ma ybe I should pic k up a L isp book som e tim e . ”
E ithe r wa y, you’re now holding a book a bout a progra m m ing la ngua ge tha t is ve ry c ool but a lso ve ry unusua l.
W hat M ake s Lisp So Cool and Unusual?
L isp is a ve ry e x pre ssiv e la ngua ge . L isp is de signe d to le t you ta ke the m ost c om plic a te d progra m m ing ide a s a nd e xpre ss the m in
a c le a r a nd a ppropria te wa y. L ispe rs ha ve the fre e dom to write a progra m in e xa c tly the wa y tha t is m ost he lpful for solving a ny
proble m a t ha nd.
T he powe r a t your finge rtips whe n writing L isp c ode is wha t m a ke s it so diffe re nt. Onc e you “ ge t” L isp, you’ll be fore ve r
c ha nge d a s a progra m m e r. E ve n if you e nd up ne ve r writing L isp c ode a ga in for the re st of your life , le a rning L isp will
funda m e nta lly c ha nge you a s a c ode r.
In a wa y, le a rning a typic a l progra m m ing la ngua ge is sim ila r to le a rning a fore ign la ngua ge a s a n a dult. Suppose you go out
tom orrow a nd de c ide you’re going to le a rn Fre nc h. You m a y ta ke e ve ry c ourse on Fre nc h tha t you c a n find, re a d m a te ria ls tha t a re
only in Fre nc h, a nd e ve n m ove to Fra nc e . But no m a tte r wha t you do, your unde rsta nding of Fre nc h will a lwa ys re m a in a little
im pe rfe c t. And no m a tte r how good of a Fre nc h spe a ke r you e ve ntua lly be c om e , in your dre a m s you proba bly will still be spe a king
in your na tive la ngua ge .
L isp is diffe re nt. It’s not just like le a rning a ny fore ign la ngua ge . Onc e you’ve le a rne d L isp, you’ll e ve n dre a m in L isp. L isp is
suc h a powe rful ide a tha t it will c rowd out your pre vious progra m m ing e xpe rie nc e a nd be c om e your ne w m othe r tongue ! W he ne ve r
you e nc ounte r a ne w progra m m ing ide a in a ny la ngua ge , you’ll a lwa ys sa y to yourse lf, “ T ha t’s kind of how I’d do it in L isp, e xc e pt
. . . . ” T ha t’s the kind of powe r only L isp will give you.

At this point, a ll you m a y know a bout L isp is tha t a t le a st one pe rson (m e ) is e xtre m e ly e xc ite d a bout it. But your tim e is
va lua ble , a nd le a rning som e thing ne w is bound to re quire som e e ffort.
T he good ne ws is L isp isn’t re a lly a s diffic ult a s it m a y se e m a t first gla nc e . For insta nc e , the following is a va lid L isp
e xpre ssion:
(+ 3 (* 2 4))
Ca n you gue ss wha t the va lue of this e xpre ssion is? If you a nswe re d 11, the n you’ve a lre a dy figure d out how to re a d ba sic L isp
c ode . It is writte n just like m a th, e xc e pt tha t the func tions— in this c a se , a ddition a nd m ultiplic a tion— c om e be fore the num be rs,
a nd e ve rything is in pa re nthe se s.
If Lisp Is So G r e at, W hy Don't M or e P e ople Use It?
Ac tua lly, a fa ir num be r of la rge c om pa nie s do use L isp for som e se rious work (you’ll find a long list of industria l L isp proje c ts a t
http://snipurl. c om /e 3lv9/). Othe r progra m m ing la ngua ge s a re c onsta ntly “ borrowing” fe a ture s of L isp a nd pre se nting the m a s the
la te st a nd gre a te st ide a s. Also, the Se m a ntic W e b, whic h m a ny be lie ve will pla y a big role in the future of the W e b, use s m a ny
tools writte n in L isp.

Note

T he ide a be hind the Se m a ntic W e b is to c re a te a se t of protoc ols for we bsite s to follow so tha t a c om pute r c a n de te rm ine the
“ m e a ning” of inform a tion on a we b pa ge . T his is done by a nnota ting we b pa ge s with spe c ia l m e ta da ta (usua lly in a form a t c a lle d
Re sourc e De sc ription Fra m e work, or RDF) tha t links to c om m on voc a bula rie s, whic h diffe re nt we bsite s m a y sha re . Ma ny of the
tools use d for working with de sc ription logic s a nd RDF da ta a re writte n in L isp (for e xa m ple , Ra c e rPro a nd Alle groGra ph).

So, L isp c e rta inly ha s a prom ising future . But som e m a y think tha t le a rning L isp is not worth the e ffort.
How did L isp ge t this unde se rve d re puta tion?
I think tha t pe ople use a rule of thum b whe n de c iding wha t things in life a re worth le a rning. Most pe ople se e k knowle dge in one
of the following thre e c a te gorie s:

W ha t m a ny othe r pe ople le a rn (c a lc ulus, C++, a nd so on)


W ha t is e a sy to le a rn (hula -hooping, Ruby, a nd so on)
W ha t ha s va lue tha t is e a sy to a ppre c ia te (the rm onuc le a r physic s, for e xa m ple , or tha t ridic ulously loud whistle
whe re you stic k your finge rs in your m outh)

L isp doe sn’t fa ll into a ny of the se c a te gorie s. It’s not a s popula r a s c a lc ulus, pa rtic ula rly e a sy to le a rn, or a s obviously va lua ble
a s tha t loud whistle . If we we re to follow the se (usua lly ve ry se nsible ) rule s of thum b, we would c onc lude tha t a re a sona ble pe rson
should sta y a wa y from L isp. Howe ve r, in the c a se of L isp, we ’re going to throw out the se rule s. As you’ll se e from re a ding this
book, L isp give s you insights into c om pute r progra m m ing tha t a re so profound tha t e ve ry se rious progra m m e r should ha ve som e
e xpe rie nc e with this unusua l la ngua ge , e ve n if it re quire s a little e ffort.
If you’re still not c onvinc e d, you m ight wa nt to ta ke a pe e k a t the c om ic book e pilogue wa y a t the e nd of the book. You m ight
not be a ble to unde rsta nd e ve rything in the re right now, but it will give you a fe e l for the a dva nc e d fe a ture s a va ila ble within L isp
a nd wha t m a ke s L isp progra m m ing diffe re nt from othe r type s of progra m m ing.
W he r e Did Lisp Come F r om?
T he L isp fa m ily of la ngua ge s is truly a nc ie nt, with a history tha t diffe rs from othe r la ngua ge s. W e ’ll ne e d to tra ve l fa r ba c k in
tim e to ge t to the be ginning of it a ll.
A long tim e a go (wa y ba c k in the 1940s), the E a rth wa s c ove re d by a gia nt oc e a n c a lle d the Pa ntha la ssic Oc e a n, a long with a
single ba rre n la nd m a ss na m e d Pa nga e a . In this unforgiving e nvironm e nt, the first c om pute r progra m s e volve d, writte n in pure
m a c hine la ngua ge (or “ one s a nd z e ros, ” a s the y sa y).
T he se protola ngua ge s we re tightly bound to spe c ific c om pute r syste m s, suc h a s the E NIAC, the Z use Z 3, a nd othe r e a rly va c uum -
tube c ontra ptions. Ofte n, the se e a rly c om pute rs we re so prim itive tha t “ progra m m ing” the m involve d sim ply flipping switc he s or
pa tc hing c a ble s to physic a lly e nc ode e a c h ope ra tion.
T he da rk da ys of the se protola ngua ge s sa w a lot of e xpe rim e nta tion with diffe re nt c om pute r a rc hite c ture s a nd a n e xplosion of
diffe re nt c om pute r instruc tion se ts. Com pe tition wa s fie rc e . W hile m ost of the se prim itive la ngua ge e xpe rim e nts ultim a te ly
disa ppe a re d— vic tim s of a nc ie nt ba ttle s for surviva l— othe rs thrive d.

At a c e rta in point, c om pute rs a c quire d the ir own in m e m ory to store progra m s, a long with prim itive asse mble rs tha t a llowe d
progra m s to be writte n in te xt, inste a d of with just pure num be rs. T he se asse mbly la ngua ge s inc lude d Short Code , ARC a sse m bly,
a nd E DSAC Initia l Orde rs.
Asse m bly la ngua ge s m a de softwa re de ve lopm e nt m uc h m ore e ffic ie nt, e na bling a nc ie nt a sse m ble rs to e va de the m a ny pre da tors
in this prim ordia l oc e a n. But a sse m bly la ngua ge s still ha d signific a nt lim ita tions. T he y we re a lwa ys de signe d a round the instruc tion
se t of a spe c ific proc e ssor a nd so the y we re not porta ble a c ross diffe re nt m a c hine a rc hite c ture s. Progra m m ing la ngua ge s ne e de d to
e volve to survive be yond the c onfine s of a spe c ific m a c hine instruc tion se t.
T he 1950s sa w the a rriva l of the first m a c hine -inde pe nde nt progra m m ing la ngua ge s. L a ngua ge s like Autoc ode a nd Inform a tion
Proc e ssing L a ngua ge a c c om plishe d this inde pe nde nc e not only through lungs a nd le gs, but a lso through ne w type s of softwa re , suc h
a s c om pile rs a nd inte rpre te rs.
W ith c om pile rs a nd inte rpre te rs, c om pute r progra m s c ould now be writte n in a hum a n-frie ndly synta x. A c ompile r c a n ta ke a
hum a n-writte n c om pute r progra m a nd c onve rt it a utom a tic a lly into a m a c hine -frie ndly bina ry form a t tha t the c om pute r c a n
e xe c ute . An inte rpre te r, on the othe r ha nd, pe rform s the a c tions de sc ribe d in a hum a n-writte n progra m dire c tly, without c onve rting
the m a ll the wa y down to a m a c hine -frie ndly bina ry form a t.
For the first tim e , progra m m e rs c ould use la ngua ge s tha t we re de signe d to m a ke c om pute r progra m m ing a ple a sa nt a c tivity,
without ne e ding to ope ra te a t the prim itive le ve l of the c om pute r ha rdwa re . T he se inte rpre te d a nd c om pile d progra m m ing
la ngua ge s a re wha t we now think of a s the first “ true ” progra m m ing la ngua ge s. One of the m ost im posing of the se e a rly la ngua ge s,
FORT RAN (de ve lope d in 1957), wa s wide ly supporte d on diffe re nt a rc hite c ture s a nd is still use d he a vily to this da y.

Up until this point, the m ost suc c e ssful la ngua ge s ha d be e n de signe d a round one c e ntra l ide a : to offe r a ge ne ra l de sign a nd synta x
tha t would m a ke progra m m ing a s e a sy a s possible for novic e s. Howe ve r, de signing a good progra m m ing la ngua ge turns out to be
ve ry diffic ult. He nc e , m ost of the se la ngua ge s, like FORT RAN, BASIC, a nd C, we re re a lly just a m ishm a sh of olde r ide a s, c opie d
from one a nothe r a nd thrown toge the r in a wa y tha t la c ke d a ny re a l be a uty. T he y we re usua lly e a sy to use in only supe rfic ia l wa ys.
None the le ss, the se fie rc e la ngua ge s roa m e d the jungle s for de c a de s in se a rc h of e a sy pre y.
In the sha dows of the se fe a rsom e be a sts lurke d a sm a ll, hum ble , a nd e ntire ly diffe re nt sort of c re a ture — m ostly hidde n from vie w,
but pre se nt a lm ost sinc e the ve ry first m a c hine -inde pe nde nt la ngua ge s c ra wle d onto la nd. T he se we re la ngua ge s tha t use d
m a the m a tic a l synta x, suc h a s the la m bda c a lc ulus, de ve lope d by m a the m a tic ia ns in the 1930s.
Not the le a st bit c onc e rne d with be ing pra gm a tic or e a sy for novic e s to le a rn, the se la ngua ge s we re highly inte llige nt a nd wa nte d
to push the lim its of la ngua ge de sign. T he y pose d que stions a bout progra m nota tion, la ngua ge se m a ntic s, a nd the sim ple st possible
la ngua ge synta x.
From the se highly inte llige nt m a the m a tic a l synta xe s e volve d one m ost nota ble c re a ture : the origina l L isp progra m m ing
la ngua ge . Unlike m ost othe r progra m m ing la ngua ge s, it did not e volve from FORT RAN or othe r la ngua ge s tha t we re c onc e rne d
with pra gm a tism or e a se of use . Its line a ge is a c om ple te ly se pa ra te one , dra wn stra ight from m a the m a tic s. But whe re did L isp
c om e from ?
Som e pe ople c la im tha t the story be hind L isp’s origins ha s be e n fore ve r lost in the fog of tim e . Othe rs (who a re proba bly m ore
c orre c t) sa y L isp’s c re a tion wa s the work of John Mc Ca rthy in 1959. One da y, it is sa id, he ga the re d toge the r his tribe a t MIT a nd
pre se nte d a n inge nious ide a . Mc Ca rthy e nvisione d a c om ple te ly the ore tic a l progra m m ing la ngua ge , whic h would ha ve m inim a l
synta x a nd se m a ntic s but, a t the sa m e tim e , c re a te inc re dibly e le ga nt progra m s. T he se progra m s we re so e le ga nt tha t e ve n writing
a n inte rpre te r for L isp in L isp itse lf would ta ke only a round 50 line s of c om pute r c ode !

Note

John Mc Ca rthy publishe d the pa pe r “ Re c ursive Func tions of Sym bolic E xpre ssions a nd T he ir Com puta tion by Ma c hine , Pa rt I, ”
Com m unic a tions of the ACM (April 1960): 184-195. You c a n re a d it a t http://www-form a l. sta nford. e du/jm c /re c ursive . pdf.

W he n Mc Ca rthy first publishe d his ide a , it wa s inte nde d only a s a n inte lle c tua l e xplora tion of m a the m a tic a l synta x. But soon,
the L isp la ngua ge e volve d a nd c ould work with c om pile rs a nd inte rpre te rs. It now ra n on re a l c om pute rs, just like FORT RAN a nd
the othe r progra m m ing la ngua ge s! But unlike the se othe r la ngua ge s, L isp re ta ine d a be a uty de rive d from its m a the m a tic a l
a nc e stry.
Soon a fte r the first L isps a ppe a re d, the first L isp progra m m e rs a ppe a re d, c a pturing the se doc ile c re a ture s a nd tra nsform ing the m
into e ve r-m ore -re fine d progra m m ing la ngua ge s. Ove r tim e , the se progra m m e rs turne d the prim a l L isps into dia le c ts suc h a s
MACL ISP a nd Inte rlisp.

Although the hunting of e a rly L isps wa s a suc c e ssful a voc a tion for e a rly L isp progra m m e rs, it soon be c a m e c le a r tha t the se
hunte rs ha d a c om pe titor: Cro-Ma gnon m a n. T he Cro-Ma gnons we re m ore a ggre ssive tha n the pe a c e ful L isp progra m m e rs, a tta c king
e ve r-bigge r softwa re de ve lopm e nt proje c ts using fe a rsom e la ngua ge s suc h a s COBOL . De ve lope d for busine ss a pplic a tions, COBOL
wa s a n ugly a nd vile be he m oth tha t none the le ss m a de luc ra tive pre y for the Cro-Ma gnons. L isp progra m m e rs, on the othe r ha nd,
we re m ore c onte nt c onte m pla ting e le ga nt progra m m ing a nd hunting the oc c a siona l L isp.
Now, while L isp wa s a n inc re dibly powe rful ide a , othe r progra m m ing la ngua ge s a lre a dy ha d a he a d sta rt in m ind sha re a nd m ore
m a ture de ve lopm e nt tools. T his m a de it a c ha lle nge for L isps, a nd the L isp progra m m e rs de pe nde nt on the m , to ge t the tra c tion
the y ne e de d for m a instre a m suc c e ss. Howe ve r, the ge ntle L ispe rs we re not c onc e rne d with suc h pe tty things. De spite the ir diffe ring
dispositions, the L ispe rs a nd the Cro-Ma gnons live d side by side in re la tive ha rm ony.
In the ir own wa y, the L ispe rs we re thriving. At tha t tim e , the y be ne fite d he a vily from highly a c a de m ic re se a rc h in a re a s suc h a s
im a ge re c ognition, c om pute riz e d da ta c la ssific a tion, a nd othe r proble m s tha t fa ll unde r the ge ne ra l um bre lla of artific ial
inte llige nc e (A I). T he highly m a the m a tic a l na ture of the se proble m s le nt the ir inve stiga tion to a L ispy a pproa c h, a nd L isp
progra m m e rs built ne w dia le c ts of L isp into e ve r-m ore -a dva nc e d c om pute r syste m s to a tta c k the m . Ma ny c onside r this the Golde n
Age of L isp.
Unfortuna te ly, a fte r this brie f golde n pe riod, the winds une xpe c te dly turne d on the poor L ispe rs. In the m id-1980s, a sudde n tilt
in the a xis of the E a rth a lte re d the c lim a te , c a using shorta ge s in the food sourc e s tha t the L isp la ngua ge s ne e de d to survive .
Disa ppointm e nts in the progre ss of AI re se a rc h c a use d m a ny gra nts for a c a de m ic re se a rc h to dry up, a nd m uc h of the ha rdwa re
fa vore d by the L isps (suc h a s L isp m a c hine s from Sym bolic s, L isp Ma c hine , Inc . , a nd T e xa s Instrum e nts) fe ll be hind the
c a pa bilitie s of m ore tra ditiona l c om ple x instruc tion se t c om pute r (CISC) a nd re duc e d instruc tion se t c om pute r (RISC) ha rdwa re
a rc hite c ture s. T he world ha d be c om e a n unwe lc om ing pla c e for L isps a nd the L isp progra m m e rs tha t de pe nde d on the m for
surviva l. T he “ AI winte r” ha d a rrive d, a nd L isp wa s doom e d.
T his fina lly ga ve the Cro-Ma gnons the de finite a dva nta ge in the la ngua ge ra c e . T he ne w c ra z e of m e ga lithic , FORT RAN-
de rive d, obje c t-orie nte d la ngua ge s— suc h a s C++, de ve lope d in 1983— ha d slowly c onque re d c om m e rc ia l softwa re de ve lopm e nt.
T his ga ve the Cro-Ma gnons c om ple te im m unity from the AI winte r, whic h wa s a fflic ting the L ispe rs. Furthe rm ore , the wily Cro-
Ma gnons borrowe d som e of the ide a s pione e re d by the L ispe rs to pa tc h up the proble m s of m a instre a m la ngua ge s. T hus, ga rba ge
c olle c tion a nd pa ra m e tric polym orphism , origina lly found in the L isps, be c a m e c om m on in the la ngua ge s use d by m a instre a m
progra m m e rs.
E ve ntua lly, through im m e nse e ffort, the la ngua ge be he m oths of olde n da ys ha d be e n ta m e d by the Cro-Ma gnons into C#, Ja va ,
a nd sim ila r la ngua ge s. T he be lie f a rose tha t the se la ngua ge s we re m ore ple a sa nt to use a s tools tha n a nything a va ila ble in the pa st,
with the Golde n Age of L isp long forgotte n. More re c e ntly, la ngua ge s suc h a s Python a nd Ruby ha ve furthe r re fine d the se Cro-
Ma gnon la ngua ge s into m ore m ode rn dire c tions.
But wha t ha s ha ppe ne d to the L isp progra m m e rs during a ll this tim e ? Ha ve the y c om ple te ly suc c um be d to the AI winte r? Are
the y onc e a ga in lurking in the sha dows, wa iting for a nothe r da y in the sun? No one knows for sure . But if you look ha rd e nough,
m a ybe in the highe st m ounta ins, in the de e pe st jungle s, or on the lowe st ba se m e nt le ve ls of MIT , you m a y c a tc h a glim pse of a n
odd sort of c re a ture . Som e c a ll it the W indigo; othe rs re fe r to it a s a ye ti, Sa squa tc h, or rm s. But those who re a lly know think it
just m ight be — tha t it c ould only be — a L isp progra m m e r.
W he r e Doe s Lisp G e t Its P owe r ?
I’ve sa id tha t L isp is a pa rtic ula rly powe rful la ngua ge . So wha t we re the ke y insights tha t John Mc Ca rthy (a nd the othe r, la te r
innova tors of L isp) ha d tha t m a de this powe r possible ?
T o m a ke a progra m m ing la ngua ge powe rful, you ne e d to m a ke it e xpre ssive . Ha ving a n e xpre ssive la ngua ge m e a ns tha t you c a n
do a lot of stuff with ve ry little a c tua l c ode . But wha t tra its doe s a la ngua ge ne e d to m a ke this possible ? I think the re a re two tha t
a re m ost im porta nt.
One tra it is a lot of fe a ture s built into the la ngua ge . T ha t wa y, for m ost things you ne e d to ge t done , som e one ha s a lre a dy
pe rform e d som e of the work for you, a nd you c a n le ve ra ge tha t work to m a ke your own c ode look pithy. Ma ny m ode rn la ngua ge s
ha ve this tra it. T he Ja va la ngua ge , for insta nc e , is re nowne d for powe rful libra rie s tha t, for e xa m ple , le t you a c quire da ta from
a nothe r PC ove r a soc ke t with e a se .
T he se c ond tra it tha t give s a la ngua ge powe r is le tting you m uc k a round inside it a s de e ply a s possible to m a ke it do your
bidding. T ha t wa y, e ve n if the de signe rs of the la ngua ge ne ve r c onc e ive d of wha t you’re trying to do, you c a n m a ke your own
c ha nge s to the la ngua ge until it doe s e xa c tly wha t you ne e d to solve your proble m s e le ga ntly. T his tra it is m uc h m ore diffic ult to
provide in a la ngua ge . Suppose you wa nte d to a dd som e thing like ne ste d func tion de finition support to Ja va . If you know Ja va we ll,
thinking a bout how to a dd suc h support is in the re a lm of nightm a re s.
T he re a son m ost la ngua ge s a re n’t good a t supporting both of the se tra its sim ulta ne ously is tha t the y c onflic t with e a c h othe r. T he
ric he r a la ngua ge is a t the sta rt, the m ore c om plic a te d it is. And the m ore c om plic a te d the la ngua ge , the m ore pa inful it is to
m uc k with tha t la ngua ge . T ha t’s why m a king your own c ha nge s to the m ost m a ture progra m m ing la ngua ge s is c lose to im possible .
Of c ourse , if you try ha rd e nough, you c a n a lwa ys m a ke funda m e nta l c ha nge s to a ny la ngua ge . For insta nc e , whe n C++ wa s
de ve lope d, it origina lly took the form of a C pre proc e ssor. A spe c ia l C progra m wa s writte n tha t c ould ta ke c ode writte n in the ne w
C++ dia le c t a nd c onve rt it into pla in-old C, whic h you c ould the n just run through a sta nda rd C c om pile r. T his is how Bja rne
Stroustrup, the inve ntor of C++, wa s a ble to twe a k the C la ngua ge a nd a dd fe a ture s to turn it into his own. Howe ve r, writing a
tra nsla tor suc h a s this is a n e xtre m e ly diffic ult a nd te dious proc e ss tha t you would c onside r only a s a la st re sort.
In c ontra st, L isp la ngua ge s m a ke it e xtre m e ly e a sy for a n e xpe rie nc e d L ispe r to a lte r the c om pile r/inte rpre te r tha t runs a
progra m , while still supporting ric h la ngua ge fe a ture s with e xte nsive libra rie s. In fa c t, m e ssing a round with the la ngua ge within
L isp is e a sie r tha n in a ny othe r la ngua ge e ve r c re a te d!
For e xa m ple , writing a func tion in L isp to c a lc ula te the dista nc e be twe e n two points would be sim ple , a s in m ost othe r
la ngua ge s. But a n e xpe rie nc e d L ispe r would find it e qua lly e a sy to inve nt a ne w wa y to ne st func tion de finitions or de vise a funky
if-the n c om m a nd. E ve n writing your own obje c t-orie nte d progra m m ing support inside L isp is not c om plic a te d (a nd m ost L ispe rs
ha ve proba bly done so a t som e point). In L isp, e ve ryone ge ts to be a m ini-Stroustrup!

How doe s L isp m a ke this ne a t fe a t possible ? One of L isp’s c ore c ha ra c te ristic s is tha t writing a L isp dire c tly in L isp is, itse lf,
unbe lie va bly sim ple . It turns out tha t this is the k e y prope rty tha t a llows L isp to bre a k the pa ra dox of the two tra its. By sta rting out
a s a la ngua ge tha t c ould pe rform a c ool m a the m a tic a l tric k of e le ga ntly writing itse lf, it e nde d up posse ssing the ve ry prope rty
ne e de d to be both fe a ture -ric h and twe a ka ble . T ha t, in turn, m a ke s it the pe rfe c t tool for a c tua lly writing just a bout a ny kind of
progra m a t a ll!
T hink of it this wa y: Give a progra m m e r a fish c om m a nd in his progra m m ing la ngua ge , a nd he will e a t Chine se ta ke out a nd
drink Jolt for a da y. Give a progra m m e r a progra m m ing la ngua ge tha t a llows him to write his own fish c om m a nd, a nd he ’ll e a t
Chine se ta ke out a nd drink Jolt for a life tim e (whic h, a dm itte dly, would proba bly be c ut short by nutritiona l de fic ie nc ie s, a nd le t’s
not e ve n disc uss the proba ble he a rt a rrhythm ia s).
So, now you ha ve a n ide a of why L isp is a ve ry c ool a nd ve ry unusua l progra m m ing la ngua ge . It ha s a long a nd a typic a l history
c om pa re d with m ost progra m m ing la ngua ge s. Most la ngua ge s c a m e from the world of e ngine e ring, whe re a s L isp origina te d from a
m ore m a the m a tic a l ba c kground. It ha s a lot to offe r to those willing to spe nd a little tim e le a rning som e thing ne w.
P ar t I. Lisp is P owe r
Chapte r 1. G e tting Star te d with Lisp
T his c ha pte r be gins with a n introduc tion to the va rious dia le c ts of L isp. T he n we ’ll ta lk a bit a bout ANSI Com m on L isp, the
dia le c t tha t we ’ll be using in this book. Fina lly, you’ll ge t sta rte d by insta lling a nd te sting CL ISP, the im ple m e nta tion of ANSI
Com m on L isp tha t will le t you run a ll the L isp ga m e s you’re going to be c re a ting!
Lisp Diale c ts
Any la ngua ge tha t obe ys the c e ntra l princ iple s of L isp is c onside re d a L isp dia le c t. Sinc e the se princ iple s a re so sim ple , it’s not
surprising tha t lite ra lly hundre ds of dia le c ts of L isp ha ve be e n c re a te d. In fa c t, sinc e so m a ny budding L ispe rs c re a te the ir own L isp
dia le c t a s a n e xe rc ise , the re m a y be thousands of pa rtia lly c om ple te d L isps slum be ring in long-a ba ndone d dire c torie s on ha rd drive s
a c ross the pla ne t. Howe ve r, the va st m a jority of the L isp c om m unity use s two L isps: ANSI Com m on L isp (ofte n a bbre via te d CL )
a nd Sc he m e .
In this book, we ’ll be ta lking e xc lusive ly a bout the ANSI Com m on L isp dia le c t, the slightly m ore popula r of the two.
Ne ve rthe le ss, m ost of the knowle dge you’ll ga in from re a ding this book will a lso be re le va nt to Sc he m e (a lthough the na m e s of
func tions te nd to diffe r som e wha t be twe e n the dia le c ts).
A Tale of Two Lisps
Som e de e p philosophic a l diffe re nc e s e xist be twe e n ANSI Com m on L isp a nd Sc he m e , a nd the y a ppe a l to diffe re nt progra m m e r
pe rsona litie s. Onc e you le a rn m ore a bout L isp la ngua ge s, you c a n de c ide whic h dia le c t you pre fe r. T he re is no right or wrong
c hoic e .
T o a id you in your de c ision, I ha ve c re a te d the following pe rsona lity te st for you:

If you c hose A, you like ra w powe r in your la ngua ge . You don’t m ind if your la ngua ge is a bit ugly, due to a lot of pra gm a tic
c om prom ise s, a s long a s you c a n still write tight c ode . ANSI Com m on L isp is the be st la ngua ge for you! ANSI Com m on L isp tra c e s
its a nc e stry m ost dire c tly from the a nc ie nt L isp dia le c ts, built on top of m illions of progra m m e r hours, giving it inc re dibly ric h
func tiona lity. Sure , it ha s som e ba roque func tion na m e s due to c ountle ss historic a l a c c ide nts, but this L isp c a n re a lly fly in the
right ha c ke r’s ha nds.
If you c hose B, you like la ngua ge s tha t a re c le a n a nd e le ga nt. You a re m ore inte re ste d in funda m e nta l progra m m ing proble m s
a nd a re ha ppy to while a wa y on a be a utiful m e a dow, c onte m pla ting the be a uty of your c ode , oc c a siona lly writing a re se a rc h pa pe r
on the ore tic a l c om puting proble m s. Sc he m e is the la ngua ge for you! It wa s c re a te d in the m id-1970s by Guy L . Ste e le a nd Ge ra ld
Ja y Sussm a n a nd involve d som e soul-se a rc hing a bout the ide a l L isp. Code in Sc he m e te nds to be slightly m ore ve rbose , sinc e
Sc he m e rs c a re m ore a bout m a the m a tic a l purity in the ir c ode tha n c re a ting the shorte st progra m s possible .
If you c hose C, you’re som e one who wa nts it a ll: the powe r of ANSI CL a nd the m a the m a tic a l be a uty of Sc he m e . At this tim e ,
no L isp dia le c t c om ple te ly fits the bill, but tha t c ould c ha nge in the future . One la ngua ge tha t m ight work for you (a lthough it is
sa c rile ge to m a ke this c la im in a L isp book) is Ha ske ll. It is not c onside re d a L isp dia le c t, but its followe rs obe y pa ra digm s popula r
a m ong L ispe rs, suc h a s ke e ping the synta x uniform , supporting na tive lists, a nd re lying he a vily on highe r-orde r func tions. More
im porta nt, it ha s a n e xtre m e m a the m a tic a l rigor (e ve n m ore so tha n Sc he m e ) tha t a llows it to hide ve ry powe rful func tiona lity
unde r a sque a ky c le a n surfa c e . It’s e sse ntia lly a wolf in she e p’s c lothing. L ike L isp, Ha ske ll is a la ngua ge tha t a ny progra m m e r
would be ne fit from inve stiga ting furthe r.
Up-and-Coming Lisps
As just m e ntione d, the re re a lly isn’t a true L isp dia le c t a va ila ble ye t tha t posse sse s both the powe r a nd fle xibility of ANSI
Com m on L isp a nd the e le ga nc e of Sc he m e . Howe ve r, som e ne w c onte nde rs on the horiz on m a y a tta in the be st-of-both-worlds c rown
in the ne a r future .
One ne w L isp tha t is showing prom ise is Clojure , a dia le c t de ve lope d by Ric h Hic ke y. Clojure is built on the Ja va pla tform ,
a llowing it to le ve ra ge a lot of m a ture Ja va libra rie s right out of the box. Also, Clojure c onta ins som e c le ve r a nd we ll-thought-out
fe a ture s to e a se m ultithre a de d progra m m ing, whic h m a ke s it a use ful tool for progra m m ing se e m ingly ubiquitous m ultic ore CPUs.
Anothe r inte re sting c ha lle nge r is Arc . It is a true L isp la ngua ge be ing princ ipa lly de ve lope d by Pa ul Gra ha m , a we ll-known
L ispe r. Arc is still in a n e a rly sta ge of de ve lopm e nt, a nd opinion va rie s wide ly on how m uc h of a n im prove m e nt it is ove r othe r
L isps. Also, its de ve lopm e nt ha s be e n progre ssing a t a gla c ia lly slow pa c e . It will be a while be fore a nyone c a n sa y if Arc m ight be
a m e a ningful c onte nde r.
W e ’ll be dipping our toe s in som e Arc a nd Clojure in the e pilogue .
Lisp Diale c ts Use d for Sc r ipting
Som e L isp dia le c ts a re use d for sc ripting, inc luding the se :

E m a c s L isp is use d for sc ripting inside the popula r (a nd ove ra ll a we som e ) E m a c s te xt e ditor.
Guile Sc he m e is use d a s a sc ripting la ngua ge in se ve ra l ope n sourc e a pplic a tions.
Sc ript-Fu Sc he m e is use d with the GIMP im a ge e ditor.

T he se dia le c ts a re forks from olde r ve rsions of the m a in L isp bra nc he s a nd a re not typic a lly use d for c re a ting sta nd-a lone
a pplic a tions. Howe ve r, the y a re still pe rfe c tly re spe c ta ble dia le c ts of L isp.
ANSI Common Lisp
In 1981, in orde r to c ope with the diz z ying num be r of dia le c ts of the la ngua ge , m e m be rs of the va rying L isp c om m unitie s
dra fte d a spe c ific a tion for a ne w dia le c t na m e d Com m on L isp. In 1986, this la ngua ge , a fte r furthe r a djustm e nts, wa s turne d into
the ANSI Com m on L isp sta nda rd. Ma ny of the de ve lope rs of olde r ve rsions of L isp m odifie d the ir inte rpre te rs a nd c om pile rs to
c onform to this ne w sta nda rd, whic h be c a m e the m ost popula r ve rsion of L isp a nd re m a ins so to this da y.

Note

T hroughout this book, the te rm Common Lisp re fe rs to the ve rsion of Com m on L isp de fine d by the ANSI sta nda rd.

A ke y de sign goa l with Com m on L isp wa s to c re a te a multiparadigm language , m e a ning it inc lude s support for m a ny diffe re nt
style s of progra m m ing. You’ve proba bly he a rd of obje c t-orie nte d programming, whic h c a n be done quite nic e ly in Com m on L isp.
Othe r progra m m ing style s you m a y not ha ve he a rd of be fore inc lude func tional programming, ge ne ric programming, a nd domain-
spe c ific language programming. T he se a re a ll we ll supporte d within Com m on L isp. You’ll be le a rning a bout e a c h of the se style s,
a long with othe rs, a s we progre ss through this book.
G e tting Star te d with CLISP
Ma ny gre a t L isp c om pile rs a re a va ila ble , but one in pa rtic ula r is e a sie st to ge t sta rte d with: CL ISP, a n ope n sourc e Com m on
L isp. CL ISP is sim ple to insta ll a nd runs on a ny ope ra ting syste m .
Othe r popula r L isps inc lude Ste e l Ba nk Com m on L isp (SBCL ), a fa st Com m on L isp tha t’s c onside re d a bit m ore he a vy-duty tha n
CL ISP a nd a lso ope n sourc e ; Alle gro Com m on L isp, a powe rful c om m e rc ia l L isp by Fra nz , Inc ; L ispW orks; Cloz ure CL ; a nd
CMUCL . Ma c use rs m a y wa nt to c onside r L ispW orks or Cloz ure CL , whic h will be e a sie r to ge t running on the ir m a c hine s.
Howe ve r, for our purpose s, CL ISP is the be st c hoic e .

Note

Sta rting with Cha pte r 12, we ’ll be using som e CL ISP-spe c ific c om m a nds tha t a re c onside re d nonsta nda rd. Howe ve r, up until tha t
point, a ny im ple m e nta tion of Com m on L isp will work for running the e xa m ple s in this book.
Installing CLISP
You c a n downloa d a CL ISP insta lle r from http://c lisp. c ons. org/. It will run on W indows PCs, Ma c s, a nd L inux va ria nts. On a
W indows PC, you sim ply run a n insta ll progra m . On a Ma c , the re a re som e a dditiona l ste ps, whic h a re de ta ile d on the we bsite .
On a De bia n-ba se d L inux m a c hine , you should find tha t CL ISP a lre a dy e xists in your sta nda rd sourc e s. Just type apt-get install
clisp a t the c om m a nd line , a nd you’ll ha ve CL ISP insta lle d a utom a tic a lly.
For othe r L inux distributions (Fe dora , SUSE , a nd so on), you c a n use sta nda rd pa c ka ge s liste d unde r “ L inux pa c ka ge s” on the
CL ISP we bsite . And e xpe rie nc e d L inux use rs c a n c om pile CL ISP from sourc e .
Star ting Up CLISP
T o run CL ISP, type clisp from your c om m a nd line . If a ll goe s a c c ording to pla n, you’ll se e the following prom pt:
$ clisp
i i i i i i i ooooo o ooooooo ooooo ooooo
I I I I I I I 8 8 8 8 8 o 8 8
I \ `+' / I 8 8 8 8 8 8
\ `-+-' / 8 8 8 ooooo 8oooo
`-__|__-' 8 8 8 8 8
| 8 o 8 8 o 8 8
------+------ ooooo 8oooooo ooo8ooo ooooo 8
Copyright (c) Bruno Haible, Michael Stoll 1992, 1993
Copyright (c) Bruno Haible, Marcus Daniels 1994-1997
Copyright (c) Bruno Haible, Pierpaolo Bernardi, Sam Steingold 1998
Copyright (c) Bruno Haible, Sam Steingold 1999-2000
Copyright (c) Sam Steingold, Bruno Haible 2001-2006
[1]>
L ike a ll Com m on L isp e nvironm e nts, CL ISP will a utom a tic a lly pla c e you into a re ad-e v al-print loop (R E P L) a fte r you sta rt it
up. T his m e a ns you c a n im m e dia te ly sta rt typing in L isp c ode .
T ry it out by typing (+ 3 (* 2 4)). You’ll se e the re sult printe d be low the e xpre ssion:
[1]> (+ 3 (* 2 4))
11
T his shows how the RE PL works. You type in a n e xpre ssion, a nd the n the L isp will im m e dia te ly e va lua te it a nd re turn the
re sulting va lue . W he n you wa nt to shut down CL ISP, just type (quit).
Now tha t you ha ve CL ISP working on your c om pute r, you’re re a dy to write a L isp ga m e !
W hat You've Le ar ne d
In this c ha pte r, we disc usse d the diffe re nt dia le c ts of L isp a nd insta lling CL ISP. You le a rne d the following a long the wa y:
T he re a re two m a in dia le c ts of L isp: Com m on L isp a nd Sc he m e . Both ha ve a lot to offe r, but we ’ll foc us on Com m on L isp in
this book.
Com m on L isp is a m ultipa ra digm la ngua ge , m e a ning tha t it supports m a ny diffe re nt progra m m ing style s.
CL ISP is a Com m on L isp im ple m e nta tion tha t is e a sy to se t up, m a king it a gre a t c hoic e for a L isp novic e .
You c a n type in L isp c om m a nds dire c tly from the CL ISP R E P L.
Chapte r 2. Cr e ating Your F ir st Lisp P r ogr am
Now tha t we ’ve disc usse d som e of the philosophy of L isp a nd ha ve a running CL ISP e nvironm e nt, we ’re re a dy to write som e
a c tua l L isp c ode in the form of a sim ple ga m e .
The G ue ss-M y-Numbe r G ame
T his first ga m e we ’ll write is pre tty m uc h the sim ple st ga m e im a gina ble . It’s the c la ssic gue ss-m y-num be r ga m e .
In this ga m e , you pic k a num be r from 1 to 100, a nd the c om pute r ha s to gue ss it.
T he following shows wha t ga m e pla y m ight look like if you pic k the num be r 23. T he c om pute r sta rts by gue ssing 50, a nd with
e a c h suc c e ssive gue ss, you e nte r (smaller) or (bigger) until the c om pute r gue sse s your num be r.
> (guess-my-number)
50
> (smaller)
25
> (smaller)
12
> (bigger)
18
> (bigger)
21
> (bigger)
23
T o c re a te this ga m e , we ne e d to write thre e func tions: guess-my-number, smaller, a nd bigger. T he pla ye r sim ply c a lls the se
func tions from the RE PL . As you sa w in the pre vious c ha pte r, whe n you sta rt CL ISP (or a ny othe r L isp), you a re pre se nte d with the
RE PL , from whic h the c om m a nds you type will be re ad, the n e v aluate d, a nd fina lly printe d. In this c a se , we ’re running the
c om m a nds guess-my-number, smaller, a nd bigger.
T o c a ll a func tion in L isp, you put pa re nthe se s a round it, a long with a ny pa ra m e te rs you wish to give the func tion. Sinc e the se
pa rtic ula r func tions don’t re quire a ny pa ra m e te rs, we sim ply surround the ir na m e s in pa re nthe se s whe n we e nte r the m .
L e t’s think a bout the stra te gy be hind this sim ple ga m e . Afte r a little thought, we c om e up with the following ste ps:

1. De te rm ine the uppe r a nd lowe r (big a nd sm a ll) lim it of the pla ye r’s num be r. Sinc e the ra nge is be twe e n 1 a nd
100, the sm a lle st possible num be r would be 1 a nd the bigge st would be 100.
2. Gue ss a num be r in be twe e n the se two num be rs.
3. If the pla ye r sa ys the num be r is sm a lle r, lowe r the big lim it.
4. If the pla ye r sa ys the num be r is bigge r, ra ise the sm a ll lim it.

By following the se sim ple ste ps, c utting the ra nge of possible num be rs in ha lf with e ve ry gue ss, the c om pute r c a n quic kly hone in
on the pla ye r’s num be r.
T his type of se a rc h is c a lle d a binary se arc h. As you m a y know, bina ry se a rc he s like this a re use d a ll the tim e in c om pute r
progra m m ing. You c ould follow the se sa m e ste ps, for insta nc e , to e ffic ie ntly find a spe c ific num be r give n a sorte d ta ble of va lue s.
In tha t c a se , you would sim ply tra c k the sm a lle st a nd la rge st row in tha t ta ble , a nd the n quic kly hone in on the c orre c t row in a n
a na logous m a nne r.
De fining G lobal Var iable s in Lisp
As the pla ye r c a lls the func tions tha t m a ke up our ga m e , the progra m will ne e d to tra c k the sm a ll a nd big lim its. In orde r to do
this, we ’ll ne e d to c re a te two globa l va ria ble s c a lle d *small* a nd *big*.
De fining the small and big Var iable s
A va ria ble tha t is de fine d globa lly in L isp is c a lle d a top-le v e l de finition. W e c a n c re a te ne w top-le ve l de finitions with the
defparameter func tion:
> (defparameter *small* 1)
*SMALL*
> (defparameter *big* 100)
*BIG*
T he func tion na m e defparameter is a bit c onfusing, sinc e it doe sn’t re a lly ha ve a nything to do with pa ra m e te rs. W ha t it doe s is
le t you de fine a global v ariable .
T he first a rgum e nt we se nd to defparameter is the na m e of the ne w va ria ble . T he a ste risks surrounding the na m e s *big* a nd
*small*— a ffe c tiona te ly c a lle d e armuffs— a re c om ple te ly a rbitra ry a nd optiona l. L isp se e s the a ste risks a s pa rt of the va ria ble
na m e s a nd ignore s the m . L ispe rs like to m a rk a ll the ir globa l va ria ble s in this wa y a s a c onve ntion, to m a ke the m e a sy to
distinguish from loc a l va ria ble s, whic h a re disc usse d la te r in this c ha pte r.

Note

Although e a rm uffs m a y be “ optiona l” in a stric tly te c hnic a l se nse , I sugge st tha t you use the m . I c a nnot vouc h for your sa fe ty if
you e ve r post a ny c ode to a Com m on L isp ne wsgroup a nd your globa l va ria ble s a re m issing the ir e a rm uffs.
An Alte r native G lobal Var iable De finition F unc tion
W he n you se t the va lue of a globa l va ria ble using defparameter, a ny va lue pre viously store d in the va ria ble will be ove rwritte n:
> (defparameter *foo* 5)
FOO
> *foo*
5
> (defparameter *foo* 6)
FOO
> *foo*
6
As you c a n se e , whe n we re de fine the va ria ble *foo*, its va lue c ha nge s.
Anothe r c om m a nd tha t you c a n use for de c la ring globa l va ria ble s, c a lle d defvar, won’t ove rwrite pre vious va lue s of a globa l
va ria ble :
> (defvar *foo* 5)
FOO
> *foo*
5
> (defvar *foo* 6)
FOO
> *foo*
5
Som e L ispe rs pre fe r to use defvar inste a d of defparameter whe n de fining globa l va ria ble s. In this book, howe ve r, we ’ll be using
defparameter e xc lusive ly.

Note

W he n you re a d a bout L isp in othe r pla c e s, you m a y a lso se e progra m m e rs using the te rm dy namic v ariable or spe c ial v ariable
whe n re fe rring to a globa l va ria ble in Com m on L isp. T his is be c a use globa l va ria ble s in Com m on L isp ha ve som e spe c ia l a bilitie s,
whic h we ’ll be disc ussing in future c ha pte rs.
Basic Lisp Etique tte
T he wa y c om m a nds a re c a lle d a nd the wa y c ode is form a tte d in L isp is som e wha t stra nge c om pa re d with othe r la ngua ge s. First
of a ll, you ne e d to surround the c om m a nd (a nd its a rgum e nts) with pa re nthe se s, a s with the defparameter func tion:
> (defparameter *small* 1)
*SMALL*
W ithout the pa re nthe se s, a c om m a nd will not be c a lle d.
Also, spa c e s a nd line bre a ks a re c om ple te ly ignore d whe n L isp re a ds in your c ode . T ha t m e a ns you c ould c a ll this c om m a nd in
a ny c ra z y wa y, with the sa m e re sult:
> ( defparameter
*small* 1)
*SMALL*
Be c a use L isp c ode c a n be form a tte d in suc h fle xible wa ys, L ispe rs ha ve a lot of c onve ntions for form a tting c om m a nds, inc luding
whe n to use m ultiple line s a nd inde nta tion. W e ’ll loose ly follow som e of the c om m on inde nta tion c onve ntions in the c ode
e xa m ple s in this book. Howe ve r, we ’re m ore inte re ste d in writing ga m e s tha n in disc ussing sourc e c ode inde nta tion rule s, so we ’re
not going to be spe nding too m uc h tim e on c ode la yout rule s in this book.
De fining G lobal F unc tions in Lisp
Our gue ss-m y-num be r ga m e ha s the c om pute r re spond to the pla ye r’s re que st to sta rt the ga m e , a nd the n to re que sts for e ithe r
sm a lle r or bigge r gue sse s. For the se , we ne e d to de fine thre e globa l func tions: guess-my-number, smaller, a nd bigger. W e ’ll a lso
de fine a func tion to sta rt ove r with a diffe re nt num be r, c a lle d start-over. In Com m on L isp, func tions a re de fine d with defun, like
this:
(defun function_name (arguments)
...)
First, we spe c ify the na m e a nd a rgum e nts for the func tion. T he n we follow it up with the c ode tha t c om pose s the func tion’s
logic .
De fining the gue ss-my-numbe r F unc tion
T he first func tion we ’ll de fine is guess-my-number. T his func tion use s the va lue s of the *big* a nd *small* va ria ble s to ge ne ra te
a gue ss of the pla ye r’s num be r. T he de finition looks like this:
> (defun guess-my-number ()

(ash (+ *small* *big*) −1))

GUESS-MY-NUMBER
T he e m pty pa re nthe se s, (), a fte r the func tion na m e guess-my-number indic a te tha t this func tion doe sn’t re quire a ny pa ra m e te rs.
Although you don’t ne e d to worry a bout inde nta tion or line bre a ks whe n e nte ring c ode snippe ts a t the RE PL , you m ust be sure to
pla c e pa re nthe se s c orre c tly. If you forge t a pa re nthe sis or put one in the wrong pla c e , you’ll m ost like ly ge t a n e rror.
W he ne ve r we run a pie c e of c ode like this in the RE PL , the re sulting va lue of the e nte re d e xpre ssion will be printe d. E ve ry
c om m a nd in Com m on L isp ge ne ra te s a re turn va lue . T he defun c om m a nd, for insta nc e , sim ply re turns the na m e of the ne wly

c re a te d func tion. T his is why we se e the na m e of the func tion pa rrote d ba c k to us in the RE PL a fte r we c a ll defun .
W ha t doe s this func tion do? As disc usse d e a rlie r, the c om pute r’s be st gue ss in this ga m e will be a num be r in be twe e n the two
lim its. T o a c c om plish this, we c hoose the a ve ra ge of the two lim its. Howe ve r, if the a ve ra ge num be r e nds up be ing a fra c tion,
we ’ll wa nt to use a ne a r-a ve ra ge num be r, sinc e we ’re gue ssing only whole num be rs.
W e im ple m e nt this in the guess-my-number func tion by first a dding the num be rs tha t re pre se nt the high a nd low lim its, the n
using the a rithm e tic shift func tion, ash, to ha lve the sum of the lim its a nd shorte n the re sult. T he c ode (+ *small* *big*) a dds

toge the r those two va ria ble s. Be c a use the a ddition ha ppe ns within a nothe r func tion c a ll, , the re sulting sum is the n pa sse d to
the ash func tion.
T he pa re nthe se s surrounding the ash func tion a nd the a ddition (+) func tion a re m a nda tory in L isp. T he se pa re nthe se s a re wha t
te ll L isp, “ I wa nt you to c a ll this func tion. ”
T he built-in L isp func tion ash looks a t a num be r in bina ry form , a nd the n shifts its bina ry bits to the le ft or right, dropping a ny
bits lost in the proc e ss. For e xa m ple , the num be r 11 writte n in bina ry is 1011. W e c a n m ove the bits in this num be r to the le ft with
ash by using 1 a s the se c ond a rgum e nt:
> (ash 11 1)
22
T his produc e s 22, whic h is 10110 in bina ry. W e c a n m ove the bits to the right (a nd lop off the bit on the e nd) by pa ssing in −1 a s
the se c ond a rgum e nt:
> (ash 11 −1)
5
T his produc e s 5, whic h is 101 in bina ry.
By using the ash func tion in guess-my-number, we a re c ontinua lly ha lving our se a rc h spa c e of possible num be rs to quic kly
na rrow down to the fina l c orre c t num be r. As a lre a dy m e ntione d, this ha lving proc e ss is c a lle d a binary se arc h, a use ful te c hnique in
c om pute r progra m m ing. T he a sh func tion is c om m only use d for suc h bina ry se a rc he s in L isp.
L e t’s se e wha t ha ppe ns whe n we c a ll our ne w func tion:
> (guess-my-number)
50
Sinc e this is our first gue ss, the output we se e whe n c a lling this func tion te lls us tha t e ve rything is working a s pla nne d: T he
progra m pic ke d the num be r 50, right in be twe e n 1 a nd 100.
W he n progra m m ing in L isp, you’ll write m a ny func tions tha t won’t e xplic itly print va lue s on the sc re e n. Inste a d, the y’ll sim ply
re turn the va lue c a lc ula te d in the body of the func tion. For insta nc e , le t’s sa y we wa nte d a func tion tha t just re turns the num be r 5.
He re ’s how we c ould write this:
> (defun return-five ()

(+ 2 3))

Be c a use the va lue c a lc ula te d in the body of the func tion e va lua te s to 5, c a lling (return-five) will just re turn 5.
T his is how guess-my-number is de signe d. W e se e this c a lc ula te d re sult on the sc re e n (the num be r 50) not be c a use the func tion
c a use s the num be r to displa y, but be c a use this is a fe a ture of the RE PL .

Note

If you’ve use d othe r progra m m ing la ngua ge s be fore , you m a y re m e m be r ha ving to write som e thing like return... to c a use a
va lue to be re turne d. In L isp, this is not ne c e ssa ry. T he fina l va lue c a lc ula te d in the body of the func tion is re turne d
a utom a tic a lly.
De fining the smalle r and bigge r F unc tions
Now we ’ll write our smaller a nd bigger func tions. L ike guess-my-number, the se a re globa l func tions de fine d with defun:

> (defun smaller ()

(setf *big* (1- (guess-my-number)))

(guess-my-number))
SMALLER
> (defun bigger ()

(setf *small* (1+ (guess-my-number)))


(guess-my-number))
BIGGER
First, we use defun to sta rt the de finition of a ne w globa l func tion smaller. Be c a use this func tion ta ke s no pa ra m e te rs, the

pa re nthe se s a re e m pty .

Ne xt, we use the setf func tion to c ha nge the va lue of our globa l va ria ble *big* . Sinc e we know the num be r m ust be
sm a lle r tha n the la st gue ss, the bigge st it c a n now be is one le ss tha n tha t gue ss. T he c ode (1- (guess-my-number)) c a lc ula te s this:
It first c a lls our guess-my-number func tion to ge t the m ost re c e nt gue ss, a nd the n it use s the func tion 1-, whic h subtra c ts 1 from the
re sult.
Fina lly, we wa nt our smaller func tion to show us a ne w gue ss. W e do this by putting a c a ll to guess-my-number a s the fina l line

in the func tion body . T his tim e , guess-my-number will use the upda te d va lue of *big*, c a using it to c a lc ula te the ne xt
gue ss. T he fina l va lue of our func tion will be re turne d a utom a tic a lly, c a using our ne w gue ss (ge ne ra te d by guess-my-number) to be
re turne d by the smaller func tion.
T he bigger func tion works in e xa c tly the sa m e m a nne r, e xc e pt tha t it ra ise s the *small* va lue inste a d. Afte r a ll, if you c a ll the
bigger func tion, you a re sa ying your num be r is bigge r tha n the pre vious gue ss, so the sm a lle st it c a n now be (whic h is wha t the
*small* va ria ble re pre se nts) is one more tha n the pre vious gue ss. T he func tion 1+ sim ply a dds 1 to the va lue re turne d by guess-my-

number .
He re we se e our func tions in a c tion, with the num be r 56 a s our gue ss:
> (bigger)
75
> (smaller)
62
> (smaller)
56
De fining the star t-ove r F unc tion
T o c om ple te our ga m e , we ’ll a dd the func tion start-over to re se t our globa l va ria ble s:
(defun start-over ()
(defparameter *small* 1)
(defparameter *big* 100)
(guess-my-number))
As you c a n se e , the start-over func tion re se ts the va lue s of *small* a nd *big* a nd the n c a lls guess-my-number a ga in to re turn
a ne w sta rting gue ss. W he ne ve r you wa nt to sta rt a bra nd-ne w ga m e with a diffe re nt num be r, you c a n c a ll this func tion to re se t the
ga m e .
De fining Loc al Var iable s in Lisp
For our sim ple ga m e , we ’ve de fine d globa l va ria ble s a nd func tions. Howe ve r, in m ost c a se s you’ll wa nt to lim it your de finitions
to a single func tion or a bloc k of c ode . T he se a re c a lle d loc al va ria ble s a nd func tions.
T o de fine a loc a l va ria ble , use the c om m a nd let. A let c om m a nd ha s the following struc ture :

(let (variable declarations)

...body...)

T he first thing inside the let c om m a nd is a list of va ria ble de c la ra tions . T his is whe re we c a n de c la re one or m ore loc a l

va ria ble s. T he n, in the body of the c om m a nd (a nd only within this body), we c a n use the se va ria ble s . He re is a n e xa m ple of
the let c om m a nd:

> (let ((a 5)

(b 6))

(+ a b))
11

In this e xa m ple , we ’ve de c la re d the va lue s 5 a nd 6 for the va ria ble s a a nd b , re spe c tive ly. T he se a re our va ria ble

de c la ra tions. T he n, in the body of the let c om m a nd, we a dde d the m toge the r , re sulting in the displa ye d va lue of 11.
W he n using a le t e xpre ssion, you m ust surround the e ntire list of de c la re d va ria ble s with pa re nthe se s. Also, you m ust surround
e a c h pa ir of va ria ble na m e s a nd initia l va ria ble s with a nothe r se t of pa re nthe se s.

Note

Although the inde nta tion a nd line bre a ks a re c om ple te ly a rbitra ry, be c a use the na m e s of the va ria ble s a nd the ir va lue s in a let
e xpre ssion form a kind of sim ple ta ble , c om m on pra c tic e is to a lign the de c la re d va ria ble s ve rtic a lly. T his is why the b is pla c e d
dire c tly unde rne a th the a in the pre c e ding e xa m ple .
De fining Loc al F unc tions in Lisp
W e de fine loc a l func tions using the flet c om m a nd. T he flet c om m a nd ha s the following struc ture :

(flet ((function_name (arguments)

...function body...))

...body...)

At the top of the flet, we de c la re a func tion (in the first two line s). T his func tion will the n be a va ila ble to us in the body

. A func tion de c la ra tion c onsists of a func tion na m e , the a rgum e nts to tha t func tion , a nd the func tion body , whe re we
put the func tion’s c ode .
He re is a n e xa m ple :

> (flet ((f (n)

(+ n 10)))

(f 5))
15

In this e xa m ple , we de fine a single func tion, f, whic h ta ke s a single a rgum e nt, n . T he func tion f the n a dds 10 to this

va ria ble n , whic h ha s be e n pa sse d in it. T he n we c a ll this func tion with the num be r 5 a s the a rgum e nt, c a using the va lue 15

to be re turne d .
As with let, you c a n de fine one or m ore func tions within the sc ope of the flet.
A single flet c om m a nd c a n be use d to de c la re m ultiple loc a l func tions a t onc e . Sim ply a dd m ultiple func tion de c la ra tions in
the first pa rt of the c om m a nd:

> (flet ((f (n)


(+ n 10))

(g (n)
(- n 3)))
(g (f 5)))
12

He re , we ha ve de c la re d two func tions: one na m e d f a nd one na m e d g . In the body of the flet, we c a n the n re fe r to
both func tions. In this e xa m ple , the body first c a lls f with 5 to yie ld 15, the n c a lls g to subtra c t 3, le a ding to 12 a s a fina l re sult.
T o m a ke func tion na m e s a va ila ble in de fine d func tions, we c a n use the labels c om m a nd. It’s ide ntic a l in its ba sic struc ture to
the flet c om m a nd. He re ’s a n e xa m ple :

> (labels ((a (n)


(+ n 5))

(b (n)

(+ (a n) 6)))

(b 10))
21

In this e xa m ple , the loc a l func tion a a dds 5 to a num be r . Ne xt, the func tion b is de c la re d . It c a lls the func tion a,

a nd the n a dds 6 to the re sult . Fina lly, the func tion b is c a lle d with the va lue 10 . Sinc e 10 plus 6 plus 5 e qua ls 21, the
num be r 21 be c om e s the fina l va lue of the e ntire e xpre ssion. T he spe c ia l ste p tha t re quire s us to use labels inste a d of flet is whe re

the func tion b c a lls the func tion a . If we ha d use d flet, the func tion b would not ha ve “ known” a bout the func tion a.
T he labels c om m a nd le ts you c a ll one loc a l func tion from a nothe r, a nd it a llows you to ha ve a func tion c a ll itse lf. T his is
c om m only done in L isp c ode a nd is c a lle d re c ursion. (You will se e m a ny e xa m ple s of re c ursion in future c ha pte rs. )
W hat You've Le ar ne d
In this c ha pte r, we disc usse d the ba sic Com m on L isp c om m a nds for de fining va ria ble s a nd func tions. Along the wa y, you le a rne d
the following:
T o de fine a globa l va ria ble , use the defparameter c om m a nd.
T o de fine a globa l func tion, use the defun c om m a nd.
Use the let a nd flet c om m a nds to de fine loc a l va ria ble s a nd func tions, re spe c tive ly.
T he func tion labels is like flet, but it le ts func tions c a ll the m se lve s. Func tions tha t c a ll the m se lve s a re c a lle d re c ursiv e
func tions.
Chapte r 3. Explor ing the Syntax of Lisp Code
As you’ve le a rne d so fa r, L isp c om m a nds m ust be e nte re d in a ra the r unorthodox wa y, with pa re nthe se s surrounding e a c h
c om m a nd. In this c ha pte r, we ’ll e xplore why L isp works this wa y.
T o unde rsta nd why a ny la ngua ge — whe the r it’s a progra m m ing la ngua ge or a hum a n la ngua ge — looks a c e rta in wa y, we ne e d to
be gin with two c onc e pts from the fie ld of linguistic s: synta x a nd se m a ntic s.
Syntax and Se mantic s
He re is a typic a l se nte nc e in the E nglish la ngua ge :

My dog ate my homework.


T his se nte nc e ha s the c orre c t synta x for a se nte nc e in E nglish. T he sy ntax of a pie c e of te xt re pre se nts the ba sic rule s tha t it
ne e ds to follow to be a va lid se nte nc e . He re a re som e of the rule s of se nte nc e s in the E nglish la ngua ge tha t this te xt obe ys:

T he se nte nc e e nds in a punc tua tion m a rk.


T he se nte nc e c onta ins a subje c t a nd a ve rb.
T he se nte nc e is m a de up of le tte rs in the E nglish a lpha be t (a s oppose d to E gyptia n hie roglyphic s or Sum e ria n
c une iform ).

Howe ve r, the re is m ore to a se nte nc e tha n just its synta x. W e a lso c a re a bout wha t the se nte nc e a c tua lly m e a ns. W he n we ta lk
a bout the se mantic s of a se nte nc e , we ’re re fe rring to its m e a ning.
For insta nc e , he re a re som e se nte nc e s tha t a ll roughly ha ve ide ntic a l se m a ntic s:

My dog ate my homework.


The canine, which I possess, has consumed my school
assignment.
Der Hund hat meine Hausarbeit gefressen.
T he first two a re just diffe re nt wa ys of sa ying the sa m e thing in E nglish. T he third se nte nc e is in Ge rm a n, but it still ha s the
sa m e m e a ning a nd, he nc e , se m a ntic s a s the first two.
T he sa m e distinc tion be twe e n the se two ide a s e xists in progra m m ing la ngua ge s. For insta nc e , he re is a va lid line of c ode writte n
in C++:
((foo<bar>)*(g++)).baz(!&qux::zip->ding());
T his line of c ode obe ys the rule s of C++ synta x. T o m a ke m y point, I put in a lot of we ird synta x tha t is unique to C++, whic h
diffe re ntia te s it from othe r la ngua ge s. If you we re to pla c e this line of c ode in a progra m of a nothe r progra m m ing la ngua ge with a
diffe re nt synta x, it would proba bly be inva lid a nd c a use a n e rror.
Of c ourse , this C++ progra m m ing c ode a lso m e a ns som e thing. If we we re to put this line of c ode in a C++ progra m (in the prope r
c onte xt), it would c a use your c om pute r to do som e thing. T he a c tions tha t a progra m pe rform s a re the se mantic s of the progra m . It
is usua lly possible to write a progra m tha t ha s the sa m e se m a ntic s in diffe re nt progra m m ing la ngua ge s; tha t is, the progra m will do
the sa m e thing in both la ngua ge s.
W ha t a ll this boils down to is tha t m ost progra m m ing la ngua ge s ha ve sim ila r se m a ntic powe rs. Howe ve r, ba sic L isp c ode is
diffe re nt from c ode in a ny othe r m a jor progra m m ing la ngua ge in tha t it ha s a fa r sim ple r synta x. Hav ing a simple sy ntax is a
de fining fe ature of the Lisp language .
The Building Bloc ks of Lisp Syntax
From the c ra z y line of C++ c ode in the pre vious se c tion, you c a n ge t the ide a tha t C++ ha s a lot of we ird synta x— for indic a ting
na m e spa c e s, de re fe re nc ing pointe rs, pe rform ing c a sts, re fe re nc ing m e m be r func tions, pe rform ing Boole a n ope ra tions, a nd so on.
If you we re to write a C++ c om pile r, you would ne e d to do a lot of ha rd work so tha t the c om pile r c ould re a d this c ode a nd obe y
the m a ny C++ synta x rule s, be fore you c ould m a ke a ny se nse of the c ode .
W riting a L isp c om pile r or inte rpre te r is m uc h e a sie r. T he pa rt of a L isp c om pile r or inte rpre te r tha t re a ds in the c ode (whic h
L ispe rs a c tua lly c a ll the re ade r) is sim ple r tha n in C++ or a ny othe r m a jor progra m m ing la ngua ge . T a ke a ra ndom pie c e of L isp
c ode :
(defun square (n)
(* n n))
T his func tion de c la ra tion, whic h c re a te s a func tion tha t sim ply squa re s a num be r, c onsists of nothing m ore tha n pa re nthe se s a nd
sym bols. In fa c t, you c a n vie w it a s just a bunc h of ne ste d lists, de lim ite d by pa re nthe se s.
L isp only ha s one wa y of orga niz ing bits of c ode : It use s pa re nthe se s to orga niz e the c ode into lists.
All ba sic L isp c ode use s this sim ple list-like synta x:

But wha t sorts of things c a n we put into the se lists? W e ll, be side s othe r lists, we c a n a lso put sym bols, num be rs, a nd strings into
our c ode . He re , we ’ll look a t the se ba sic building bloc ks, or da ta type s, you’ll use in L isp. (W e ’ll disc uss m a ny othe r Com m on L isp
da ta type s in la te r c ha pte rs. )
Symbols
Sy mbols a re a funda m e nta l type of da ta in L isp a nd a re use d e xte nsive ly. A sym bol in L isp is a sta nd-a lone word. Com m on L isp
sym bols a re typic a lly m a de up of le tte rs, num be rs, a nd c ha ra c te rs like + - / * = < > ? ! _. Som e e xa m ple s of va lid L isp
sym bols a re foo, ice9, my-killer-app27, a nd e ve n —<<==>>—.
Sym bols in Com m on L isp a re c ase -inse nsitiv e (a lthough m ost L ispe rs a void using uppe rc a se ). T o illustra te this, we ’ll use a
func tion c a lle d eq, whic h le ts us se e if two sym bols a re ide ntic a l:
> (eq 'fooo 'FoOo)
T
As you c a n se e , this func tion re turne d T, whic h te lls us tha t L isp c onside rs the se two sym bols to be ide ntic a l. (For now, ignore the
quota tion m a rk in front of the sym bols. T his will be e xpla ine d shortly, whe n we disc uss data mode . )
Numbe r s
L isp supports both floa ting-point num be rs a nd inte ge rs. W he n you write a num be r, the pre se nc e of a de c im a l point de te rm ine s
whe the r your num be r is se e n a s a floa ting-point num be r or a n inte ge r. T he num be rs 1 a nd 1. 0 a re two diffe re nt e ntitie s in Com m on
L isp.
For insta nc e , if you use m ost m a th func tions with both a n inte ge r a nd a floa ting-point num be r, the inte ge r will be c om e
“ poisone d, ” a nd a floa ting-point num be r will be re turne d. He re ’s a c a se in point:
> (+ 1 1.0)
2.0
Note tha t the de c im a l point in the re turne d num be r, 2.0, indic a te s tha t it is a floa ting-point num be r.
L isp c a n pe rform som e a m a z ing fe a ts with num be rs, e spe c ia lly whe n c om pa re d with m ost othe r la ngua ge s. For insta nc e , he re
we ’re using the func tion expt to c a lc ula te the fifty-third powe r of 53:
> (expt 53 53)
2435684816502271213247760652010472551853345312868564084450513087957
6720609150223301256150373
Isn’t tha t c ool? Most la ngua ge s would c hoke on a c a lc ula tion involving suc h a la rge num be r.
Fina lly, you should know tha t som e thing we ird c ould ha ppe n if you divide two inte ge rs:
> (/ 4 6)
2/3
T he division func tion is dividing the 4 by 6. But inste a d of re turning a fra c tion (0. 66666. . . ) a s you m ight e xpe c t, it re turns a
rational numbe r, re pre se nte d a s two inte ge rs with a division sym bol be twe e n the m . So the 2/3 re sult re pre se nts a single ra tiona l
num be r, whic h is the m a the m a tic a lly ide a l wa y to e nc ode a fra c tion suc h a s this.
Note tha t we ge t a diffe re nt a nswe r if the re is a floa ting-point num be r in our c a lc ula tion:
> (/ 4.0 6)
0.6666667
As in the pre vious e xa m ple , the num be r with the de c im a l point (4.0) ha s poisone d our num be rs to give us a fra c tion a s a re sult.
If you’re not a m a th ge e k, this m ight not be of m uc h use to you, but a t le a st you now know wha t’s ha ppe ning if you se e this sort
of thing while you’re c oding. You c a n a lso re st a ssure d tha t L isp will do the right thing with this num be r whe n you use it la te r on in
a nothe r c a lc ula tion. L isp is sm a rt.
Str ings
T he la st ba sic building bloc k in L isp is the string. Although strings a re n’t re a lly tha t funda m e nta l to L isp from a the ore tic a l
sta ndpoint, a ny progra m tha t c om m unic a te s with a hum a n will usua lly ne e d strings, be c a use hum a ns like to c om m unic a te with
te xt.
T o indic a te a string in L isp, surround c ha ra c te rs with double quote s. For e xa m ple , "Tutti Frutti" is a va lid string.
W e c a n displa y a string using a func tion c a lle d princ:
> (princ "Tutti Frutti")

Tutti Frutti

"Tutti Frutti"
[1]
Notic e tha t printing our te xt a t the RE PL will c a use the te xt to a ppe a r twic e . First, we se e the a c tua l printing c a use d by the

princ c om m a nd . Howe ve r, sinc e the RE PL will a lwa ys show the re sult of e va lua ting the e nte re d e xpre ssion, we se e our

string pa rrote d ba c k to us . T his is be c a use the princ func tion a lso re turns a va lue , whic h ha ppe ns to be the sourc e string.
A string c a n a lso c onta in so-c a lle d e sc ape d c harac te rs. If you wa nt a string to inc lude double quote s or a ba c ksla sh, you’ll ne e d
to pre fix the se c ha ra c te rs with a ba c ksla sh. For e xa m ple , this string ha s two e sc a pe d quote s:
> (princ "He yelled \"Stop that thief!\" from the busy street.")
He yelled "Stop that thief!" from the busy street.
As you c a n se e , the ba c ksla she s in front of the two quote s te ll L isp tha t the se a re lite ra l quota tion m a rks in the string, shown in
the displa ye d string just like a ny othe r c ha ra c te r.

[1] As disc usse d in Cha pte r 2, in a re a d-e va l-print loop (or RE PL ), the func tions we e nte r will be re a d, the n e va lua te d, a nd
fina lly printe d.
H ow Lisp Distinguishe s Be twe e n Code and Data
W he n we write our L isp progra m , how doe s L isp de c ide whic h pa rts of our progra m c onsist of c ode (stuff to be e xe c ute d) a nd
whic h pa rts a re just da ta ? T he synta x of L isp ha s a spe c ia l wa y of distinguishing be twe e n the two.
Com m on L isp use s two m ode s whe n it re a ds your c ode : a c ode mode a nd a data mode . You c a n switc h be twe e n the se two m ode s
whe n writing L isp c ode .
Code M ode
W he ne ve r you type som e thing into the L isp RE PL , the c om pile r a ssum e s tha t you’re e nte ring a c om m a nd you wa nt to e xe c ute .
In othe r words, L isp a lwa ys a ssum e s tha t you’re writing c ode a nd de fa ults to c ode m ode .
As we ’ve a lre a dy disc usse d, L isp will e xpe c t L isp c ode to be e nte re d a s a list. Howe ve r, the c ode should be in a spe c ia l type of
list: a form. So whe n you’re in c ode m ode , a s you a re whe n you sta rt typing into the RE PL , the c om m a nds you e nte r ne e d to be
struc ture d a s form s:

A form is sim ply a list with a spe c ia l c om m a nd a t the be ginning— typic a lly the na m e of a func tion.
W he n re a ding a form , L isp se nds a ll othe r ite m s in the list to the func tion a s pa ra m e te rs. For e xa m ple , e nte r the following into
your RE PL :
> (expt 2 3)
8
T his c a lc ula te s 2^3 = 8. It doe s this by c a lling expt, whic h c om pute s a n e xpone nt. T his c om m a nd wa s e nte re d in the sta nda rd
wa y for L isp: a s a form with the func tion na m e a t the be ginning.
W he n L isp re a ds the te xt for the pa ra m e te rs of suc h a c om m a nd, it usua lly a ssum e s tha t the se pa ra m e te rs a re also in c ode m ode .
He re ’s a n e xa m ple :
> (expt 2 (+ 3 4))
128
T his e xa m ple ha s two ne ste d form s. L isp first looks a t the e ntire e xpre ssion in c ode m ode . It de te rm ine s tha t we ’ve e nte re d a
form for the expt c om m a nd. T he n L isp looks a t the a rgum e nts to this c om m a nd, a lso in c ode m ode . One of the se a rgum e nts (+ 3
4) is a form in its own right. T his form is the n e xe c ute d, yie lding 7. Afte rwa rd, this re sult is pa sse d to the oute r expt form , whic h
is the n e xe c ute d.
Data M ode
As you m ight im a gine , a ny stuff writte n in da ta m ode is tre a te d a s da ta . T his m e a ns the c om pute r will not try to “ e xe c ute ” it,
whic h a llows us to ha ve inform a tion in our c ode tha t’s just pla in old da ta .
L e t’s ta ke a look a t da ta m ode in a c tion. W e ’ll e nte r the sa m e form tha t we e nte re d in c ode m ode in the pre vious e xa m ple ,
with one diffe re nc e :
> '(expt 2 3)
(expt 2 3)
T his tim e , we put a single quote in front of the list. Inste a d of re sponding with the sum of the num be rs 1 a nd 2, L isp sim ply
pa rrots our e xpre ssion to us. T he single quote te lls L isp to tre a t the subse que nt form a s a c hunk of da ta — sim ply a list of ite m s. L isp
the n prints the re sult of e va lua ting wha t we e nte re d, whic h is the list itse lf. It ignore s a ny func tions or va ria ble s in our list, tre a ting
e ve rything a s da ta .
Pla c ing a quote in front of lists so tha t the y won’t be e va lua te d a s a c om m a nd is c a lle d quoting. By using quoting, you c a n te ll
L isp, “ T his ne xt pa rt isn’t a c om m a nd. It’s just a c hunk of da ta for m y progra m . ”
Lists in Lisp
L ists a re a c ruc ia l fe a ture in L isp. T he y a re wha t hold a ll your L isp c ode (a s we ll a s da ta ) toge the r. T a ke a ny ba sic pie c e of
L isp c ode , suc h a s the following:
(expt 2 3)
T his pie c e of c ode c onta ins a sym bol (expt) a nd two num be rs, a ll tie d toge the r a s a list, indic a te d by the pa re nthe se s.

You c a n think of a L isp progra m a s a house . If you we re to build a house in L isp, your wa lls would be m a de out of lists. T he
bric ks would be m a de out of sym bols, num be rs, a nd strings. Howe ve r, a wa ll ne e ds m orta r to hold it toge the r. In the sa m e wa y, lists
in L isp a re he ld toge the r by struc ture s c a lle d c ons c e lls.
Cons Ce lls
L ists in L isp a re he ld toge the r with c ons c e lls. Unde rsta nding the re la tionship be twe e n c ons c e lls a nd lists will give you a be tte r
ide a of how L isp works.
A c ons c e ll looks like this:

It’s m a de of two little c onne c te d boxe s, both of whic h c a n point a t othe r things. A c ons c e ll c a n point to a nothe r c ons c e ll or
a nothe r type of L isp da ta . By be ing a ble to point to two diffe re nt things, it’s possible to link c ons c e lls toge the r into lists. In fa c t,
lists in L isp a re just a n a bstra c t illusion— a ll of the m a re a c tua lly c om pose d of c ons c e lls.
For insta nc e , suppose we c re a te the list '(1 2 3). He re ’s how this list is re pre se nte d in c om pute r m e m ory:

It’s c re a te d using thre e c ons c e lls. E a c h c e ll points to a num be r, a s we ll a s the ne xt c ons c e ll for the list. T he fina l c ons c e ll
the n points a t nil, to te rm ina te the list. (If you’ve e ve r use d a linke d list in a nothe r progra m m ing la ngua ge , this is the sa m e ba sic
ide a . ) You c a n think of this a rra nge m e nt like a c a lling c ha in for your frie nds: “ W he n I know a bout a pa rty this we e ke nd, I’ll c a ll
Bob, a nd the n Bob will c a ll L isa , who will c a ll . . . ” E a c h pe rson in a c a lling c ha in is re sponsible for only one phone c a ll, whic h
a c tiva te s the ne xt c a ll in the list.
List F unc tions
Ma nipula ting lists is e xtre m e ly im porta nt in L isp progra m m ing. T he re a re thre e ba sic func tions for m a nipula ting c ons c e lls (a nd
he nc e lists) in L isp: cons, car, a nd cdr.

The c ons F unc tion

If you wa nt to link a ny two pie c e s of da ta in your L isp progra m (re ga rdle ss of type ), the usua l wa y to do tha t is with the cons
func tion. W he n you c a ll cons, the L isp c om pile r typic a lly a lloc a te s a sm a ll c hunk of m e m ory, the c ons c e ll, tha t c a n hold two
re fe re nc e s to the obje c ts be ing linke d. (Usua lly, the se c ond of the two ite m s be ing linke d will be a list. ) For e xa m ple , le t’s link the
sym bol chicken to the sym bol cat:
> (cons 'chicken 'cat)
(CHICKEN . CAT)
As you c a n se e , cons re turns a single obje c t, the c ons c e ll, re pre se nte d by pa re nthe se s a nd a dot be twe e n the two c onne c te d
ite m s. Don’t c onfuse this with a re gula r list. T he dot in the m iddle m a ke s this a c ons c e ll, just linking those two ite m s toge the r.
Notic e how we pre fix our two pie c e s of da ta with a single quote to m a ke sure tha t L isp se e s the m a s just da ta a nd doe sn’t try to
e va lua te the m a s c ode .
If inste a d of a nothe r pie c e of da ta , we a tta c h the sym bol nil on the right side of the list, som e thing spe c ia l ha ppe ns:
> (cons 'chicken 'nil)
(CHICKEN)
Unlike with our cat, the nil doe s not show in the output this tim e . T he re ’s a sim ple re a son for this: nil is a spe c ia l sym bol tha t
is use d to te rm ina te a list in L isp. T ha t sa id, the L isp RE PL is ta king a shortc ut a nd just sa ying tha t we c re a te d a list with one
ite m , our chicken. It c ould ha ve displa ye d the re sult by e xplic itly showing our c ons c e ll a nd printing (CHICKEN . NIL). Howe ve r,
be c a use this re sult is c oinc ide nta lly a lso a list, it inste a d will show the list nota tion.
T he le sson he re is tha t L isp will a lwa ys go out of its wa y to “ hide ” the c ons c e lls from you. W he n it c a n, it will show your
re sults using lists. It will show a c ons c e ll (with the dot be twe e n the obje c ts) only if the re isn’t a wa y to show your re sult using lists.
T he pre vious e xa m ple c a n a lso be writte n like this:
> (cons 'chicken ())
(CHICKEN)
T he e mpty list, (), c a n be use d inte rc ha nge a bly with the nil sym bol in Com m on L isp. T hinking of the te rm ina tor of a list a s a n
e m pty list m a ke s se nse . W ha t do you ge t whe n you a dd a c hic ke n to a n e m pty list? Just a list with a c hic ke n in it. T he cons
func tion a lso c a n a dd a ne w ite m to the front of the list. For e xa m ple , to a dd pork to the front of a list c onta ining (beef
chicken), use cons like so:
> (cons 'pork '(beef chicken))
(PORK BEEF CHICKEN)
W he n L ispe rs ta lk a bout using cons, the y sa y the y a re c onsing som e thing. In this e xa m ple , we c onse d pork to a list c onta ining
be e f a nd c hic ke n.
Sinc e a ll lists a re m a de of c ons c e lls, our (beef chicken) list m ust ha ve be e n c re a te d from its own two c ons c e lls, pe rha ps like
this:
> (cons 'beef (cons 'chicken ()))
(BEEF CHICKEN)
Com bining the pre vious two e xa m ple s, we c a n se e wha t a ll the lists look like whe n vie we d a s c onse s. T his is wha t is re ally
ha ppe ning:
> (cons 'pork (cons 'beef (cons 'chicken ())))
(PORK BEEF CHICKEN)
Ba sic a lly, this is te lling us tha t whe n we c ons toge the r a list of thre e ite m s, we ge t a list of thre e ite m s. No whole sa le c opying or
de le ting of da ta e ve r ne e ds to ta ke pla c e .
T he RE PL e c hoe d ba c k to us our e nte re d ite m s a s a list, (pork beef chicken), but it c ould just a s e a sily (though a little le ss
c onve nie ntly) ha ve re porte d ba c k the ite m s e xa c tly a s we e nte re d the m : (cons 'pork (cons 'beef (cons 'chicken ()))). E ithe r
re sponse would ha ve be e n pe rfe c tly c orre c t. In Lisp, a c hain of c ons c e lls and a list are e x ac tly the same thing.

The c ar and c dr F unc tions

L ists a re just long c ha ins of two-ite m c e lls.


T he car func tion is use d for ge tting the thing out of the first slot of a c e ll:
> (car '(pork beef chicken))
PORK
T he cdr func tion is use d to gra b the va lue out of the se c ond slot, or the re m a inde r of a list:
> (cdr '(pork beef chicken))
(BEEF CHICKEN)
You c a n string toge the r car a nd cdr into ne w func tions like cadr, cdar, or cadadr. T his le ts you suc c inc tly e xtra c t spe c ific
pie c e s of da ta out of c om ple x lists. E nte ring cadr is the sa m e a s using car a nd cdr toge the r— it re turns the se c ond ite m from a list.
(T he first slot of the se c ond c ons c e ll would c onta in tha t ite m . ) T a ke a look a t this e xa m ple :

> (cdr '(pork beef chicken))


(BEEF CHICKEN)

> (car '(beef chicken))


BEEF

> (car (cdr '(pork beef chicken)))


BEEF

> (cadr '(pork beef chicken))


BEEF
W e know tha t cdr will ta ke a wa y the first ite m in a list . If we the n ta ke tha t shorte ne d list a nd use car, we ’ll ge t the first

ite m in the ne w list . T he n, if we use the se two c om m a nds toge the r, we ’ll ge t the se c ond ite m in the origina l list .

Fina lly, if we use the cadr c om m a nd, it give s us the sa m e re sult a s using car a nd cdr toge the r . E sse ntia lly, using the cadr
c om m a nd is the sa m e a s sa ying tha t you wa nt the se c ond ite m in the list.

The list F unc tion

For c onve nie nc e , Com m on L isp ha s m a ny func tions built on top of the ba sic thre e — cons, car, a nd cdr. A use ful one is the list
func tion, whic h doe s the dirty work of c re a ting a ll the c ons c e lls a nd builds our list a ll a t onc e :
> (list 'pork 'beef 'chicken)
(PORK BEEF CHICKEN)
Re m e m be r tha t the re is no diffe re nc e be twe e n a list c re a te d with the list func tion, one c re a te d by spe c ifying individua l c ons
c e lls, or one c re a te d in da ta m ode using the single quote . T he y’re a ll the sa m e a nim a l.
Ne ste d Lists
L ists c a n c onta in othe r lists. He re ’s a n e xa m ple :
'(cat (duck bat) ant)
T his is a list c onta ining thre e ite m s. T he se c ond ite m of this list is (duck bat), whic h is a list itse lf. T his is a n e xa m ple of a
ne ste d list.
Howe ve r, unde r the hood, the se ne ste d lists a re still just m a de out of c ons c e lls. L e t’s look a t a n e xa m ple whe re we pull ite m s
out of ne ste d lists. He re , the first ite m is (peas carrots tomatoes) a nd the se c ond ite m is (pork beef chicken):

> (car '((peas carrots tomatoes) (pork beef chicken)))


(PEAS CARROTS TOMATOES)

> (cdr '(peas carrots tomatoes))


(CARROTS TOMATOES)

> (cdr (car '((peas carrots tomatoes) (pork beef chicken))))


(CARROTS TOMATOES)

> (cdar '((peas carrots tomatoes) (pork beef chicken)))


(CARROTS TOMATOES)

T he car func tion give s us the first ite m in the list, whic h is a list in this c a se . Ne xt, we use the c dr c om m a nd to c hop off

the first ite m from this inne r list, le a ving us with (CARROTS TOMATOES) . Using the se c om m a nds toge the r give s this sa m e re sult

. Fina lly, using cdar give s the sa m e re sult a s using cdr a nd car se pa ra te ly .
As de m onstra te d in this e xa m ple , c ons c e lls a llow us to c re a te c om ple x struc ture s, a nd we use the m he re to build a ne ste d list.
T o prove tha t our ne ste d list c onsists sole ly of c ons c e lls, he re is how we c ould c re a te this ne ste d list using only the cons c om m a nd:
> (cons (cons 'peas (cons 'carrots (cons 'tomatoes ())))
(cons (cons 'pork (cons 'beef (cons 'chicken ()))) ()))
((PEAS CARROTS TOMATOES) (PORK BEEF CHICKEN))
He re a re som e m ore e xa m ple s of func tions ba se d on car a nd cdr tha t we c ould use on our da ta struc ture :
> (cddr '((peas carrots tomatoes) (pork beef chicken) duck))
(DUCK)
> (caddr '((peas carrots tomatoes) (pork beef chicken) duck))
DUCK
> (cddar '((peas carrots tomatoes) (pork beef chicken) duck))
(TOMATOES)
> (cadadr '((peas carrots tomatoes) (pork beef chicken) duck))
BEEF
Com m on L isp a lre a dy de fine s a ll the se func tions for you. You c a n use a ny func tion with the na m e c*r right out of the box, up to
four le ve ls de e p. In othe r words, cadadr will a lre a dy e xist for you to use , whe re a s cadadar (whic h is five le ve ls de e p) doe s not (you
would ne e d to write tha t func tion yourse lf). T he se func tions m a ke it e a sy to m a nipula te c ons c e lls-ba se d struc ture s in L isp, no
m a tte r how c om plic a te d the y m ight be .
W hat You've Le ar ne d
In this c ha pte r, we disc usse d the ba sic L isp synta x. Along the wa y, you le a rne d the following:
Pa re nthe se s in L isp a re the re to ke e p the a m ount of synta x to a m inim um .
L ists a re c re a te d from c ons c e lls.
You c a n c re a te lists by m a king c ons c e lls with the cons c om m a nd.
You c a n inspe c t the pie c e s of a list with car a nd cdr.
P ar t II. Lisp is Symme tr y
Chapte r 4. M aking De c isions with Conditions
In the pre vious c ha pte rs, you le a rne d som e ba sic L isp c om m a nds, a s we ll a s som e of the philosophy be hind L isp. In this c ha pte r,
we ’ll be looking in de ta il a t c om m a nds for ha ndling c onditions. T he e le ga nc e of the se c om m a nds shows tha t the unusua l philosophy
a nd de sign of L isp ha s re a l pra c tic a l be ne fits.
The Symme tr y of nil and ()
One thing is pa rtic ula rly striking whe n we look a t how L isp c om m a nds a nd da ta struc ture s work: T he y a re im bue d with sym m e try
in e ve ry c onc e iva ble wa y. T his sym m e try c a n give your L isp c ode a c e rta in e le ga nc e tha t othe r la ngua ge s c a nnot ha ve , a nd L isp’s
sim ple synta x is a n im porta nt fa c tor in m a king this sym m e try possible .
Empty Equals F alse
Sinc e the L isp philosophy strongly e m pha siz e s the use of lists to store a nd m a nipula te inform a tion, it will c om e a s no surprise
tha t the de sign of Com m on L isp fa vors be ha viors tha t m a ke it e a sy to slic e a nd dic e suc h lists. T he m ost profound de sign de c ision
m a de in Com m on L isp, with re ga rd to lists, is tha t it a utom a tic a lly tre a ts a n e m pty list a s a fa lse va lue whe n e va lua ting a
c ondition:
> (if '()
'i-am-true
'i-am-false)

I-AM-FALSE
> (if '(1)
'i-am-true
'i-am-false)
I-AM-TRUE
T his e xa m ple shows tha t whe n we pa ss the e m pty list () into a n if form , it e va lua te s a s a fa lse va lue , whe re a s a list tha t
c onta ins a n ite m e va lua te s a s true .
Be c a use we c a n e a sily de te c t a n e m pty list, we c a n proc e ss lists using re c ursion. W ith this te c hnique , we c a n ta ke ite m s from
the front of a list a nd se nd the re st of the list ba c k to the sa m e func tion until the list is e m pty. (It’s a good thing tha t de te c ting
e m pty lists is so e a sy, be c a use so m a ny func tions in L isp e nd up be ing list-e a te rs. )

L e t’s look a t a c om m on list-e a ting func tion, whic h c a lc ula te s the le ngth of a list.
> (defun my-length (list)
(if list
(1+ (my-length (cdr list)))
0))
> (my-length '(list with four symbols))

4
T his func tion is writte n in c la ssic L isp style . It c a lls itse lf re c ursive ly a s it c hom ps ite m s off the front of the list. Ca lling yourse lf
in this wa y is not only a llowe d in L isp, but is ofte n strongly e nc oura ge d. L ists in L isp a re re c ursive (c onse s of c onse s of c onse s . .
. ), so the a c t of c onsum ing a list m a ps na tura lly onto func tions tha t a re re c ursive .

Note

Ca lling yourse lf re c ursive ly c a n som e tim e s m a ke for slow c ode . In Cha pte r 14, we ’ll re write the my-length func tion using a
spe c ia l, pote ntia lly fa ste r, type of re c ursion.
The F our Disguise s of ()
Not only doe s the e m pty list e va lua te to fa lse , but it is the only fa lse va lue in Com m on L isp. A ny v alue not e quiv ale nt to an
e mpty list will be c onside re d a true va lue . T his e xpla ins why the e xpre ssion '(1) in the e a rlie r e xa m ple wa s tre a te d a s true .
Howe ve r, the re a re som e othe r e xpre ssions in L isp tha t a re disguise s for the one a nd only e m pty list:

W e c a n se e tha t the e xpre ssions in this ta ble a re e quiva le nt by c om pa ring the m with one a nothe r:

(eq '() nil) ==> T

(eq '() ()) ==> T

(eq '() 'nil) ==> T


Notic e tha t the only va lue in the ta ble tha t se e m s norm a l is the quote d list on the le ft side of the c om pa risons. T he othe r thre e
a ll se e m to bre a k the rule s of L isp form s tha t we ta lke d a bout in the pre vious c ha pte r.
T he first two e xa m ple s a re pa rtic ula rly puz z ling. T he y a re m issing the quota tion m a rk tha t te lls the L isp e nvironm e nt, “ He y,
this ite m is a pie c e of da ta , not c ode !” In the c a se of nil, you would e xpe c t tha t this would a c tua lly be the na m e of a va ria ble
tha t c ould ha ve som e kind of a rbitra ry va lue . In the c a se of the unquote d (), the re ’s no wa y you c ould te ll wha t would ha ppe n. T he
pa re nthe se s look like a form of c ode tha t ne e ds to be e va lua te d, but a L isp form a lwa ys ha s a sym bol a t the be ginning, te lling it
wha t to do. W ha t do we do whe n the re ’s nothing inside the form a t a ll?
T he bottom line is tha t Com m on L isp is a rc hite c te d be hind the sc e ne s to m a ke sure a ll four of the se va lue s look like a n e m pty
list whe n you use the m in your progra m , a llowing m ost L isp c onditiona ls to be writte n with a n e le ga nt bre vity. For insta nc e , the re

is a c onsta nt na m e d nil tha t e va lua te s to itse lf a nd a llows you to om it the quota tion m a rk in the first c a se . T he se c ond c a se

is a na tura l by-produc t of how Com m on L isp pa rse s a n e m pty form . T he third c a se is due to a re quire m e nt in the
Com m on L isp spe c tha t sa ys tha t () a nd nil should be tre a te d the sa m e .
Although the re ’s a c e rta in be a uty to ha ving a ll of the se va lue s be the sa m e , not e ve ry L ispe r a gre e s with this se ntim e nt. Afte r
a ll, a re fa lse a nd e m pty list re a lly the sa m e kind of thing? T he c re a tors of the othe r popula r dia le c t of L isp, Sc he m e , fe lt
diffe re ntly a bout this issue , a nd pre fe rre d to ke e p the c onc e pts of fa lsity a nd e m pty list c om ple te ly se pa ra te , a t a slight c ost to
c ode bre vity.
The Conditionals: if and Be yond
Now tha t you unde rsta nd how L isp ha ndle s true a nd fa lse , le t’s look a t if a nd som e of the othe r c onditiona l c om m a nds.
O ne Thing at a Time with if
T he if c om m a nd c a n be use d to m a ke diffe re nt things ha ppe n whe n things a re true (suc h a s whe n 1 + 2 = 3) or fa lse (suc h a s
whe n 1 + 2 = 4).
> (if (= (+ 1 2) 3)
'yup
'nope)
YUP
> (if (= (+ 1 2) 4)
'yup
'nope)
NOPE
T he if c om m a nd c a n a lso be use d to c he c k whe the r a list is e m pty:
> (if '(1)
'the-list-has-stuff-in-it
'the-list-is-empty)
THE-LIST-HAS-STUFF-IN-IT
> (if '()
'the-list-has-stuff-in-it
'the-list-is-empty)
THE-LIST-IS-EMPTY
So fa r, the only wa y to bra nc h on a c ondition tha t we ’ve looke d a t ha s be e n the if c om m a nd:
> (if (oddp 5)
'odd-number
'even-number)
ODD-NUMBER
All we ’re doing he re is c he c king whe the r the num be r 5 is odd, the n, de pe nding on the re sult, e va lua ting one of the two following
e xpre ssions in the if form . Sinc e 5 is odd, it e va lua te s the first suc h e xpre ssion, a nd the form a s a whole re turns odd-number.
T he re ’s a lot ha ppe ning in this ha rm le ss-looking little c om m a nd— stuff tha t’s im porta nt to unde rsta nding L isp. He re a re two
im porta nt obse rva tions:

Only one of the e xpre ssions a fte r the if is a c tua lly e va lua te d.
W e c a n only do one thing in a n if sta te m e nt.

Usua lly, whe n a func tion is e xe c ute d in L isp, a ll the e xpre ssions a fte r the func tion na m e a re e va lua te d, be fore the func tion itse lf
is e va lua te d. Howe ve r, if doe s not follow the se rule s. T o se e this, c onside r the following e xa m ple :
> (if (oddp 5)
'odd-number
(/ 1 0))

ODD-NUMBER
Any se lf-re spe c ting, la w-a biding L isp func tion would kic k your butt to the c urb if you trie d to run this c ode , be c a use you’re
dividing by z e ro.
But if is not just a func tion. It’s a spe c ial form, whic h give s it spe c ia l privile ge s, suc h a s the right to not e va lua te a ll its
pa ra m e te rs in the norm a l wa y. T his m a ke s se nse , sinc e the whole point of a c ondition is to run som e stuff but not othe r stuff. In this
c a se , it just m e rrily ignore s the division by z e ro, sinc e it’s in the pa rt of the bra nc h tha t a pplie s only to e ve n num be rs. Conditiona l
c om m a nds in L isp a re typic a lly spe c ia l form s.
Note

Som e of the c onditiona l c om m a nds m a y be m a c ros, whic h a re som e thing like use r-c re a te d spe c ia l form s. Be ing a spe c ia l form
usua lly im plie s tha t a c om m a nd is dire c tly “ ba ke d in” to the la ngua ge . In Cha pte r 16, you’ll le a rn how to write suc h m a c ros
yourse lf.

Sinc e only one e xpre ssion inside a n if is e ve r e va lua te d, it’s im possible to do two or m ore se pa ra te things inside your bra nc h.
T he re is a c tua lly a c le ve r style of progra m m ing (c a lle d func tional programming, a s we ’ll disc uss in Cha pte r 14), whic h c onside rs
this a Good T hing. Howe ve r, for c a se s whe n you re a lly wa nt to do m ore tha n one thing, you c a n use a spe c ia l c om m a nd, progn, to
we dge in e xtra c om m a nds in a single e xpre ssion. W ith progn, only the la st e va lua tion is re turne d a s the va lue of the full
e xpre ssion. In this ne xt e xa m ple , for insta nc e , we use the c om m a nd to se t a spe c ia l globa l va ria ble dire c tly inside our c onditiona l
bra nc h.
> (defvar *number-was-odd* nil)
> (if (oddp 5)
(progn (setf *number-was-odd* t)
'odd-number)
'even-number)

ODD-NUMBER
> *number-was-odd*
T
G oing Be yond if: The whe n and unle ss Alte r native s
Sinc e it’s kind of a pa in to use progn e ve ry tim e you wa nt to do m ultiple things inside a n if, L isp ha s se ve ra l othe r c om m a nds
tha t inc lude a n implic it progn. T he m ost ba sic of the se a re when a nd unless:
> (defvar *number-is-odd* nil)
> (when (oddp 5)
(setf *number-is-odd* t)
'odd-number)
ODD-NUMBER
> *number-is-odd*

T
> (unless (oddp 4)
(setf *number-is-odd* nil)
'even-number)
EVEN-NUMBER
> *number-is-odd*
NIL
W ith when, a ll the e nc lose d e xpre ssions a re e va lua te d whe n the c ondition is true . W ith unless, a ll the e nc lose d e xpre ssions a re
e va lua te d whe n the c ondition is fa lse . T he tra de -off is tha t the se c om m a nds c a n’t do a nything whe n the c ondition e va lua te s in the
opposite wa y; the y just re turn nil a nd do nothing.
The Command That Doe s It All: c ond
But wha t do you do if you’re the kind of c ode r who wa nts it a ll? Ma ybe you just a in’t in a c om prom isin’ m ood a nd wa nt a
func tion tha t will do e ve rything! W e ll, L isp ha s you c ove re d.

T he cond form is the c la ssic wa y to do bra nc hing in L isp. T hrough the libe ra l use of pa re nthe se s, it a llows for a n im plic it progn,
c a n ha ndle m ore tha n one bra nc h, a nd c a n e ve n e va lua te se ve ra l c onditions in suc c e ssion. Sinc e cond ha s be e n a round sinc e the
L isp Stone Age , a nd it’s c om pre he nsive in its a bilitie s, m a ny L isp progra m m e rs c onside r it to be the one true L isp c onditiona l.
He re ’s a n e xa m ple :
> (defvar *arch-enemy* nil)

> (defun pudding-eater (person)


(cond ((eq person 'henry) (setf *arch-enemy* 'stupid-lisp-alien)
'(curse you lisp alien - you ate my pudding))

((eq person 'johnny) (setf *arch-enemy* 'useless-old-johnny)


'(i hope you choked on my pudding johnny))

(t '(why you eat my pudding stranger ?))))

> (pudding-eater 'johnny)


(I HOPE YOU CHOKED ON MY PUDDING JOHNNY)
> *arch-enemy*
JOHNNY
> (pudding-eater 'george-clooney)
(WHY YOU EAT MY PUDDING STRANGER ?)
As you c a n se e , the body of a cond use s a la ye r of pa re nthe se s to se pa ra te the diffe re nt bra nc he s of the c ondition. T he n the first
e xpre ssion of e a c h pa re nthe siz e d pa rt c onta ins the c ondition for m a king tha t bra nc h a c tive . In our e xa m ple , we ha ve diffe re nt

bra nc he s for e a c h type of pudding thie f: one for He nry , one for Johnny , a nd one for e ve ryone e lse . W e use eq to
c om pa re the supplie d pe rson’s na m e with e a c h pote ntia l pe rpe tra tor.
T he c onditions in a cond form a re a lwa ys c he c ke d from the top down, so the first suc c e ssful m a tc h drive s the be ha vior. In this

e xa m ple , the la st bra nc h ha s a c ondition of t (for true ), gua ra nte e ing tha t a t le a st the la st bra nc h will a lwa ys be e va lua te d.
T his is a c om m on cond idiom .
As with when a nd unless, the trigge re d bra nc h m a y c onta in m ore tha n one c om m a nd, sinc e the re is a n im plic it progn. In this

c a se , the first two bra nc he s se t a n e xtra *arch-enemy* va ria ble , be side s supplying a re turn va ria ble .
Br anc hing with c ase
L e t’s look a t one fina l L isp c om m a nd: the case form . It is c om m on to use the eq func tion for c onditiona ls, a nd case le ts you
supply a va lue to c om pa re a ga inst. Using case, we c a n re write the pre vious e xa m ple a s follows:
> (defun pudding-eater (person)
(case person
((henry) (setf *arch-enemy* 'stupid-lisp-alien)
'(curse you lisp alien - you ate my pudding))
((johnny) (setf *arch-enemy* 'useless-old-johnny)
'(i hope you choked on my pudding johnny))
(otherwise '(why you eat my pudding stranger ?))))
T his ve rsion of the c ode is a lot e a sie r on the e ye s. T he na m e of the pe rson ha ndle d by e a c h pa rt of the case sta te m e nt is c le a rly
visible — it’s not hidde n inside a n e qua lity c he c k. De pe nding on whic h ve rsion of L isp you use , a case sta te m e nt like this m a y a lso
be m ore e ffic ie nt, e spe c ia lly with longe r sta te m e nts, whe re la rge r num be rs of c a se s a re ha ndle d.

W ar ning

Be c a use the case c om m a nd use s eq for c om pa risons, it is usua lly use d only for bra nc hing on sym bol va lue s. It c a nnot be use d to
bra nc h on string va lue s, a m ong othe r things. Se e Com pa ring Stuff: e q, e qua l, a nd More in Com pa ring Stuff: e q, e qua l, a nd More for
de ta ils.
Cool Tr ic ks with Conditions
T he funda m e nta l de sign of L isp le ts you ge t a lot of m ile a ge out of a fe w sim ple c om m a nds. Spe c ific a lly, a c ouple of
c ounte rintuitive tric ks involving c onditions in L isp c a n he lp you write c le a ne r c ode . T he first involve s two ne w c onditiona l
c om m a nds. T he se c ond ta ke s a dva nta ge of L isp’s sim ple c onc e ption of true a nd fa lse .
Using the Ste alth Conditionals and and or
T he c onditiona ls and a nd or a re sim ple m a the m a tic a l ope ra tors, whic h a llow you to m a nipula te Boole a n va lue s in the sa m e wa y
you m ight m a nipula te num be rs using a ddition a nd subtra c tion.
For e xa m ple , he re ’s how we c ould use and to se e if thre e num be rs a re odd:
> (and (oddp 5) (oddp 7) (oddp 9))
T
Be c a use 5, 7, a nd 9 a re odd, the e ntire e xpre ssion e va lua te s a s true .
Sim ila rly, we c a n use or to se e whe the r a t le a st one of a se t of num be rs is odd:
> (or (oddp 4) (oddp 7) (oddp 8))
T
Be c a use 7 is odd, the or c om m a nd still e va lua te s a s true , de spite the fa c t tha t 4 a nd 8 a re e ve n.
But the re ’s som e thing a bit m ore inte re sting a bout and a nd or tha t you m ight not notic e just by looking a t the se first two
e xa m ple s. So fa r, the se two c om m a nds look like c om ple te ly ordina ry m a the m a tic a l ope ra tors; the y do not look like c onditiona l
c om m a nds, suc h a s if or cond. Howe ve r, the y c a n be use d for c onditiona l be ha vior.
For insta nc e , he re ’s how we c ould use the se c onditiona ls to se t a globa l va ria ble to true only whe n a num be r is e ve n:
> (defparameter *is-it-even* nil)
*IS-IT-EVEN*

> (or (oddp 4) (setf *is-it-even* t))


T
> *is-it-even*
T
If we do the sa m e thing using a n odd num be r, the va ria ble re m a ins unc ha nge d:
> (defparameter *is-it-even* nil)

*IS-IT-EVEN
> (or (oddp 5) (setf *is-it-even* t))

T
> *is-it-even*

NIL
T his e xa m ple illustra te s tha t L isp use s shortc ut B oole an e v aluation. T his m e a ns tha t onc e L isp de te rm ine s tha t a n e a rlie r
sta te m e nt in a list of or va lue s is true , it sim ply re turns true a nd doe sn’t bothe r e va lua ting the re m a ining sta te m e nts. Sim ila rly,
onc e it de te rm ine s tha t a n e a rlie r sta te m e nt in a list of and va lue s is fa lse , it stops without bothe ring to e va lua te the re st of the
sta te m e nts.
W hile this m a y se e m like a m inor e sote ric obse rva tion, it c a n a c tua lly be ve ry use ful in m a ny situa tions. For insta nc e , im a gine
if you wa nt to sa ve a file to disk, but only if the file wa s m odifie d, a nd only whe n the use r wa nts it to be sa ve d. T he ba sic struc ture
c ould be writte n a s follows:
(if *file-modified*
(if (ask-user-about-saving)
(save-file)))
He re , the func tion ask-user-about-saving would a sk the use r a bout the file , a nd the n re turn true or fa lse ba se d on the use r’s
wishe s. Howe ve r, sinc e shortc ut Boole a n e va lua tion is gua ra nte e d to be use d for Boole a n ope ra tions unde r Com m on L isp a nd m ost
othe r L isp dia le c ts, we c ould write this inste a d:
(and *file-modified* (ask-user-about-saving) (save-file))
Using this c le a ne r style for e va lua ting c onditiona l c ode is possible only if you think be yond the typic a l use of the Boole a n
ope ra tors a s sim ply m a the m a tic a l ope ra tors. T his form ha s a n e le ga nt sym m e try be twe e n the thre e e xpre ssions, whic h som e L ispe rs
m a y like . Howe ve r, othe rs would a rgue tha t a re a de r of your c ode m a y e a sily m iss the fa c t tha t (save-file) doe s som e thing
be yond re turning a Boole a n va lue . A bit of tim e is re quire d to wra p your he a d a round this m ore -ge ne ra l c onc e ption of wha t and a nd
or a c tua lly m e a n.
A third wa y to write this c ode , whic h is a c om prom ise be twe e n the pre vious a pproa c he s, is a s follows:
(if (and *file-modified*
(ask-user-about-saving))
(save-file)))
Ma ny e xpe rie nc e d L ispe rs will c onside r this ve rsion a bit c le a re r tha n the pre vious two ve rsions, be c a use only e xpre ssions tha t a re
e xpre ssly de signe d to re turn a Boole a n va lue a re tre a te d a s pa rt of the c ondition.
Using F unc tions That Re tur n M or e than Just the Tr uth
Now le t’s look a t a nothe r be ne fit of L isp’s sim ple wa y of thinking a bout true a nd fa lse . As we ’ve a lre a dy disc usse d, a ny va lue in
Com m on L isp (e xc e pt for the diffe re nt va ria tions on nil) is true . T his m e a ns tha t func tions tha t a re c om m only use d in c onditions
ha ve the option of re turning more than just the truth.
For insta nc e , the L isp c om m a nd member c a n be use d to c he c k for list m e m be rship for a n ite m :
> (if (member 1 '(3 4 1 5))
'one-is-in-the-list
'one-is-not-in-the-list)

'ONE-IS-IN-THE-LIST
T his se e m s pre tty stra ightforwa rd. Howe ve r, onc e a ga in, the re is som e thing ha ppe ning be hind the sc e ne s tha t you m a y not
e xpe c t. L e t’s run the member c om m a nd in isola tion:
> (member 1 '(3 4 1 5))
(1 5)
W ha t the he c k ha ppe ne d he re ? W hy is it re turning (1 5)?
Ac tua lly, the re ’s a pe rfe c tly ra tiona l e xpla na tion for this. W he ne ve r a L ispe r write s a func tion tha t re turns true a nd fa lse , she
will think to he rse lf, “ Is the re a nything e lse I c ould re turn othe r tha n just t? ” Sinc e a ll non-nil va lue s in Com m on L isp e va lua te to
true , re turning som e othe r va lue is e sse ntia lly a fre e bie . T he im ple m e nte rs of the member func tion de c ide d tha t som e c ra z y L ispe r
som e whe re m a y se e the va lue in ha ving the ta il of the list for som e c a lc ula tion tha t use s this func tion.

Note

Re m e m be r from Cha pte r 3 tha t the list '(3 4 1 5) is the sa m e a s the ne ste d c ontra ption (cons 3 (cons 4 (cons 1 (cons 5
nil)))). T his should m a ke it c le a r why the va lue (cons 1 (cons 5 nil)) is a n e a sy thing for the member func tion to re turn.

But why doe sn’t it just re turn the va lue it found, inste a d of the ta il? In fa c t, this would ha ve be e n a use ful wa y to de fine the
member func tion, be c a use it would a llow pa ssing the origina l va lue to som e othe r func tion in suc h a m a nne r. Unfortuna te ly, one
e dge c a se in pa rtic ula r would ruin this pla n:
> (if (member nil '(3 4 nil 5))
'nil-is-in-the-list
'nil-is-not-in-the-list)

'nil-is-in-the-list
As you c a n se e in this e xa m ple , the member func tion still give s the c orre c t a nswe r, e ve n whe n we se a rc h for nil a s the m e m be r!
If the member func tion ha d a c tua lly re turne d nil (in othe r words, the origina l va lue we we re se a rc hing for), it would ha ve e va lua te d
a s fa lse , a nd the e xa m ple would ha ve inc orre c tly sta te d tha t nil isn’t in the list. Howe ve r, sinc e the member func tion re turns the ta il
of the list a t the point of the found ite m , it c a n be gua ra nte e d to a lwa ys be a true va lue . A suc c e ssful disc ove ry of the de sire d
va lue will a lwa ys re turn a list with a t le a st one va lue , whic h we know a lwa ys e va lua te s a s true .
One func tion tha t re a lly be ne fits from ric h re turn va lue s is find-if, a s follows:
> (find-if #'oddp '(2 4 5 6))

5
> (if (find-if #'oddp '(2 4 5 6))
'there-is-an-odd-number
'there-is-no-odd-number)

'there-is-an-odd-number
T he find-if func tion a c tua lly ta ke s a nothe r func tion, in this c a se oddp, a s a pa ra m e te r. find-if will find the first va lue in the
list for whic h oddp re turns true . In this c a se , it will find the first num be r (if a ny) tha t is a n odd num be r.
You c a n se e c le a rly how find-if c a n fill dua l role s: e ithe r a s a re trie ve r of va lue s m a tc hing som e c onstra int or a s a true /fa lse
va lue inside a c ondition.

Note

Don’t worry ye t a bout the we ird ha sh m a rk (#) in front of oddp in the e xa m ple . W e ’ll disc uss the find-if func tion, a nd othe r so-
c a lle d highe r-orde r func tions, in gre a te r de ta il in Cha pte r 7 a nd Cha pte r 14.

Ala s, the e le ga nt sym m e try of the find-if func tion ha s a single , sm a ll, ugly wa rt. If we try our e dge c a se a ga in, se a rc hing for a
nil va lue , we ge t a ra the r disa ppointing re sult:
> (find-if #'null '(2 4 nil 6))
NIL
T he null func tion, whic h re turns true for a ny of the nil va lue s, c orre c tly finds the nil. Unfortuna te ly, in this one a nnoying c a se ,
we would not wa nt to use find-if inside a c onditiona l sta te m e nt, be c a use a c orre c tly found va lue still re turns a re sult tha t
e va lua te s a s fa lse . T he sym m e try ha s be e n broke n.
T he se a re the kinds of sm a ll things tha t m a ke e ve n grown L ispe rs she d a te a r.
Compar ing Stuff: e q, e qual, and M or e
T he re ’s a lot of be a utiful sym m e try in L isp. One pa rt of L isp tha t isn’t so be a utiful, though, involve s the c om m a nds for
c om pa ring things.
If you wa nt to c om pa re two va lue s in L isp to find out if the y a re “ the sa m e , ” you will find a be wilde ring a ssortm e nt of diffe re nt
func tions tha t purport to a c c om plish this. Of the se , equal, eql, eq, =, string-equal, a nd equalp a re the m ost c om m only use d. A
L ispe r m ust unde rsta nd the subtle tie s of the se func tions intim a te ly in orde r to know how to c om pa re va lue s c orre c tly.

Be fore we sta rt disse c ting this m a dne ss, le t m e give you Conra d’s Rule of T hum b for Com pa ring Stuff. Follow this rule , a nd
though you m a y not be writing the world’s c le a ne st L isp c ode , you will proba bly be a ble to post som e sa m ple s to a ne wsgroup
without m ore se a sone d L ispe rs running you out of town with torc he s a nd pitc hforks.

Sym bols should a lwa ys be c om pa re d to othe r sym bols with eq:


> (defparameter *fruit* 'apple)
*FRUIT*
> (cond ((eq *fruit* 'apple) 'its-an-apple)
((eq *fruit* 'orange) 'its-an-orange))
ITS-AN-APPLE
T he eq func tion is the sim ple st of a ll the L isp c om pa rison func tions, a nd it’s a lso ve ry fa st. It doe sn’t re a lly work for c om pa ring
ite m s be side s sym bols, but if you c onside r the c e ntra l role sym bols pla y in L isp, you’ll re a liz e how use ful this func tion c a n be .
E xpe rie nc e d L ispe rs m ight look down on c ode if it c om pa re s two things, known to be sym bols, with som e thing othe r tha n eq.

Note

eq c a n a lso be use d to c om pa re c onse s (the links c re a te d by the c ons c om m a nd). Howe ve r, it re turns true va lue s only whe n a c ons
is c om pa re d dire c tly to itse lf, c re a te d by the sa m e c ons c a ll. T his m e a ns, two unre la te d c onse s tha t “ look” e xa c tly the sa m e c a n
fa il a n e q te st. Sinc e eq c a n c he c k a c ons c e ll only a ga inst itse lf, using eq with c onse s isn’t re a lly tha t use ful for a be ginne r.
Howe ve r, a n a dva nc e d L ispe r m a y wa nt to c om pa re c onse s with eq unde r c e rta in c irc um sta nc e s.

If you’re not de a ling with two sym bols, just use equal. T his c om m a nd will te ll you whe n two things a re isomorphic, m e a ning
the y “ look the sa m e . ” It works for the whole suite of ba sic L isp da ta type s, a s shown he re :
;;comparing symbols
> (equal 'apple 'apple)
T

;;comparing lists
> (equal (list 1 2 3) (list 1 2 3))
T
;;Identical lists created in different ways still compare as the same
> (equal '(1 2 3) (cons 1 (cons 2 (cons 3))))

;;comparing integers
> (equal 5 5)
T

;;comparing floating point numbers


> (equal 2.5 2.5)

T
;;comparing strings
> (equal "foo" "foo")
T

;;comparing characters
> (equal #\a #\a)

T
As you c a n se e , m ost ite m s in L isp c a n be e ffe c tive ly c om pa re d with equal, inc luding strings a nd c ha ra c te rs (whic h a re disc usse d
in the ne xt c ha pte r).
Now tha t you know the ba re m inim um a bout L isp c om pa risons to fa ke your wa y through your ne xt c oc kta il pa rty, le t’s look a t a ll
the othe r c om pa rison c om m a nds.

T he eql c om m a nd is sim ila r to the eq c om m a nd, but unlike eq, it a lso ha ndle s c om pa risons of num be rs a nd c ha ra c te rs:
;;comparing symbols
> (eql 'foo 'foo)
T

;;comparing numbers
> (eql 3.4 3.4)
T
;;comparing characters
> (eql #\a #\a)

T
T he equalp c om m a nd is e sse ntia lly the sa m e a s the equal c om m a nd, e xc e pt tha t it c a n ha ndle som e diffic ult c om pa rison c a se s
with a bit of e xtra sophistic a tion. For insta nc e , it c a n c om pa re strings with diffe re nt c a pita liz a tions a nd c a n c om pa re inte ge rs
a ga inst floa ting-point num be rs:
;;comparing strings with different CAPS
> (equalp "Bob Smith" "bob smith")
T
;;comparing integers against floating point numbers
> (equalp 0 0.0)
T
T he re m a ining c om pa rison c om m a nds a re just spe c ia liz a tions for spe c ific da ta type s. Othe rwise , the y a re sim ila r to equal. For
insta nc e , the = (e qua l sign) func tion ha ndle s num be rs, string-equal ha ndle s strings, a nd char-equal ha ndle s c ha ra c te rs.
I hope tha t you c a n now a ppre c ia te just how se riously L ispe rs ta ke c om pa risons.
W hat You've Le ar ne d
In this c ha pte r, we disc usse d how c onditions work in L isp. Along the wa y, you le a rne d the following:
T he va lue s nil, 'nil, (), a nd '() a re a ll ba sic a lly the sa m e thing in Com m on L isp.
L isp m a ke s it e a sy to c he c k for e m pty lists. T his m a ke s it sim ple to write list-e a te rs.
L isp c onditiona ls, suc h a s the if c om m a nd, c a use L isp c ode to be e va lua te d only unde r the right c onditions.
If you ne e d a c onditiona l c om m a nd tha t doe s e ve rything, the n you wa nt to use cond.
Com pa ring stuff in L isp is c om plic a te d, but you c a n ge t by if you just use eq for c om pa ring sym bols a nd equal for c om pa ring
e ve rything e lse .
Chapte r 5. Building a Te xt G ame Engine
W he n you write a progra m , no m a tte r whic h progra m m ing la ngua ge you’re using or wha t your progra m doe s, it will proba bly
ne e d to work with te xt. Sure , one da y we m a y a ll ha ve E the rne t ports a t the ba se of our skulls (100Mbps E the rne t will ha ve be e n
fully a dopte d by the n, of c ourse ). But until the da y a rrive s whe n you c a n just e xc ha nge thoughts with your Ma c Book using a dire c t
hookup, you’ll be stuc k using a lpha be tic te xt for input a nd output in your softwa re .
Com pute rs ha ve a lwa ys ha d a bit of a te nuous re la tionship with te xt. Although we te nd to think of te xt proc e ssing a s a c e ntra l
ta sk for c om pute r ha rdwa re a nd softwa re (inde e d, the 8-bit byte is the sta nda rd de sign e le m e nt in m ode rn c om pute rs, in la rge pa rt,
due to how we ll suite d it is for e nc oding W e ste rn c ha ra c te r se ts), the truth of the m a tte r is tha t the hum a n c onc e pt of te x t is re a lly
a lie n to a c om pute r.
In this c ha pte r, you’ll le a rn how to use L isp to m a nipula te te xt. You’ll se e , onc e a ga in, tha t the L ispy a pproa c h to solving
proble m s a llows you to c re a te c ode tha t is full of e le ga nc e a nd sym m e try. T o de m onstra te this a pproa c h, we will do som e thing tha t
would se e m to m a ke thinking with te xt una voida ble : build the e ngine for a sim ple te xt a dve nture ga m e . Howe ve r, we ’ll do this in
a wa y tha t a voids c onstra ining our c ode by a rtific ia lly forc ing the hum a n notion of te xt into its de sign. T his will a llow us to write
c ode tha t foc use s on the stre ngths of a c om pute r.
As you re a d this c ha pte r, re m e m be r tha t ha ndling te xt is not a c om pute r’s stre ngth. It is a ne c e ssa ry e vil be st ke pt to a
m inim um .
The W iz ar d's Adve ntur e G ame
In this ga m e , you a re a wiz a rd’s a ppre ntic e . You’ll e xplore the wiz a rd’s house . W he n we c om ple te the ga m e (in Cha pte r 17),
you’ll be a ble to solve puz z le s a nd win a m a gic a l donut.
O ur G ame W or ld
He re is a pic ture of our ga m e world:

As you c a n se e , we c a n visit thre e diffe re nt loc a tions: a living room , a n a ttic , a nd a ga rde n. Pla ye rs c a n m ove be twe e n pla c e s
using the door a nd the la dde r to the a ttic .
T hink of this ga m e world a s a sim ple dire c te d gra ph with thre e node s (re pre se nte d a s e llipse s) a nd four e dge s (re pre se nte d a s
a rrows):
Pla ye rs m ove be twe e n node s by tra ve ling a long the e dge s in e ithe r dire c tion. W he re ve r the pla ye rs a re , the y c a n inte ra c t with
va rious obje c ts a round the m .
Basic Re quir e me nts
Our ga m e c ode will ne e d to ha ndle a fe w ba sic things:
L ooking a round
W a lking to diffe re nt loc a tions
Pic king up obje c ts
Pe rform ing a c tions on the obje c ts pic ke d up
In this c ha pte r, we ’ll a ddre ss the first thre e of the se re quire m e nts. T o pe rform m ore c om ple x a c tions on obje c ts, we ’ll use the
m ore a dva nc e d L isp te c hnique s c ove re d in la te r c ha pte rs. Be c a use of this, our ga m e e ngine will be som e wha t lim ite d in its
a bilitie s until we finish it in Cha pte r 17.
W he n looking a round in our ga m e world, you will be a ble to “ se e ” thre e kinds of things from a ny loc a tion:

Ba sic sc e ne ry
One or m ore pa ths to othe r loc a tions
Obje c ts tha t you c a n pic k up a nd m a nipula te

L e t’s a dd fe a ture s for the se one a t a tim e .


De sc r ibing the Sc e ne r y with an Assoc iation List
T he world inside our a dve nture ga m e is ve ry sim ple , c onta ining only thre e loc a tions. L e t’s first c re a te a top-le ve l va ria ble ,
*nodes*, to c onta in de sc riptions of the loc a tions tha t e xist in our ga m e :
(defparameter *nodes* '((living-room (you are in the living-room.
a wizard is snoring loudly on the couch.))
(garden (you are in a beautiful garden.
there is a well in front of you.))
(attic (you are in the attic.
there is a giant welding torch in the corner.))))
T his va ria ble c onta ins a list a nd de sc ription of our thre e loc a tions. In e sse nc e , the *nodes* va ria ble ba sic a lly give s us a wa y to
find a pie c e of da ta a ssoc ia te d with a lookup ke y. In this c a se , the ke y is the na m e of the pla c e (living-room, garden, or attic),
a nd the da ta is a te xt de sc ription of the sc e ne ry a t tha t pla c e . T his type of struc ture is c a lle d a n assoc iation list, or alist for short
(a lists a re c ove re d in gre a te r de ta il in Cha pte r 7).
One thing is ra the r unusua l a bout the de finition of this *nodes* va ria ble : E ve n though it c onta ins de sc riptions of the va rious
loc a tions in our ga m e world, it doe s not a c tua lly c onta in a ny te xt strings. Sinc e Com m on L isp ha s a string da ta type , we c ould ha ve
writte n de sc riptions using quote s. For insta nc e , we c ould ha ve writte n "You are in a beautiful garden. There is a well in
front of you." Inste a d, we use m ore funda m e nta l da ta type s— sym bols a nd lists— to e nc ode this inform a tion.
W hy wouldn’t we just use strings? As I m e ntione d a t the be ginning of this c ha pte r, the m a nipula tion of te xt is not re a lly a
funda m e nta l c om puting c onc e pt. In this ga m e , we ’ll m a nipula te the m e ssa ge s displa ye d to pla ye rs ba se d on the ir inte ra c tion with
the ga m e world in c om plic a te d wa ys. For m ost re a l-world progra m s, the inform a tion you’ll ge ne ra te a s output (suc h a s HT ML ,
PDFs, or e ve n ric he r gra phic a l form a ts) will proba bly be fa r m ore c om plic a te d tha n just sim ple te xt.
By ke e ping your sourc e da ta struc ture s fre e from a ssum ptions re ga rding the output form a t from the sta rt, your c oding c a n ta ke
full a dva nta ge of your progra m m ing la ngua ge . Sinc e the e a sie st things to m a nipula te in L isp a re sym bols a nd lists, m ost
e xpe rie nc e d L ispe rs will try to foc us on the se da ta type s in the de sign of the ir softwa re whe ne ve r possible . So, we will sta y a wa y
from strings in our de sign. (In the ne xt c ha pte r, we will tra nsla te the se lists a nd sym bols into prope rly form a tte d te xt. )

Note

Com m on L isp doe sn’t forc e you to re pre se nt strings with lists a nd sym bols in this wa y. If it’s m ore c onve nie nt, you c a n work with
strings dire c tly. (You’ll se e e xa m ple s of working with strings la te r in the book, e spe c ia lly in Cha pte r 11. ) Using lists a nd sym bols a s
a n inte rm e dia ry for m a nipula ting te xt is de finite ly a n old-sc hool L isp te c hnique . Howe ve r, it c a n ofte n le a d to ve ry e le ga nt c ode ,
sinc e list ope ra tions a re so funda m e nta l to L isp.
De sc r ibing the Loc ation
Now tha t we ’ve c re a te d a n a list of our ga m e world, we ne e d to c re a te a c om m a nd to de sc ribe a loc a tion. T o a c c om plish this,
we ’ll use the assoc func tion to find the c orre c t ite m in the list using a ke y:
> (assoc 'garden *nodes*)
(GARDEN (YOU ARE IN A BEAUTIFUL GARDEN. THERE IS A WELL IN FRONT OF YOU.))
Using assoc, we c a n e a sily c re a te the describe-location func tion:
(defun describe-location (location nodes)
(cadr (assoc location nodes)))
T o use this func tion, we pa ss in a loc a tion a nd the *nodes* list:
> (describe-location 'living-room *nodes*)
(YOU ARE IN THE LIVING-ROOM. A WIZARD IS SNORING LOUDLY ON THE COUCH.)
W hy don’t we just re fe re nc e the *nodes* va ria ble dire c tly from the describe-location func tion? Be c a use this func tion is
writte n in the func tional programming style . In this style , a func tion will re fe re nc e only pa ra m e te rs or va ria ble s de c la re d in the
func tion itse lf, a nd it will do nothing be side s re turn a va lue , whic h is the de sc ription of the loc a tion in this c a se .
By writing func tions tha t don’t re fe re nc e va ria ble s in the “ outside world” dire c tly a nd tha t don’t pe rform a ny a c tions othe r tha n
re turning a va lue , you c a n write c ode tha t c a n e a sily be te ste d in isola tion. You should try to write your L isp func tions in this style
whe ne ve r possible . (W e will disc uss the func tiona l progra m m ing style in gre a te r de ta il in Cha pte r 14. )
De sc r ibing the P aths
Now tha t we ha ve ba sic de sc riptions of e a c h loc a tion, we ne e d de sc riptions of pa ths to othe r loc a tions a s we ll. W e ’ll c re a te a
se c ond va ria ble , *edges*, tha t c onta ins the pa ths tha t pla ye rs c a n ta ke to m ove be twe e n pla c e s on our m a p. (W e use the te rm
e dge s be c a use tha t’s the prope r m a th te rm for the line s c onne c ting node s in a gra ph. )
(defparameter *edges* '((living-room (garden west door)
(attic upstairs ladder))
(garden (living-room east door))
(attic (living-room downstairs ladder))))
Using this struc ture , we c re a te the describe-path func tion, whic h builds a te xtua l de sc ription of a give n e dge using our sym bols
syste m .
(defun describe-path (edge)
`(there is a ,(caddr edge) going ,(cadr edge) from here.))
T his describe-path func tion looks pre tty stra nge — a lm ost like a pie c e of da ta m ore tha n a func tion. L e t’s try it, a nd the n figure
out how it works.
> (describe-path '(garden west door))
(THERE IS A DOOR GOING WEST FROM HERE.)
T his func tion ba sic a lly re turns a pie c e of da ta with sm a ll bits of c a lc ula te d inform a tion inse rte d into it. T his fe a ture of L isp,
c a lle d quasiquoting, a llows us to c re a te c hunks of da ta tha t ha ve sm a ll pie c e s of L isp c ode e m be dde d in the m .
H ow Q uasiquoting W or ks
T o e na ble qua siquoting, you m ust use a ba c kquote [`] not a single quote ['] whe n switc hing from c ode to da ta m ode . T he
describe-path func tion ha s just suc h a ba c kquote in it.
Both the single quote a nd ba c kquote in L isp “ flip” a pie c e of c ode into da ta m ode , but only a ba c kquote c a n a lso be unquote d
using the c om m a c ha ra c te r, to flip ba c k into c ode m ode .
W ith a little im a gina tion, this should m a ke se nse to you. Afte r a ll, a c om m a doe s look just like a n upside -down ba c kquote ,
doe sn’t it? He re ’s how the flip-flop in the describe-path func tion works (the pa rts in c ode m ode a re sha de d):

L isp a tte m pts to m a ke list m a nipula tion a s e a sy a s possible . He re , you c a n se e how our progra m , whic h use s lists of sym bols to
store our te xt, c a n now le ve ra ge the qua siquoting fe a ture to c onstruc t se nte nc e s in a ve ry c onc ise a nd c le a r wa y.
De sc r ibing M ultiple P aths at O nc e
Now le t’s use our describe-path func tion to c re a te a m ore a dva nc e d func tion. Sinc e a loc a tion m a y ha ve a ny num be r of pa ths
e xiting from it, we ne e d a func tion tha t c a n ge ne ra te de sc riptions for a ll e dge s from a give n loc a tion by looking up the loc a tion
from our da ta struc ture of e dge s:
(defun describe-paths (location edges)
(apply #'append (mapcar #'describe-path (cdr (assoc location edges)))))
T his func tion use s a bunc h of c om m a nds tha t m a y se e m ve ry e xotic to a pe rson not a c c ustom e d to the world of L isp. Ma ny
progra m m ing la ngua ge s would use som e kind of for-ne xt loop to run through the e dge s, a nd the n c ra m the de sc riptions of e a c h pa th
toge the r using a te m pora ry va ria ble . L isp use s a m uc h m ore e le ga nt a pproa c h. L e t’s se e it in a c tion:
> (describe-paths 'living-room *edges*)
(THERE IS A DOOR GOING WEST FROM HERE. THERE IS A LADDER GOING UPSTAIRS FROM HERE.)
T he describe-paths func tion ta ke s the following ste ps:

1. Find the re le va nt e dge s.


2. Conve rt the e dge s to de sc riptions.
3. Join the de sc riptions.

L e t’s se e how it pe rform s e a c h of the se ste ps.

F inding the Re le vant Edge s

T he first, inne r pa rt of the describe-paths func tion is pre tty stra ightforwa rd. T o find the re le va nt pa ths a nd e dge s le a ding from
the living room , we use assoc a ga in to look up the loc a tion in our list of e dge s:
> (cdr (assoc 'living-room *edges*))
((GARDEN WEST DOOR) (ATTIC UPSTAIRS LADDER))

Conve r ting the Edge s to De sc r iptions

Ne xt, the e dge s a re c onve rte d to de sc riptions. He re is just the c ode to a c c om plish this, shown in isola tion:
> (mapcar #'describe-path '((GARDEN WEST DOOR) (ATTIC UPSTAIRS LADDER)))
((THERE IS A DOOR GOING WEST FROM HERE.)
(THERE IS A LADDER GOING UPSTAIRS FROM HERE.))
T he mapcar func tion is use d fre que ntly by L ispe rs. T his func tion ta ke s a nothe r func tion a nd a list, a nd the n a pplie s this func tion
to e ve ry m e m be r of a list. He re ’s a n e xa m ple :
> (mapcar #'sqrt '(1 2 3 4 5))
(1 1.4142135 1.7320508 2 2.236068)
T his e xa m ple pa sse s the sqrt (squa re root) func tion, a long with the (1 2 3 4 5) list, into mapcar. As a re sult, the func tion
ge ne ra te s a list of the squa re roots of the origina l num be rs by a pplying sqrt to e ve ry m e m be r of the list a nd c re a ting a ne w list.
Func tions tha t ta ke othe r func tions a s pa ra m e te rs, suc h a s mapcar, a re ve ry use ful a nd a distinguishing fe a ture of L isp. Suc h
func tions a re c a lle d highe r-orde r func tions.
He re is a nothe r e xa m ple :
> (mapcar #'car '((foo bar) (baz qux)))
(foo baz)
T his tim e , our sourc e list c onta ins two sm a lle r lists. T he car func tion, whic h gra bs the first ite m in a list, c a use s mapcar to
re turn the first ite m s from e a c h sm a lle r list, foo a nd baz.
You m a y be wonde ring why the func tion na m e s we pa ss into mapcar ha ve the #' sym bols in front of the m . T his sym bol se que nc e
is a shortha nd for the function ope ra tor. T he L isp re a de r (the pa rt of your L isp e nvironm e nt tha t re a ds the c ode you type ) will
c onve rt the pre vious e xa m ple into the following longe r ve rsion:
> (mapcar (function car) '((foo bar) (baz qux)))
(foo baz)
Com m on L isp re quire s you to use the function ope ra tor whe n re fe rring to a func tion a s a va lue dire c tly like this, be c a use the
na m e of a func tion m a y c onflic t with othe r na m e d ite m s in a progra m , c a using unpre dic ta ble e rrors. For insta nc e , im a gine if we
a dde d m ore stuff to the pre vious e xa m ple , like this:

> (let ((car "Honda Civic"))

(mapcar #'car '((foo bar) (baz qux))))


(foo baz)
In this ve rsion, the car sym bol c ould ha ve two diffe re nt m e a nings. T he first m e a ning of c a r is tha t it is a sta nda rd func tion built

into L isp (introduc e d in Cha pte r 3). Howe ve r, we ’re a lso c re a ting a loc a l va ria ble na m e d car . Be c a use we pre pe nde d the

word car with #' in our c a ll to mapcar , the re is no c onfusion a bout whic h car we a re ta lking a bout.
Now le t’s look a t the describe-paths func tion a ga in:
(defun describe-paths (location edges)
(apply #'append (mapcar #'describe-path (cdr (assoc location edges)))))
Notic e how the append a nd describe-path func tions a re pa sse d in a s va lue s to the apply a nd mapcar func tions, whic h a re
de signe d to re c e ive a nd use the func tions.
Com m on L isp tra c ks func tion na m e s diffe re ntly from va ria ble na m e s. It ha s m ultiple name spac e s, inc luding one for va ria ble s a nd
one for func tions. (W e ’ll le a rn m ore a bout na m e spa c e s la te r, e spe c ia lly in Cha pte r 16. ) Sc he m e , the othe r popula r L isp dia le c t,
doe sn’t forc e you to m a rk func tions with a func tion ope ra tor whe n using the m a s va lue s.
In othe r words, Sc he m e ha s only one na m e spa c e for both func tions a nd va ria ble s. For insta nc e , in Sc he m e , you c a n just write
(map sqrt '(1 2 3 4 5)) to ge ne ra te the squa re roots of the num be rs 1 through 5 without ge ne ra ting a n e rror (map is the Sc he m e
ve rsion of mapcar). As a re sult of this de sign, in Sc he m e , a va ria ble a nd a se pa ra te func tion c a n’t be a va ila ble in the sa m e bloc k of
c ode . T ha t de sign de c ision is one of the gre a t be ne fits (or c urse s) of Sc he m e , de pe nding on your point of vie w. Be c a use of this
diffe re nc e in the num be r of na m e spa c e s, Sc he m e is som e tim e s c a lle d a Lisp-1, whe re a s Com m on L isp is som e tim e s re fe rre d to a s a
Lisp-2.

Joining the De sc r iptions

Onc e we ’ve use d mapcar to ge ne ra te a list of de sc riptions for a ll the pa ths a nd e dge s, we ne e d to c om bine the m into a single
de sc ription. W e a c c om plish this with the append func tion, whic h joins se ve ra l lists into one big list:
> (append '(mary had) '(a) '(little lamb))
(MARY HAD A LITTLE LAMB)
W e use the append func tion to c ra m the list of pa th de sc riptions into one list tha t de sc ribe s the whole e nc hila da , in one swoop.
T he proble m is tha t append ne e ds a ll of the lists ha nde d to it a s se pa ra te pa ra m e te rs. In describe-paths, we ha ve our lists in one
big list, not a s se pa ra te obje c ts we c a n pa ss a s pa ra m e te rs. He c k, we don’t e ve n know how m a ny pa ths the re m a y be from a ny give n
spot.
T he apply func tion solve s this proble m . You pa ss it a func tion a nd a list of obje c ts, a nd it pre te nds tha t the ite m s in the list a re
se pa ra te obje c ts a nd pa sse s the m to the give n func tion a s suc h. For e xa m ple , if we ha ve the ne ste d list '((mary had) (a) (little
lamb)), the apply func tion will a dd in tha t little bit of duc t ta pe ne e de d to m a ke the append func tion work with a single big list:
> (apply #'append '((mary had) (a) (little lamb)))
(MARY HAD A LITTLE LAMB)

W ar ning

Sinc e the apply func tion pa sse s e a c h ite m in a list a s a n a rgum e nt to the target func tion, you c a n run into proble m s whe n
c a lling it on ve ry la rge lists tha t ha ve thousa nds of ite m s or m ore . You c a n c he c k the va lue of the call-arguments-limit va ria ble
in the RE PL to se e the m a xim um num be r of a llowe d a rgum e nts to a func tion. (More re c e nt dia le c ts of L isp a re typic a lly de signe d
to a llow a rgum e nt lists of a ny siz e , without a n a rtific ia l lim it. )

You c a n se e how apply e na ble s the describe-paths func tion to build one long list de sc ribing a ll pa ths le a ding from a single
loc a tion. L e t’s use this sa m e a pproa c h on the pa th de sc ription lists we c onstruc te d:
> (apply #'append '((THERE IS A DOOR GOING WEST FROM HERE.)
(THERE IS A LADDER GOING UPSTAIRS FROM HERE.)))
(THERE IS A DOOR GOING WEST FROM HERE. THERE IS A LADDER GOING UPSTAIRS FROM HERE.)
Now tha t we ’ve looke d a t e a c h pa rt of the describe-paths func tion, le t’s re vie w how it works:
(defun describe-paths (location edges)
(apply #'append (mapcar #'describe-path (cdr (assoc location edges)))))
T he func tion ta ke s two pa ra m e te rs: the c urre nt pla ye r’s loc a tion, a s we ll a s a n a list of e dge s/pa ths for the ga m e m a p. First, it
use s assoc to look up the c orre c t loc a tion from the e dge a list. Sinc e assoc re turns both the ke y a nd the va lue from the a list, we
c a ll cdr to re trie ve only the va lue . Ne xt, we use mapcar to m a p the describe-path func tion a ga inst e a c h e dge tha t we found.
Fina lly, we c onc a te na te the lists for de sc ribing a ll the pa ths into one long list by a pplying append a ga inst the list.
T he progra m m ing style use d by describe-path is ve ry typic a l for L isp c ode . It involve s pa ssing a long a c om plic a te d c hunk of
da ta a nd m a nipula ting it in se ve ra l ste ps, ofte n using highe r-orde r func tions. T o be c om e a profic ie nt L isp progra m m e r, you should
try to ge t c om forta ble re a ding c ode writte n in this wa y.
De sc r ibing O bje c ts at a Spe c ific Loc ation
T o c re a te the fina l pie c e of c ode to he lp us visua liz e our ga m e world, we ne e d to de sc ribe the obje c ts on the floor a t a give n
loc a tion, whic h a pla ye r c a n pic k up a nd use .
Listing Visible O bje c ts
T o do so, we first c re a te a list of the obje c ts:
> (defparameter *objects* '(whiskey bucket frog chain))
*OBJECTS*

W e c a n a lso c re a te a se c ond va ria ble , *object-locations*, to tra c k the loc a tion of e a c h obje c t in the form of a n a list:
(defparameter *object-locations* '((whiskey living-room)
(bucket living-room)
(chain garden)
(frog garden)))
Ne xt, we write a func tion tha t lists the obje c ts visible from a give n loc a tion:
(defun objects-at (loc objs obj-locs)

(labels ((at-loc-p (obj)

(eq (cadr (assoc obj obj-locs)) loc)))

(remove-if-not #'at-loc-p objs)))

T h i s objects-at func tion de c la re s a ne w func tion na m e d at-loc-p using the labels c om m a nd . (Re m e m be r tha t the
labels func tion a llows you to de fine func tions loc a lly. ) Sinc e the at-loc-p func tion won’t be use d e lse whe re , we c a n just de c la re
it dire c tly within objects-at, hiding it from the re st of the c ode in our progra m .
T he at-loc-p func tion ta ke s the sym bol for a n obje c t a nd re turns t or nil, de pe nding on whe the r tha t obje c t e xists a t the
loc a tion loc. It doe s this by looking up the obje c t in the obj-locs a list. T he n, it use s eq to se e whe the r the loc a tion it finds

m a tc he s the loc a tion in que stion .


W hy did we na m e this func tion at-loc-p? W he n a func tion re turns nil or a truth va lue , it’s a Com m on L isp c onve ntion to
a ppe nd a p to the e nd of tha t func tion’s na m e . For insta nc e , you c a n c he c k tha t the num be r 5 is odd by c a lling (oddp 5). Suc h
true /fa lse func tions a re c a lle d pre dic ate s, whic h is why we use the le tte r p.

T he remove-if-not func tion in the la st line of the listing , a s you m ight e xpe c t, re m ove s a ll things from a list for whic h a
pa sse d-in func tion (in this c a se , at-loc-p) doe sn’t re turn true . E sse ntia lly, it re turns a filte re d list of obje c ts c onsisting of those
ite m s for whic h at-loc-p is true .
He re ’s wha t object-at looks like in a c tion:
> (objects-at 'living-room *objects* *object-locations*)
(WHISKEY BUCKET)
De sc r ibing Visible O bje c ts
Now we c a n write a func tion to de sc ribe the obje c ts visible a t a give n loc a tion:
(defun describe-objects (loc objs obj-loc)

(labels ((describe-obj (obj)

`(you see a ,obj on the floor.)))

(apply #'append (mapcar #'describe-obj (objects-at loc objs obj-loc)))))

In this listing, describe-objects first c re a te s the describe-obj func tion . T his func tion ge ne ra te s a pre tty se nte nc e sta ting

tha t a give n obje c t is on the floor, using qua siquoting . T he m a in pa rt of the func tion c onsists of c a lling objects-at to find
the obje c ts a t the c urre nt loc a tion, m a pping describe-obj a c ross this list of obje c ts, a nd fina lly a ppe nding the de sc riptions into a

single list .
L e t’s try running describe-objects:
> (describe-objects 'living-room *objects* *object-locations*)
(YOU SEE A WHISKEY ON THE FLOOR. YOU SEE A BUCKET ON THE FLOOR)
Pe rfe c t!
De sc r ibing It All
Now we ’ll tie a ll of the se de sc ription func tions into one e a sy c om m a nd c a lle d look. Be c a use this will be the a c tua l c om m a nd
pla ye rs c a n e nte r to look a round the m in the ga m e , look will ne e d to know a pla ye r’s c urre nt loc a tion. So, we ne e d a va ria ble to
tra c k the pla ye r’s c urre nt position. L e t’s c a ll it *location*:
(defparameter *location* 'living-room)
Be c a use the *location* va lue is initia liz e d to the living-room sym bol, whic h oc c urs a t the ve ry sta rt of the ga m e , pla ye rs will
find the m se lve s in the living room of the wiz a rd’s house . At this point, we c a n write a look func tion to de sc ribe e ve rything we
ne e d by ha ving it c a ll a ll of our de sc riptor func tions:
(defun look ()
(append (describe-location *location* *nodes*)
(describe-paths *location* *edges*)
(describe-objects *location* *objects* *object-locations*)))
Sinc e the look func tion use s globa l va ria ble na m e s (suc h a s *location*, *nodes*, a nd so on), the pla ye r won’t ne e d to pa ss in
a ny funky va lue s in orde r to look out a t the world. Howe ve r, this a lso m e a ns tha t the look func tion is not in the func tiona l
progra m m ing style , be c a use func tions in the func tiona l progra m m ing style re fe re nc e only pa ra m e te rs or va ria ble s de c la re d in the
func tion itse lf. *location* a nd its ilk a re globa l va ria ble s, so the look func tion doe sn’t hold up m uste r.
Sinc e the pla ye r’s loc a tion c ha nge s a s the ga m e progre sse s, look will do diffe re nt things at diffe re nt time s in the ga m e . In othe r
words, the things you se e whe n looking a round will c ha nge de pe nding on your loc a tion. In c ontra st, a func tion in the func tiona l
progra m m ing style a lwa ys re turns the sa m e re sult, a s long a s the sa m e va lue s a re give n a s pa ra m e te rs. T he e a rlie r func tions we
c re a te d, suc h a s describe-location, describe-paths, a nd describe-objects, a lwa ys re turn the sa m e thing, no m a tte r whe n the y
a re c a lle d, as long as the ir parame te rs are k e pt the same .
Now he re ’s wha t we se e whe n we use look:
> (look)
(YOU ARE IN THE LIVING-ROOM OF A WIZARD’S HOUSE.
THERE IS A WIZARD SNORING LOUDLY ON THE COUCH.
THERE IS A DOOR GOING WEST FROM HERE.
THERE IS A LADDER GOING UPSTAIRS FROM HERE.
YOU SEE A WHISKEY ON THE FLOOR.
YOU SEE A BUCKET ON THE FLOOR)
W alking Ar ound in O ur W or ld
Now tha t we c a n se e things in our world, le t’s write som e c ode so tha t we c a n wa lk a round. T he walk func tion (not in the
func tiona l style ) ta ke s a dire c tion a nd le ts us wa lk the re :
(defun walk (direction)

(let ((next (find direction

(cdr (assoc *location* *edges*))

:key #'cadr)))

(if next

(progn (setf *location* (car next))

(look))

'(you cannot go that way.))))

First, this func tion looks up the a va ila ble wa lking pa ths in the *edges* ta ble , using the c urre nt loc a tion . T his is use d by the

find func tion to loc a te the pa th m a rke d with the a ppropria te dire c tion . (find se a rc he s a list for a n ite m , the n re turns tha t
found ite m . ) T he direction (suc h a s west, upstairs, a nd so on) will be in the cadr of e a c h pa th, so we ne e d to te ll find to m a tc h
the direction a ga inst the cadr of a ll the pa ths in the list.

W e c a n do this by pa ssing find a k e y word parame te r . In Com m on L isp, m a ny func tions (suc h a s find) ha ve built-in
fe a ture s tha t c a n be a c c e sse d by pa ssing in spe c ia l pa ra m e te rs a t the e nd of the func tion c a ll. For insta nc e , the following c ode finds
the first ite m in a list tha t ha s the sym bol y in the cadr loc a tion:
> (find 'y '((5 x) (3 y) (7 z)) :key #'cadr)
(3 Y)
A ke yword pa ra m e te r ha s two pa rts:

T he first is the na m e (in this c a se :key), whic h be gins with a c olon. (W e ’ll disc uss the m e a ning of this c olon in
m ore de ta il in Cha pte r 7. )
T he se c ond is the va lue , whic h in this c a se is #'cadr.

W e use ke yword pa ra m e te rs the sa m e wa y in our walk func tion to find the prope r pa th ba se d on the give n dire c tion.

Onc e we ha ve the c orre c t pa th, we store the re sult in the va ria ble next . T he if e xpre ssion the n c he c ks whe the r next ha s a

va lue (the next va ria ble isn’t nil). If next ha s a va lue , if a djusts the pla ye r’s position be c a use this is a va lid dire c tion

. T he c a ll to look re trie ve s the de sc ription for the ne w loc a tion a nd re turns it a s a va lue . If the pla ye r c hoose s a n

inva lid dire c tion, look will ge ne ra te a n a dm onishm e nt inste a d of a ne w de sc ription .


He re ’s wha t our walk func tion looks like now:
> (walk 'west)
(YOU ARE IN A BEAUTIFUL GARDEN.
THERE IS A WELL IN FRONT OF YOU.
THERE IS A DOOR GOING EAST FROM HERE.
YOU SEE A CHAIN ON THE FLOOR.
YOU SEE A FROG ON THE FLOOR.)
T he re ’s a quote in front of the dire c tion, sinc e the dire c tion na m e ne e ds to be writte n in da ta m ode . It’s kind of a wkwa rd to
forc e a pla ye r to put a quote in a ga m e c om m a nd, but the inte rfa c e we a re c re a ting now is inte nde d for e a sy de bugging a nd
de ve lopm e nt. He c k, it’s a lm ost not e ve n worth c a lling a n “ inte rfa c e , ” sinc e we just e nte r the ga m e c om m a nds dire c tly into the
RE PL . In the ne xt c ha pte r, we ’ll c re a te a m uc h nic e r inte rfa c e using a c ustom RE PL de signe d for pla ying te xt ga m e s tha t will
ta ke c a re of this wa rt.

Note

You c ould use L isp mac ros to c re a te a c om m a nd in a va nilla L isp RE PL tha t doe sn’t re quire the quote in front of the dire c tion,
so tha t you c ould just write (walk west), for insta nc e . You’ll le a rn m ore a bout m a c ros in Cha pte r 16.
P ic king Up O bje c ts
Ne xt, le t’s c re a te a c om m a nd to pic k up obje c ts in our world. T o do so, we m odify the va ria ble *object-locations* tha t we ’re
using to tra c k the loc a tion of obje c ts:
(defun pickup (object)

(cond ((member object

(objects-at *location* *objects* *object-locations*))

(push (list object 'body) *object-locations*)


`(you are now carrying the ,object))
(t '(you cannot get that.))))

T he pickup func tion use s the member func tion to se e if the object is inde e d on the floor of the c urre nt loc a tion. (T he

member func tion c he c ks to se e if a pa rtic ula r ite m is found in a list of ite m s. ) W e use the objects-at c om m a nd to ge ne ra te
the lists of obje c ts a t the c urre nt loc a tion.

If the obje c t is a t the c urre nt loc a tion, we use the push c om m a nd to push a ne w ite m onto the *object-locations* list,
c onsisting of the ite m a nd its ne w loc a tion. T he ne w loc a tion will just be body, for the pla ye r’s body.

T he push c om m a nd sim ply a dds a ne w ite m to the front of a list va ria ble ’s list. For e xa m ple , the following e xa m ple a dds
the num be r 7 to the list 1 2 3:
> (defparameter *foo* '(1 2 3))
*FOO*
> (push 7 *foo*)
(7 1 2 3)
> *foo*
(7 1 2 3)
T h i s push c om m a nd is ba sic a lly a c onve nie nc e func tion built on top of setf. For e xa m ple , we c ould ha ve re pla c e d the
pre c e ding push c om m a nd with (setf *foo* (cons 7 *foo*)) a nd obta ine d the sa m e re sult. It’s just e a sie r to use push.
Pushing a ne w loc a tion for a n obje c t onto our *object-locations* a list doe s se e m a bit odd. Sinc e we ’re ne ve r re m oving old
loc a tions for obje c ts, just pushing ne w one s, it m e a ns tha t *object-locations* m a y c onta in m ultiple e ntrie s for a single obje c t,
a nd tha t this list now ha s two store d loc a tions for the obje c t in que stion. Fortuna te ly, the assoc c om m a nd, whic h we use to find
obje c ts in a give n loc a tion (within the objects-at c om m a nd), a lwa ys re turns the first ite m it finds in a list. T he re fore , using the
push c om m a nd m a ke s the assoc c om m a nd be ha ve a s if the va lue in the list for a give n ke y ha s be e n re pla c e d a ltoge the r.
Using the push a nd assoc c om m a nds toge the r in this wa y a llows us to pre te nd tha t va lue s in a n a list a re c ha nging, while still
pre se rving old va lue s. Old va lue s a re sim ply suppre sse d by ne we r va lue s, thus pre se rving a history of a ll old va lue s. T he push/assoc
idiom is a c om m on te c hnique use d by L ispe rs.
Now le t’s wa lk ba c k to the living room a nd try to pic k up a n obje c t:
> (walk 'east)
(YOU ARE IN THE LIVING-ROOM OF A WIZARDS HOUSE. THERE IS A WIZARD SNORING
LOUDLY ON THE COUCH. THERE IS A DOOR GOING WEST FROM HERE. THERE IS A LADDER
GOING UPSTAIRS FROM HERE. YOU SEE A WHISKEY ON THE FLOOR. YOU SEE A BUCKET ON
THE FLOOR.)
> (pickup 'whiskey)
(YOU ARE NOW CARRYING THE WHISKEY)
It worke d. W e ’re c a rrying the whiske y, whic h m e a ns tha t we c a n now pic k up things in our world!
Che c king O ur Inve ntor y
Fina lly, le t’s c re a te a func tion tha t le ts pla ye rs se e a n inve ntory of obje c ts the y a re c a rrying:
(defun inventory ()
(cons 'items- (objects-at 'body *objects* *object-locations*)))
T his inve ntory func tion use s the objects-at func tion to re trie ve a list of obje c ts a t a re que ste d loc a tion. W ha t loc a tion doe s it
se a rc h for? If you re m e m be r, whe n a n obje c t wa s pic ke d up by the pla ye r, we c ha nge d its loc a tion to 'body: T his is the loc a tion
we now use to que ry.
L e t’s try out this inventory func tion:
> (inventory)
(ITEMS-WHISKEY)
As you c a n se e , we a re c a rrying only one ite m right now: the whiske y bottle we just pic ke d up.
T he re you ha ve it! W e now ha ve a ba sic e ngine for a te xt a dve nture ga m e . W e c a n look a round the world with look; wa lk
be twe e n pla c e s with walk; pic k up obje c ts with pickup; a nd c he c k our inve ntory with inventory.
Of c ourse , we don’t re a lly ha ve m uc h of a ga m e , sinc e we c a n’t do a nything with the obje c ts we find. W e ’ll a dd a m e c ha nism
for a c tua lly m a nipula ting obje c ts in Cha pte r 17. In the ne xt c ha pte r, we ’ll foc us on im proving our ga m e ’s use r inte rfa c e . E ve n
though the RE PL is pe rfe c t for prototyping our ga m e , a dding a c ustom te xt ga m e inte rfa c e will m a ke the ga m e pla y m ore se a m le ss
for the pla ye r.
W hat You've Le ar ne d
In this c ha pte r, we put toge the r a sim ple e ngine for a te xt a dve nture ga m e . Along the wa y, you le a rne d the following:
A ga m e world c a n be re pre se nte d by a m a the m a tic a l gra ph, c onsisting of node s for the pla c e s the pla ye r c a n visit a nd e dge s for
the pa ths be twe e n the se pla c e s.
You c a n store the se node s in a n assoc iation list (alist) c a lle d *nodes*. T his alist a llows you to look up prope rtie s of a
node /pla c e by using its na m e . In the c a se of our ga m e , the prope rty we ’re storing is a de sc ription of e a c h node /pla c e .
You use the a ssoc func tion to look up a ke y (loc a tion na m e in our e xa m ple ) in a n a list.
Quasiquoting is a te c hnique tha t a llows you to inse rt sm a ll bits of c om pute r c ode into la rge r pie c e s of da ta .
Som e L isp func tions a c c e pt othe r func tions a s a rgum e nts. T he se a re c a lle d highe r-orde r func tions. T he mapcar func tion is the
m ost popula r highe r-orde r func tion in Com m on L isp.
T o re pla c e a va lue from a n a list, you push ne w ite m s onto the list. Only the m ost re c e nt va lue will be re porte d by the assoc
func tion.
Chapte r 6. Inte r ac ting with the W or ld: Re ading and P r inting in Lisp
So fa r, we ha ve n’t writte n a ny c ode tha t dire c tly inte ra c ts with the outside world. Inste a d, a ll the re sults ge ne ra te d by c om m a nds
a re just re turne d a s va lue s, whic h we c a n se e by c a lling func tions from our L isp RE PL .
Howe ve r, c ode c a n’t just spe nd its whole life sitting in a bla c k box. At som e point, it’s going to ne e d to inte ra c t with the world,
so it will ne e d a use r inte rfa c e . L uc kily, L isp ha s m uc h to offe r to he lp you c re a te use r inte rfa c e s. T he re a re m a ny gra phic a l use r
inte rfa c e libra rie s for diffe re nt fla vors of Com m on L isp, a s we ll a s libra rie s for building we b inte rfa c e s. In fa c t, we ’ll be building
our own toy we b inte rfa c e in Cha pte r 13.
In this c ha pte r, we ’ll foc us on the m ost ba sic of a ll use r inte rfa c e s, the c ommand-line inte rfac e .
P r inting and Re ading Te xt
For a c om m a nd-line inte rfa c e , we ne e d c om m a nds tha t c a n dire c tly print te xt from the sc re e n a nd re a d in te xt e nte re d by the
use r. T he two c om m a nds tha t do this a re , a ppropria te ly e nough, print a nd read. As you m ight e xpe c t by now, the re is a lot of
sym m e try be twe e n the se two c om m a nds.
P r inting to the Sc r e e n
T he print func tion sim ply le ts you print stuff to the c onsole :
> (print "foo")

"foo"

"foo"

Don’t ge t c onfuse d by the fa c t tha t c a lling the print func tion c a use d "foo" to be printe d twic e . T he first "foo" is wha t

the print func tion ac tually printe d. T he se c ond "foo" is the re be c a use , a s you know, the RE PL a lwa ys prints the va lue of
a ny e xpre ssion tha t is e nte re d. It so ha ppe ns tha t the va lue of (print "foo") is "foo", c a using the word to be shown twic e . In the
e xa m ple s tha t follow in this c ha pte r, I’ll typic a lly om it this e xtra fina l va lue printe d by the RE PL , just to a void c onfusion.
T he print func tion is a n e a sy wa y to print a L isp va lue to the sc re e n. Howe ve r, a dva nc e d L ispe rs ofte n fa vor a re la te d func tion
c a lle d prin1. T o unde rsta nd the diffe re nc e , le t’s try both of the se func tions out in the RE PL :
> (progn (print "this")
(print "is")
(print "a")
(print "test"))
"this"
"is"
"a"
"test"
T he print func tion c a use s e a c h ite m to be printe d on a se pa ra te line . Now, le t’s try prin1:
> (progn (prin1 "this")
(prin1 "is")
(prin1 "a")
(prin1 "test"))
"this""is""a""test"
As you c a n se e , prin1 doe s not put the printe d ite m s on se pa ra te line s. T o be pre c ise , the print a nd prin1 c om m a nds a re the
sa m e in e ve ry wa y, e xc e pt tha t print will sta rt a ne w line be fore printing a va lue . Additiona lly, print pla c e s a spa c e c ha ra c te r a t
the e nd of the printe d va lue .
Be c a use prin1 doe s le ss, it is re a lly a sim ple r, m ore funda m e nta l func tion. It is m ore fle xible a nd, the re fore , is c om m only use d
in m ore se rious L isp c ode . W e ’ll use the print func tion m ore fre que ntly in this book, but you should be a wa re of the prin1
c om m a nd, a s we ll.
Saying H e llo to the Use r
T he following e xa m ple is a sim ple func tion, say-hello, tha t you c a n c a ll from your L isp prom pt. It a sks use rs for the ir na m e
a nd re sponds with a gre e ting. Whe n y ou run the program, be sure to ty pe quote s around y our name , e v e n if this may se e m odd.
> (defun say-hello ()

(print "Please type your name:")

(let ((name (read)))

(print "Nice to meet you, ")

(print name)))
SAY-HELLO.
> (say-hello)
"Please type your name:" "bob"
"Nice to meet you,"
"bob"

In the first line of the say-hello func tion, we print a m e ssa ge a sking use rs for the ir na m e . Ne xt, we de fine a loc a l

va ria ble c a lle d name, whic h is se t to the va lue re turne d by the read func tion . T he read func tion will c a use L isp to wa it for
the use r to type in som e thing a t the RE PL . Only a fte r the use r ha s type d som e thing in a t the prom pt a nd pre sse d e nte r will the

va ria ble name be se t to the re sult. Onc e we know the use r’s na m e , a pe rsona liz e d m e ssa ge is printe d, gre e ting the use r .
As you c a n se e from this sim ple func tion, the print a nd read func tions do (a lm ost) e xa c tly wha t you would e xpe c t. T he print
func tion prints som e thing on the sc re e n. T he read func tion le ts the use r e nte r som e thing into the progra m . Howe ve r, the re is one
gla ring idiosync ra sy in the se func tions: E ve ry va lue displa ye d a nd e nte re d is surrounde d by quota tion m a rks.
Star ting with pr int and r e ad
W he n you ne e d to print som e thing on the sc re e n, you should first think of the print c om m a nd. If you ne e d to re a d som e thing in,
you should first think of the read c om m a nd. Othe r printing c om m a nds le t you c re a te the pre vious e xa m ple without ha ving
supe rfluous quote s, but whe ne ve r you ha ve a n input or output ta sk in L isp, you should a sk yourse lf, “ Ca n print or read do the job? ”
You will sa ve yourse lf a lot of trouble if you a lwa ys use the se two func tions a s your sta rting point.

W ar ning

T he read c om m a nd c a n be da nge rous if use d in the wrong wa y. Se e T he Da nge rs of re a d a nd e va l in T he Da nge rs of re a d a nd


e va l for de ta ils.

T he print a nd read func tions think a bout va lue s with the m ind of a c om pute r, not the m ind of a hum a n. A c om pute r love s
ha ving strings of te xt surrounde d by quote s. It doe sn’t ha ve a hum a n bra in, a nd c onse que ntly, it c a n’t unde rsta nd wha t we m e a n
whe n we fe e d it ra w te xtua l inform a tion. Howe ve r, if a te xt fra gm e nt is surrounde d by quote s, e ve n a dum b old c om pute r c a n
figure out tha t the va lue we ’re ha nding it is proba bly a string of te xt.
T he print a nd read c om m a nds a c tua lly ta ke this philosophy to the e xtre m e . Alm ost a ny c onc e iva ble type of da ta in L isp (with
the e xc e ption of a c tua l func tions a nd som e a dva nc e d da ta struc ture s) c a n be printe d a nd re a d using the se c om m a nds, without the
slighte st bit of loss a long the wa y. You c a n proba bly a lre a dy im a gine som e sc e na rios whe re this fe a ture would be im m e nse ly
va lua ble , suc h a s writing som e ha iry a nd huge pie c e of da ta to a file , a nd loa ding it in a ga in a t a la te r da te .
As a sim ple e xa m ple , the following c ode ha s e x ac tly the sa m e de sign a s the pre vious func tion, but a m a z ingly, it c a n re a d a nd
print a num be r inste a d of a string. Notic e how the progra m prints a nd re a ds num be rs without the use of quote s, sinc e L isp knows
whe n som e thing is a num be r just by se e ing the num be r in its ra w form .
> (defun add-five ()
(print "please enter a number:")
(let ((num (read)))
(print "When I add five I get")
(print (+ num 5))))
ADD-FIVE
> (add-five)
"please enter a number:" 4
"When I add five I get"
9
L e t’s look a t som e m ore e xa m ple s of wha t ha ppe ns whe n we use print to write out va lue s.
(print '3) => 3 An integer
(print '3.4) => 3.4 A float
(print 'foo) => FOO
A symbol. It may be printed in all caps, since Common
Lisp symbols are blind to letter case.
(print '"foo") => "foo" A string
(print '#\a) => #\a A character
T he se e xa m ple s a re a ll re a lly boring, sinc e print pre tty m uc h just prints out e xa c tly wha t we put in. Note tha t we put a n
e xplic it quote on the front of e a c h va lue . It c ould be om itte d a nd would be im plic it in a ll c a se s but the sym bol na m e , sinc e a
sym bol c a n a lso re fe r to func tions.
T he la st e xa m ple shows how lite ra l c ha ra c te rs a re e nte re d in L isp. T o c re a te a L isp c ha ra c te r, just pla c e the #\ sym bols in front
of the a c tua l c ha ra c te r. L isp a lso ha s spe c ia l lite ra ls de fine d for nonvisible c ha ra c te rs. T he m ost im porta nt for e ve ryda y use a re
#\newline, #\tab, a nd #\space.
A ta ble of output from the read func tion would look just a s boring a s this ta ble for print, in the sa m e sym m e tric a l wa y.

Note

In the e xa m ple s a bove I sta te d tha t Com m on L isp sym bols a re blind to le tte r c a se . W hile this is true for m ost strings, it is in fa c t
possible to c re a te c a se -se nsitive sym bols by surrounding the sym bol with the ve rtic a l pipe |. So the sym bol |CaseSensitiveSymbol|
will re ta in its c a se . Sym bols surrounde d by ve rtic a l pipe s c a n e ve n c onta in punc tua tion. He nc e |even this is a legal Lisp
symbol!|
Re ading and P r inting Stuff the W ay H umans Like It
Of c ourse , our initia l little say-hello func tion doe s a pre tty a wful job of gre e ting pe ople , e ve n if it ha s som e inte re sting
prope rtie s. It would be m uc h be tte r if we ha d m ore func tions tha t c ould m a ke it frie ndlie r for hum a ns. Ac tua lly, we c a n c re a te a
(ve ry sym m e tric a l) little ta ble tha t sum m a riz e s wha t we would like :

As you c a n se e , L isp ha s a c om m a nd tha t c a n print pie c e s of da ta in a wa y tha t is a ppe a ling to hum a ns. T he princ func tion c a n
ta ke a ny pie c e of L isp da ta , a nd it trie s to print tha t da ta in a wa y hum a ns would pre fe r. It will do the ba sic things you m ight
e xpe c t: le a ve off the quote s on a string, print c ha ra c te rs in the ir ra w form , a nd so on. He re a re som e e xa m ple s:
(princ '3) => 3
(princ '3.4) => 3.4
(princ 'foo) => FOO
(princ '"foo") => foo
(princ '#\a) => a
He re ’s a n e xa m ple of princing a c ha ra c te r tha t ha s a spe c ia l m e a ning:
> (progn (princ "This sentence will be interrupted")
(princ #\newline)
(princ "by an annoying newline character."))
This sentence will be interrupted
by an annoying newline character.
By its na ture , princ c ould be use d to print a ny a rbitra ry output of c ha ra c te rs you wa nt. T his is funda m e nta lly diffe re nt from
print. As we ’ve disc usse d, the c ool thing a bout the print c om m a nd is tha t it prints obje c ts in suc h a wa y tha t the y c a n a lwa ys be
“ re a d” ba c k into the ir inte rna l re pre se nta tion. Howe ve r, this m e a ns print c a n’t be use d to ge ne ra te a ny a rbitra ry bit of te xt. On
the othe r ha nd, princ c a n be use d to print a nything you wa nt.
T he re fore , a lthough princ c a n print stuff in a wa y tha t hum a ns pre fe r, it’s a one -wa y stre e t. Onc e we ’ve printe d things with
princ, only a hum a nlike inte llige nc e c ould de c iphe r how to c ha nge things ba c k into a m e a ningful, a ppropria te L isp da ta struc ture .
Sinc e c om pute rs a re too stupid to do this right now, it m e a ns our be love d sym m e try ha s be e n broke n.
Of c ourse , we c ould a lwa ys c he a t a nd c om e up with som e a rbitra ry rule s for how the c om pute r should inte rpre t wha t the hum a n
e nte rs. An obvious wa y to do this would be to sa y to the c om pute r, “ Just le t the use rs type in wha te ve r the y wa nt until the y hit the
e nte r ke y, the n tre a t the whole thing a s a string. ” T he func tion tha t doe s this in Com m on L isp is c a lle d read-line. Howe ve r, it ha s
none of the sophistic a tion of the read, print, a nd princ func tions, sinc e it knows a bout nothing be yond c ha ra c te rs a nd strings.
W ith this ne w knowle dge , we c a n fina lly go full c irc le a nd c re a te a prope r func tion for gre e ting som e one , without ugly quote s or
othe r odditie s:
> (defun say-hello ()

(princ "Please type your name:")

(let ((name (read-line)))

(princ "Nice to meet you, ")

(princ name)))
SAY-HELLO
> (say-hello)
Please type your name: Bob O'Malley
Nice to meet you, Bob O'Malley
T his ve rsion of the say-hello func tion is sim ila r to our first ve rsion. Howe ve r, whe n the c om pute r a sks use rs for the ir na m e

, it now doe s so without printing quote s a round the te xt string. T he sa m e holds true to whe n we print the gre e ting

. Also, use rs c a n now e nte r in any na m e (inc luding a na m e with spa c e s a nd quote s), sinc e the read-line c om m a nd c a pture s
a nd re turns a ll the te xt e nte re d until the e nte r ke y is pre sse d, without a ny fuss.
The Symme tr y Be twe e n Code and Data in Lisp
You ha ve se e n tha t L isp ha s ve ry e le ga nt a nd sym m e tric a l fa c ilitie s for tra nsla ting ra w string da ta from the outside world a nd
c onve rting it to a nd from L isp synta x e xpre ssions. But L isp ha s a n e ve n de e pe r sym m e try. It c a n tre a t progra m c ode a nd da ta
inte rc ha nge a bly. A progra m m ing la ngua ge tha t use s the sa m e da ta struc ture s to store da ta a nd progra m c ode is c a lle d homoic onic .
You sa w a n e xa m ple of hom oic onic ity in Cha pte r 3, whe n we disc usse d c ode m ode a nd da ta m ode . In tha t e xa m ple , we use d the
quote to c ha nge be twe e n the two m ode s:
> '(+ 1 2) ;data mode
(+ 1 2)
> (+ 1 2) ;code mode
3
In the pre vious c ha pte r, we took this c onc e pt one ste p furthe r by using a qua siquote whe n de fining the describe-path func tion.
But the quoting a nd qua siquoting fa c ilitie s in L isp a re som e wha t lim ite d in the ir a bilitie s. W ha t if we ge ne ra te a pie c e of L isp
c ode from sc ra tc h som e how a nd wish to e xe c ute it a s if it we re a pie c e of c ode ? For e xa m ple , le t’s store a ra w c hunk of c ode
inside a va ria ble :
> (defparameter *foo* '(+ 1 2))
*FOO*
How c ould we e xe c ute the c ode tha t’s in the *foo* va ria ble ? W e ne e d a n e ve n m ore powe rful c om m a nd to m a ke this possible .
T his is the eval c om m a nd:
> (eval *foo*)
3
Be c a use the eval c om m a nd is so powe rful a nd ye t so sim ple , it is e xtre m e ly e ntic ing to be ginning L ispe rs. You wa nt to write a
progra m with se lf-m odifying c ode ? T he n eval will be your be st frie nd. In fa c t, this is proba bly the m a in re a son why the a rtific ia l
inte llige nc e (AI) fre a ks ba c k in the da y love d L isp so m uc h. Go a he a d a nd try writing som e progra m s tha t use the eval c om m a nd.
You’ll find tha t it’s a lot of fun.
Howe ve r, a n e xpe rie nc e d L ispe r will only ra re ly use eval. Until you ha ve a fe w thousa nd line s of L isp c ode unde r your be lt, you
re a lly won’t know whe n it is a ppropria te to use this e xtre m e ly powe rful c om m a nd. Ofte n, a be ginning L ispe r will use the eval
c om m a nd inste a d of de fining a L isp m a c ro. W e will disc uss m a c ros in Cha pte r 16.
T he bottom line is tha t the sym m e try of da ta a nd c ode in L isp pre tty m uc h m a ke s L isp the poste r c hild of hom oic onic ity.
Quoting, qua siquoting, the eval c om m a nd, a nd m a c ros a llow you to ta ke a dva nta ge of this prope rty in your c ode .

W ar ning

Ine xpe rie nc e d use of eval c a n pose a se c urity risk. Se e T he Da nge rs of re a d a nd e va l in T he Da nge rs of re a d a nd e va l for m ore
inform a tion.
Adding a Custom Inte r fac e to O ur G ame Engine
So fa r, we ’ve be e n using the L isp RE PL to e nte r our ga m e c om m a nds. It’s a m a z ing how we ll this works for prototyping our
ga m e . But now tha t you’ve ga ine d a n unde rsta nding of the ba sic Com m on L isp input a nd output c om m a nds, we c a n be gin to put in
pla c e our own c ustom te xt ga m e inte rfa c e , whic h will be be tte r suite d for inte ra c ting with the pla ye r.
Se tting Up a Custom REP L
Cre a ting your own RE PL in L isp is a lm ost la ugha bly e a sy. He re ’s a sim ple c ustom RE PL for our ga m e , whic h le ts us c a ll the
look c om m a nd in e xa c tly the sa m e wa y a s the sta nda rd RE PL did:
> (defun game-repl ()
(loop (print (eval (read)))))
GAME-REPL
> (game-repl)
(look)
(YOU ARE IN THE LIVING-ROOM. A WIZARD IS SNORING LOUDLY ON THE COUCH. THERE IS
A DOOR GOING WEST FROM HERE. THERE IS A LADDER GOING UPSTAIRS FROM HERE. YOU
SEE A WHISKEY ON THE FLOOR.)
Stop m e if this e xpla na tion of game-repl is c onfusing: First it reads a c om m a nd, the n evals it, a nd fina lly prints it. T he only
c om m a nd you ha ve n’t se e n be fore is loop (c ove re d in de ta il in Cha pte r 10), whic h a s you m ight e xpe c t, sim ply loops fore ve r. (In
CL ISP, you’ll ne e d to hit c trl-C a nd type :a to ge t out of the infinite loop. ) As you c a n se e , it’s e a sy to build your own RE PL by
sim ply c a lling read, eval, print, a nd loop.

Of c ourse , to c ustom iz e the be ha vior of our RE PL , we ’ll wa nt to c a ll our own ve rsions of the se func tions. Also, we ’ll wa nt a wa y
to e xit from our ga m e in a m ore gra c e ful m a nne r. So, le t’s re de fine game-repl a s follows:
(defun game-repl ()

(let ((cmd (game-read)))

(unless (eq (car cmd) 'quit)

(game-print (game-eval cmd))

(game-repl))))

In this ve rsion, we first c a pture the c om m a nd the pla ye r type s using a loc a l va ria ble , cmd . T his wa y, we c a n inte rc e pt a ny
a tte m pt to c a ll quit a nd use it to e xit our game-repl. In othe r words, we wa nt to c ontinue running our RE PL unle ss the use r type d

quit . Othe rwise , the func tion evals a nd prints , but using our c ustom ve rsions of the se func tions, whic h we ’ll write

shortly. Fina lly, the game-repl func tion c a lls itse lf re c ursive ly , c a using it to loop ba c k, a s long a s we ha d not de c ide d to
quit e a rlie r.
W r iting a Custom r e ad F unc tion
T he purpose of our game-read func tion is to fix the two a nnoya nc e s tha t m a ke the sta nda rd L isp read func tion wrong for pla ying
our ga m e :

T he sta nda rd L isp read forc e s us to put pa re nthe se s a round our c om m a nds. As a ny old-sc hool te xt a dve nture
pla ye r knows, we should be a ble to just type look without a ny pa re nthe se s. T o a c c om plish this, we c a n just c a ll
read-line a nd stic k in our own pa re nthe se s.
W i t h read, we m ust put a quote in front of a ny func tion c om m a nds. W e should be a ble to type walk east
without a quote in front of east. T o do this, we ’ll stic k a quote in front of the pa ra m e te rs a fte r the fa c t.

He re ’s a de finition of game-read tha t doe s both of the se things:


(defun game-read ()

(let ((cmd (read-from-string

(concatenate 'string "(" (read-line) ")"))))

(flet ((quote-it (x)


(list 'quote x)))

(cons (car cmd) (mapcar #'quote-it (cdr cmd))))))

T he read-from-string c om m a nd works just like the read c om m a nd, but le ts us re a d a synta x e xpre ssion (or a ny othe r
ba sic L isp da ta type ) from a string inste a d of dire c tly from the c onsole .

T he string we use for this is a twe a ke d ve rsion of a string we ge t from read-line . W e twe a k it by a dding quote s a round it
using the concatenate c om m a nd, whic h c a n be use d for c onc a te na ting strings toge the r, a s we ll a s som e pa re nthe se s. T he re sult is
tha t the cmd va ria ble will be se t to the pla ye r’s re que ste d c om m a nd a nd c onve rte d into a L isp synta x e xpre ssion. For e xa m ple , if
the pla ye r type s in walk east, the cmd va ria ble will be se t to the e xpre ssion (walk east), whic h is a list c onta ining two sym bols.

Ne xt, we de fine a loc a l func tion c a lle d quote-it , whic h we c a n use to quote a ny a rgum e nts the pla ye r ha s in a
c om m a nd. How e xa c tly doe s it m a na ge to quote a pa ra m e te r? W e ll, it turns out tha t the single quote is just shortha nd for a L isp
c om m a nd c a lle d quote. T his m e a ns tha t 'foo a nd (quote foo) a re the sa m e . W e c a n quote a ra w pa ra m e te r by sim ply putting the
pa ra m e te r in a list with the quote c om m a nd in front.
Re m e m be r tha t loc a l func tions c a n be de fine d with labels or flet. Sinc e we a re not using a ny re c ursion in the quote-it

func t i on , we c a n use the sim ple r flet c om m a nd. T he fina l line in the game-read func tion a pplie s quote-it to e ve ry

a rgum e nt in the pla ye r’s c om m a nd. It doe s this by m a pping quote-it a c ross the cdr of the cmd va ria ble (a nd the n a tta c hing
the first word in the c om m a nd ba c k on front with car).
L e t’s try our ne w func tion:
> (game-read)
walk east
(WALK 'EAST)
As you c a n se e , the game-read func tion is a ble to a dd pa re nthe se s a nd quote s— just wha t our ga m e ne e ds!

Note

Our c ustom re a de r ha s som e lim ita tions tha t a suffic ie ntly bone he a de d ga m e pla ye r c ould c onc e iva bly bring to the surfa c e . T he
pla ye r c ould e nte r a we ird string like "(look", with m ism a tc he d pa re nthe se s, a nd it would c a use a L isp e xc e ption in the game-read
c om m a nd. T he re ’s nothing wrong with this, pe r se , sinc e the sta nda rd read c om m a nd will a lso a c t stra nge ly whe n give n ga rble d
input. (In this c a se , it will le t you e nte r a nothe r line of input in the hope s tha t you will e ve ntua lly supply it with the m issing
pa re nthe sis. ) Howe ve r, our game-repl doe sn’t ha ndle this situa tion prope rly, c a using the a c tua l game-repl to c ra sh. T his would be
a s if you we re pla ying Z ork a nd type d in a c om m a nd so vile tha t it took down the Z ork ga m e itse lf. T his ra re situa tion c ould be
a ddre sse d by ha ving a dditiona l e xc e ption ha ndling, a s disc usse d in Cha pte r 13.
W r iting a game -e val F unc tion
Now tha t we ’ve c re a te d a nigh-pe rfe c t L isp re a de r, le t’s think a bout how we c ould im prove the eval c om m a nd. T he m a in
proble m with using eval in a ga m e is it a llows you to c a ll a ny L isp c om m a nd, e ve n if tha t c om m a nd ha s nothing to do with
pla ying the ga m e . T o he lp prote c t our progra m from ha c ke rs, we ’ll c re a te a game-eval func tion tha t a llows only c e rta in c om m a nds
to be c a lle d, a s follows:
(defparameter *allowed-commands* '(look walk pickup inventory))
(defun game-eval (sexp)

(if (member (car sexp) *allowed-commands*)

(eval sexp)
'(i do not know that command.)))
T he game-eval func tion c he c ks if the first word in the e nte re d c om m a nd is in the list of a llowe d c om m a nds, using the member

func tion . If it is, we the n use the sta nda rd eval to e xe c ute the pla ye r’s c om m a nd . By c he c king tha t the c om m a nd
c a lle d by the pla ye r is in the offic ia l list, we prote c t ourse lve s a ga inst a ny a tte m pts to c a ll m a lic ious c om m a nds.

W ar ning

Our game-eval func tion doe s not offe r 100 pe rc e nt prote c tion a ga inst ha c king. Se e T he Da nge rs of re a d a nd e va l in T he Da nge rs
of re a d a nd e va l for de ta ils.
W r iting a game -pr int F unc tion
T he fina l m issing pie c e in our game-repl syste m is the game-print func tion. Of a ll the lim ita tions in the L isp RE PL ve rsion of
our ga m e , one wa s the m ost obvious: All the te xt de sc riptions printe d in the ga m e we re in uppe rc a se .
L a st I c he c ke d, throughout the c urre nt m ille nnium , c om pute rs ha ve be e n a ble to displa y both uppe rc a se and lowe rc a se
c ha ra c te rs. By writing our own game-print func tion, we c a n solve this proble m .
Be fore we ste p through the game-print func tion’s c ode , le t’s look a t a n e xa m ple of its output:
> (game-print '(THIS IS A SENTENCE. WHAT ABOUT THIS? PROBABLY.))
This is a sentence. What about this? Probably.
As you c a n se e , the game-print func tion c onve rts our sym bol-ba se d writing into prope rly c a pita liz e d te xt. By ha ving this
func tion a va ila ble , we c a n store the te xt in our ga m e e ngine in the m ost c om forta ble form a t possible : lists of sym bols. T his form a t
m a ke s it e a sie r to m a nipula te the te xt. T he n, a t the point of pre se nta tion, we c a n de c ora te the se sym bol lists with pre se nta tion
de ta ils.
Of c ourse , in this e xa m ple , the de c ora tions a re ve ry sim ple . All we do is a djust the c a se . But you c a n a lre a dy se e som e sm a ll
be ne fits of se pa ra ting the pre se nta tion de ta ils from the da ta m ode l. For insta nc e , suppose we c ha nge d the describe-path func tion
to write se nte nc e s like “ L e ft of he re lie s a door. ” No furthe r c ha nge s would be ne e de d; the progra m would a utom a tic a lly know to
c a pita liz e the Le ft a t the be ginning of the se nte nc e .
Howe ve r, the re a l be ne fits c om e into pla y whe n you wa nt to use m ore sophistic a te d m e thods of pre se nta tion, suc h a s ge ne ra ting
HT ML c ode . You m ight wa nt to inc orpora te c ustom se m a ntic s for your te xt ga m e to e nha nc e the a ppe a ra nc e of the te xt, suc h a s
c ha nging c olors, fonts, a nd so on. For insta nc e , you c ould a llow your ga m e de sc riptions to c onta in phra se s suc h a s “ You a re be ing
a tta c ke d by a (re d e vil de m on). ” T he n you c ould just c a tc h the ke yword red in the game-print func tion to write the e nc lose d te xt
in re d. W e will be c re a ting a n HT ML pre se nta tion syste m sim ila r to this in Cha pte r 17.

Now we ’re re a dy to look a t the game-print func tion’s c ode :


(defun tweak-text (lst caps lit)
(when lst

(let ((item (car lst))


(rest (cdr lst)))

(cond ((eq item #\space) (cons item (tweak-text rest caps lit)))

((member item '(#\! #\? #\.)) (cons item (tweak-text rest t lit)))

((eq item #\") (tweak-text rest caps (not lit)))


(lit (cons item (tweak-text rest nil lit)))

((or caps lit) (cons (char-upcase item) (tweak-text rest nil lit)))

(t (cons (char-downcase item) (tweak-text rest nil nil)))))))


(defun game-print (lst)

(princ (coerce (tweak-text (coerce (string-trim "() "

(prin1-to-string lst))
'list)
t

nil)
'string))
(fresh-line))
T he game-print func tion a nd its he lpe r func tion a re a bit m ore c om plic a te d tha n the othe r func tions we ’ve looke d a t so fa r. T he
first im porta nt pa rt of the c ode tha t is e xe c ute d is in game-print, whe re it c onve rts the sym bol list (c onta ining the te xt whose

la yout we wa nt to fix) into a string with prin1-to-string , one of L isp’s m a ny print va ria nts. T he to-string pa rt m e a ns
this func tion doe sn’t dum p the re sult to the sc re e n, but just re turns it a s a string. T he 1 m e a ns it will sta y on a single line . T he
sta nda rd print c om m a nd pre c e de s its output with a ne wline c ha ra c te r a nd a lso follows it with a spa c e . T he func tions prin1 a nd
prin1-to-string va ria nts don’t a dd the se e xtra c ha ra c te rs.

Ne xt, game-print c onve rts the string to a list of c ha ra c te rs with the coerce func tion . By c oe rc ing our string into a list, we
c a n re duc e the bigge r goa l of the func tion into a list-proc e ssing proble m . T his is sm a c k-da b in the L isp c om fort z one . In this c a se ,
we ’re c re a ting a list of the c ha ra c te rs m a king up the te xt we wa nt to fix.

W e c a n now se nd the da ta to the list-e a te r func tion tweak-text . Notic e tha t som e of the a rgum e nts use d in the c ode of
t h e game-print func tion a re printe d on the ir own line for c la rity. You c a n e a sily se e whic h a rgum e nts a re m e a nt for whic h

c om m a nds by looking a t the inde nta tion. For insta nc e , the t a nd nil a rgum e nts be long to tweak-text.
T he tweak-text func tion looks a t e a c h c ha ra c te r in the list a nd m odifie s it a s ne e de d. At the top of this func tion, we de fine two

loc a l va ria ble s, item a nd rest, whic h we ge t by c he wing off a n ite m from the front of the se nte nc e we ’re twe a king . T he n,

the tweak-text func tion use s a cond to c he c k the c ha ra c te r a t the top of the list for diffe re nt c onditions .

T he first c ondition it c he c ks for is whe the r the c ha ra c te r is a spa c e c ha ra c te r . If so, it just le a ve s the spa c e unc ha nge d a nd

proc e sse s the ne xt c ha ra c te r in the list. If the c ha ra c te r is a pe riod, que stion m a rk, or e xc la m a tion point , we turn on the cap
pa ra m e te r for the re st of the string (by using the va lue t a s a n a rgum e nt in the re c ursive c a ll) to indic a te tha t the ne xt sym bol is a t
the be ginning of a se nte nc e a nd ne e ds a c a pita l le tte r.

W e a lso tra c k whe the r we ’ve e nc ounte re d a quota tion m a rk . W e do this be c a use , infre que ntly, a sym bol list is not
a de qua te for e nc oding E nglish te xt. E xa m ple s inc lude ha ving a c om m a (c om m a s a re not a llowe d in sta nda rd Com m on L isp
sym bols) or produc t na m e s with nonsta nda rd c a pita liz a tion. In the se c a se s, we c a n just fa ll ba c k on using te xt strings. He re ’s a n
e xa m ple :
> (game-print '(not only does this sentence
have a "comma," it also mentions the "iPad."))
Not only does this sentence have a comma, it also mentions the iPad.
Our sa m ple ga m e doe sn’t a c tua lly ne e d the fa llba c k fa c ility. None the le ss, this fe a ture a llows the game-print func tion to ha ndle
m a ny ba sic e xc e ptiona l te xt situa tions tha t you m a y e nc ounte r if you try to e xpa nd the ga m e on your own. W e te ll the func tion to
tre a t the c a pita liz a tion a s shown lite ra lly by turning on the lit va ria ble in the re c ursive c a ll. As long a s this va lue is se t, the

tweak-text func tion pre ve nts the c a pita liz a tion rule s (whic h sta rt a t ) from be ing re a c he d.
T he ne xt thing the tweak-text func tion c he c ks is whe the r the ne xt c ha ra c te r is suppose d to be c a pita liz e d. If it is, we use the
char-upcase func tion to c ha nge the c urre nt c ha ra c te r to uppe rc a se (if it isn’t a lre a dy) be fore proc e ssing the ne xt ite m in the list

If none of the othe r c onditions we re m e t, we know tha t the c urre nt c ha ra c te r should be lowe rc a se , a nd we c a n c onve rt it
using the char-downcase func tion.
Afte r tweak-text is finishe d c orre c ting the te xt in the c ha ra c te r list, the game-print func tion c oe rc e s it ba c k into a prope r string

a nd princs it . T he fresh-line func tion a t the e nd of game-print m a ke s sure tha t the ne xt ite m a ppe a ring on the sc re e n will
sta rt on a fre sh line .
W e ha ve now c om ple te d the ta sk of printing the origina l list of sym bols to the sc re e n, using a se t of de c ora tions a ppropria te for
the ne e ds of a n a dve nture ga m e e ngine .
Tr ying O ut O ur F anc y Ne w G ame Inte r fac e
W e ha ve now c om ple te d a ll the pie c e s ne e de d for a c ustom RE PL for our ga m e e ngine . Sim ply c a ll the game-repl func tion a nd
e xplore our ne w ga m e world. Re m e m be r tha t we will be e xpa nding this e ngine into a full ga m e , with a dditiona l c om m a nds, in
Cha pte r 17.
> (game-repl)
look
You are in the living-room. A wizard is snoring loudly on the couch. There is
a door going west from here. There is a ladder going upstairs from here. You
see a whiskey on the floor. You see a bucket on the floor.
walk west
You are in a beautiful garden. There is a well in front of you. There is a
door going east from here. You see a frog on the floor. You see a chain on
the floor.
pickup chain
You are now carrying the chain
scratch head
I do not know that command.
pickup chicken
You cannot get that.
walk east
You are in the living-room. A wizard is snoring loudly on the couch. There is
a door going west from here. There is a ladder going upstairs from here. You
see a whiskey on the floor. You see a bucket on the floor.
walk upstairs
You are in the attic. There is a giant welding torch in the corner. There is a
ladder going downstairs from here.
inventory
Items-chain
walk china
You cannot go that way.
walk downstairs
You are in the living-room. A wizard is snoring loudly on the couch. There is
a door going west from here. There is a ladder going upstairs from here. You
see a whiskey on the floor. You see a bucket on the floor.
pickup bucket
You are now carrying the bucket
look
You are in the living-room. A wizard is snoring loudly on the couch. There is
a door going west from here. There is a ladder going upstairs from here. You
see a whiskey on the floor.
quit
Suc c e ss! W e now ha ve a n e xtre m e ly fle xible te xt ga m e e ngine . It c a n be e xpa nde d a nd de bugge d within the L isp RE PL . It a lso
ha s a fully c ustom iz a ble inte rfa c e to offe r the pla ye r a se a m le ss te xt a dve nture e xpe rie nc e . As we put it toge the r, you sa w som e
m ind-be nding L isp te c hnique s tha t le t us c onstruc t this e ngine with a m inim um of fille r c ode or othe r ove rhe a d.
The Dange r s of r e ad and e val
W e ’ve use d both the eval a nd the read c om m a nds in c re a ting a c ustom L isp RE PL . T he se c om m a nds a re ve ry powe rful, but a lso
ve ry da nge rous. Using the m without ta king the prope r pre c a utions m ight a llow a ha c ke r to a tta c k your softwa re by running
m a lic ious c om m a nds.
For e xa m ple , suppose our progra m ne e de d a func tion c a lle d format-harddrive. T his is not a func tion we would wa nt just a ny
pe rson to ha ve a c c e ss to, a nd it c ould be ve ry da nge rous if a ha c ke r som e how tric ke d our ga m e RE PL into c a lling it.
T he game-eval func tion we c re a te d e a rlie r in this c ha pte r ha s som e c rude sa fe gua rds to pre ve nt a pla ye r from e nte ring format-
harddrive a s a ga m e c om m a nd. He re ’s wha t ha ppe ns if we try to run this c om m a nd in our ne w ga m e RE PL :
> (game-repl)
format-harddrive
I do not know that command.
Our game-eval func tion will run only c om m a nds tha t a re in a n a pprove d list. T his give s our ga m e a sort of fire wa ll, whic h le ts us
a c c e ss the powe rs of L isp to e va lua te c om m a nds while still pre ve nting the pla ye r from ha c king the ga m e .
Howe ve r, the re a re a lso m ore sophistic a te d e xploits pla ye rs c ould try. For insta nc e , the y c ould e nte r walk (format-harddrive).
Fortuna te ly, our game-read func tion forc e s a ll func tion pa ra m e te rs into da ta m ode by using quote-it. By using quote-it in game-
read, the a c tua l c ode tha t is e xe c ute d is (walk '(format-ha rddrive )). T he quote in front of (format-hardrive) puts the m a lic ious
c om m a nd into da ta m ode , so nothing ba d c a n ha ppe n.
One a tta c k m e thod tha t will bre a k our progra m is to use re ade r mac ros. T he se a re a n a dva nc e d se t of fe a ture s, built into the
Com m on L isp read c om m a nd, tha t ope n a nothe r a ve nue for e xe c uting m a lic ious c om pute r c ode . (Re m e m be r tha t be fore we use
eval on ga m e c om m a nds, the y first pa ss through read. ) An e xa m ple of a ga m e c om m a nd tha t will suc c e ssfully e xe c ute e vil c ode is
walk #.{format-harddrive}.
T he bottom line is tha t you c a n ne ve r be sure tha t a L isp progra m using eval or read is c om ple te ly sa fe from a ha c ke r. W he n
writing produc tion L isp c ode , you should try to a void the se two c om m a nds whe n possible .
W hat You've Le ar ne d
In this c ha pte r, we c re a te d a c ustom RE PL to supe rc ha rge our te xt a dve nture ga m e . Along the wa y, you le a rne d the following:
T he print a nd read func tions le t you dire c tly c om m unic a te with the use r through the c onsole . T he se two func tions work in a
c om pute r-frie ndly wa y.
Othe r input/output func tions a re not a s e le ga nt a s read a nd print, but a re frie ndlie r for inte ra c ting with hum a ns. E xa m ple s
inc lude princ a nd read-line.
A homoic onic progra m m ing la ngua ge store s its progra m c ode a nd progra m da ta in a sim ila r form a t. L isp’s quoting,
qua siquoting, eval, a nd m a c ro fe a ture s m a ke it e xtre m e ly hom oic onic .
It’s e a sy to write your own c ustom RE PL .
It’s sim ple to tra nsform your inte rna l L isp da ta into the form a t m ost suita ble for your progra m ’s inte rfa c e . T his m a ke s it e a sy
to se pa ra te pre se nta tion de ta ils from your progra m ’s inte rna l da ta struc ture s.
Chapte r 6. 5. lambda: A F unc tion So Impor tant It De se r ve s Its O wn Chapte r
It’s im possible to ove rsta te the im porta nc e of the lambda c om m a nd in L isp. In fa c t, this c om m a nd is pre tty m uc h the e ntire
re a son tha t L isp e xists in the first pla c e .
W hat lambda Doe s
In short, lambda le ts you c re a te a func tion without giving it a na m e . For e xa m ple , le t’s sa y we c re a te a half func tion tha t ta ke s
a num be r a nd divide s it in ha lf. Until now, we ’ve writte n suc h a func tion this wa y:
(defun half (n)
(/ n 2))

It turns out tha t, in L isp, func tions a re a c tua lly va lue s tha t we c a n vie w a nd pa ss a round just a s if the y we re num be rs or lists. An
e xpe rie nc e d L isp progra m m e r would sa y tha t func tions a re first-c lass v alue s in L isp. As you sa w in Cha pte r 5, you c a n a c tua lly ge t
a t the func tion re pre se nte d by the word half by using the func tion ope ra tor:
> #'half
#<FUNCTION HALF ...>
T he lambda c om m a nd just le ts you do the se sa m e two things in a single ste p. You c a n de fine a func tion a nd the n ge t it, without
giving your func tion a na m e :
> (lambda (n) (/ n 2))
#<FUNCTION :LAMBDA ...>
T he first pa ra m e te r to the lambda c om m a nd is a pa ra m e te r list, no diffe re nt from the pa ra m e te r list use d in defun. T he re st of
the pa ra m e te rs a re just the c om m a nds for the body of the unna m e d func tion.
Onc e you ha ve a va lue re pre se nting your unna m e d ha lving func tion, you c a n pa ss it dire c tly to othe r Com m on L isp c om m a nds,
suc h a s the mapcar or apply c om m a nds. For insta nc e , we c ould do the following to e le ga ntly ha lve a ll the va lue s in a list:
> (mapcar (lambda (n) (/ n 2)) '(2 4 6))
(1 2 3)
Be c a use not a ll pa ra m e te rs of the lambda c om m a nd a re e va lua te d, lambda itse lf is not a c tua lly a true func tion. It is som e thing
c a lle d a mac ro. Re m e m be r from Cha pte r 2 tha t a ll pa ra m e te rs to a L isp func tion a re e va lua te d be fore the func tion itse lf is
e va lua te d. Ma c ros, on the othe r ha nd, ha ve spe c ia l powe rs a nd a re a llowe d to bre a k those rule s. You’ll le a rn m ore a bout m a c ros in
Cha pte r 16.
Also, to c onfuse m a tte rs a bit, the a c tua l va lue tha t lambda re turns is a re gula r L isp func tion— in this c a se , a func tion tha t c uts a
num be r in ha lf. W he n L ispe rs ta lk a bout la m bda func tions— whic h the y pre tty m uc h do for bre a kfa st, lunc h, a nd dinne r— the y’re
ta lking a bout func tions c re a te d using lambda. T he y’re not ta lking a bout the lambda m a c ro itse lf, whic h is not a func tion. Got tha t?
lambda le ts your progra m s do ve ry c om plic a te d things.
T he lambda form a llows your progra m m ing c ode to ta ke a c onc e ptua l le a p.
W hile m ost progra m m ing la ngua ge s try to ke e p the worlds of func tions a nd va lue s se pa ra te , L isp le ts you bridge the se worlds a s
de sire d. For insta nc e , if you wa nt to pa c ka ge up a little a d hoc func tion a nd pa ss it off to a nothe r pa rt of your progra m , lambda
doe s e xa c tly wha t you ne e d.
You will se e tha t m ost L isp progra m s use this c om m a nd ve ry he a vily. T he sa m e holds true for the re m a ining e xa m ple s in this
book.
W hy lambda Is So Impor tant
T he a bility to pa ss a round func tions a s if the y we re just pla in old pie c e s of da ta is inc re dibly va lua ble . Onc e you ge t use d to
doing this, you ope n up a ll kinds of c onc e ptua l possibilitie s in the de sign of your progra m s. E ve ntua lly, your progra m s will sta rt
looking ve ry diffe re nt from progra m s in m ore (da re I sa y) pe de stria n la ngua ge s, suc h a s Ja va or C. T he na m e for the style of
progra m m ing tha t re lie s he a vily on pa ssing func tions a s va lue s is c a lle d highe r-orde r func tional programming. W e will look a t this
style in m ore de ta il in Cha pte r 14.
An e ve n m ore im porta nt re a son why L ispe rs go ga ga ove r lambda is tha t, a s it turns out, in a pure ly m a the m a tic a l se nse , lambda
is a c tua lly the only L isp c om m a nd the re is!
Re c a ll tha t L isp is unusua l a m ong progra m m ing la ngua ge s in tha t it wa s de rive d dire c tly from a m a the m a tic a l c onc e pt c a lle d
the lambda c alc ulus. In short, the la m bda c a lc ulus is a the ore tic a l progra m m ing la ngua ge tha t c onta ins only one c om m a nd: the
lambda c om m a nd. By ha ving only this single c om m a nd a nd using spe c ia l c ode tra nsform a tions, it’s possible to c re a te a fully
func tioning (though pe rha ps not pra c tic a l) progra m m ing la ngua ge .
T he ta ke -hom e point is tha t the lambda spe c ia l form is the m ost funda m e nta l c om m a nd in a L isp syste m , a nd the c e ntra l
c onc e pt from whic h othe r func tions in L isp de rive . In fa c t, it is the c e ntra l c onc e pt from whic h the ve ry ide a of L isp itse lf
origina te d.
Now tha t you ha ve a ba sic unde rsta nding of lambda, you’re re a dy to ta c kle som e m ore c om plic a te d progra m m ing e xa m ple s tha t
would be ha rd to write without the a nonym ous func tions this c om m a nd pe rm its.
W hat You've Le ar ne d
T his short c ha pte r disc usse d how to c re a te a nonym ous func tions. He re a re the m a in points:
By using lambda, you c a n c re a te a func tion without ne e ding to give it a na m e .
Ma ny func tions in L isp a c c e pt func tions a s pa ra m e te rs. If you use the se func tions, you a re using a te c hnique c a lle d highe r-orde r
func tional programming.
Chapte r 7. G oing Be yond Basic Lists
In this c ha pte r, we ’ll go be yond ba sic list c onc e pts. W e ’ll ta lk a bout spe c ia l kinds of lists, a nd we ’ll write a ga m e tha t will ta ke
list m a nipula tion to a ne w le ve l.
Exotic Lists
As you le a rne d in Cha pte r 3, lists in L isp a re built out of c ons c e lls— sm a ll da ta struc ture s tha t a llow you to link toge the r two
pie c e s of da ta . T he right slot in the la st c ons c e ll in a list should c onta in a nil.
By stringing toge the r se ve ra l c ons c e lls, you c a n c re a te a list of a ny le ngth. For insta nc e , this is how we would use c ons c e lls to
c re a te a list of the num be rs 1, 2, a nd 3:
(cons 1 (cons 2 (cons 3 nil)))

Sinc e it’s so c um be rsom e for hum a ns to think of a c ha in of c ons c e lls a s a list, L isp ha s a spe c ia l, sim plifie d synta x for printing
out suc h lists. You c a n se e this for yourse lf by e va lua ting a c ha in of c ons c e lls in the RE PL :
> (cons 1 (cons 2 (cons 3 nil)))
(1 2 3)
L isp use s the sim ple r list synta x whe n it pa rrots our c ha in ba c k to us in the RE PL . It shows our string of c ons c e lls a s a list of
thre e ite m s. T he im porta nt point to re m e m be r is tha t this diffe re nc e in appe aranc e is e ntire ly supe rfic ial. No m a tte r how a L isp
list is displa ye d, funda m e nta lly, it a lwa ys re m a ins a c ha in of c ons c e lls.
Dotte d Lists
So, wha t ha ppe ns if we de via te from the c la ssic “ string of c onse s” form ula ? How will a L isp e nvironm e nt de a l with this whe n
printing lists?
Suppose we try to c re a te a list of the num be rs 1, 2, a nd 3, like this:
(cons 1 (cons 2 3))
He re , inste a d of c re a ting a third c ons c e ll for the third num be r of our list, we stuff it into the right slot of the pre vious c e ll.
W ha t would the printe d re sponse look like if we we re to e nte r this struc ture into a L isp RE PL ? L e t’s try it out:
> (cons 1 (cons 2 3))
(1 2 . 3)
T o indic a te tha t the fina l ite m in the list wa sn’t found in the prope r loc a tion for a nil-te rm ina te d list, L isp pla c e s a dot in front
of this fina l ite m . T his dot is ba sic a lly L isp’s wa y of sa ying, “ I trie d to print this struc ture you e nte re d using list nota tion, but the
la st ite m in the list didn’t c onta in the usua l nil I e xpe c te d; inste a d, it c onta ine d 3. ”
A list in L isp tha t e nds in som e thing othe r tha n a nil is re fe rre d to a s a dotte d list. Dotte d lists a re kind of a n oddity in the L a nd
of L isp. In a nd of the m se lve s, the y a re not tha t use ful a tool for L isp progra m m ing. It would be quite unusua l for a L isp
progra m m e r to store da ta in dotte d lists a s a re gula r pra c tic e . Howe ve r, give n the pe rva sive ne ss of c ons c e lls in L isp, you will
fre que ntly e nc ounte r a non-nil va lue a t the e nd of a c ha in of c ons c e lls. T ha t’s why you should be c om e fa m ilia r with dotte d lists,
e ve n if you m a y ne ve r use the m dire c tly.
Anothe r wa y of thinking a bout this dot nota tion is to c onside r it a s sim ply a n a lte rna te synta x for the cons c om m a nd, use d in
da ta m ode . In fa c t, if we wa nte d to m a ke life ha rd for ourse lve s, we c ould e ve n c re a te re gula r, prope r lists using the dot nota tion,
like this:
> '(1 . (2 . (3 . nil)))
(1 2 3)
Using this line of thinking, the dot a ppe a rs in a dotte d list sim ply be c a use L isp is forc e d to show the fina l c ons c e ll in orde r to
m a inta in the c onsiste nc y of its list-printing m e c ha nism .
P air s
One c om m on a nd pra c tic a l use for dotte d lists in L isp progra m s is to e le ga ntly re pre se nt pa irs. For insta nc e , suppose we wa nte d
to re pre se nt a pa ir of the num be rs 2 a nd 3. One wa y to do this would be to c ons toge the r the se two num be rs:
> (cons 2 3)
(2 . 3)
E sse ntia lly, a ll we ’re doing he re is c re a ting a dotte d list of le ngth two. As e xpe c te d, L isp use s dot nota tion to displa y this pa ir.
Cre a ting pa irs in this m a nne r in L isp is ve ry c onve nie nt a nd e ffic ie nt. It’s c onve nie nt be c a use we c a n e xtra c t m e m be rs from the
pa ir using the sta nda rd car a nd cdr c om m a nds. It’s re la tive ly e ffic ie nt be c a use the L isp e nvironm e nt ne e ds to a lloc a te only a
single c ons c e ll to c onne c t the two ite m s.
T he se type s of pa irs a re c om m only use d in L isp progra m s. For insta nc e , you c ould use the m to store the x-a nd y-c oordina te s of a
point or a ke y/va lue pa ir in a c om ple x da ta struc ture . You will se e this la tte r use for pa irs whe n we disc uss a ssoc ia tion lists.
Cir c ular Lists
He re is the pic ture we use d in Cha pte r 3 to illustra te the c ons c e lls tha t m a ke up the list '(1 2 3):

Now suppose tha t we c re a te d a we ird m uta nt of this list. L e t’s ha ve the cdr of the third c ons c e ll point ba c k to the first c ons
c e ll, ra the r tha n to nil:
E ve ry c ons c e ll in a list the ore tic a lly e xists a s a se pa ra te obje c t in m e m ory. Sinc e the car a nd cdr slots in a c e ll c a n point to
a ny othe r obje c t in m e m ory, a c ons c e ll c a n point to a n upstre a m c ons c e ll of a list. W e c a ll this a c irc ular list.
But be fore you e xpe rim e nt with c irc ula r lists in a ny Com m on L isp e nvironm e nt, you should run this c om m a nd:
(setf *print-circle* t)
Se tting *print-circle* to true wa rns L isp tha t you pla n on pla ying she na niga ns with se lf-re fe re ntia l da ta struc ture s, a nd tha t it
ne e ds to be e xtra c a re ful whe n printing on the sc re e n a ny of the m onstrositie s you m a y c re a te . If you we re to print a c irc ula r list
without this va ria ble se t, the re ’s no te lling wha t would ha ppe n, but wha te ve r the outc om e , it wouldn’t be pre tty (unle ss you find
som e be a uty in sta c k ove rflows a nd infinite loop printing).
W he n you ha ve *print-circle* se t to true , Com m on L isp will use m ore c om ple x printing routine s for printing da ta struc ture s.
T he se routine s (whic h a re disa ble d by de fa ult to im prove pe rform a nc e ) will c he c k to se e if you’ve run into a pre viously se e n c ons
c e ll, so tha t printing doe sn’t e nd up putting you into a n infinite loop.
So how would you go a bout c re a ting a c irc ula r list? T he m ost stra ightforwa rd wa y is to use the setf c om m a nd to put e xtra stuff
in the first pa ra m e te r, like so:
> (defparameter foo '(1 2 3))
FOO
> (setf (cdddr foo) foo)
#1=(1 2 3 . #1#)
In this e xa m ple , we ’ve c re a te d a n infinite list of '(1 2 3 1 2 3 1 2 3 ...) by re pla c ing the nil a t the e nd of a sim ple list
with a re fe re nc e to the list itse lf.
T he a bility to pla c e c om ple x e xpre ssions in the first pa ra m e te r of a setf c om m a nd, a s in this e xa m ple , is ve ry c ool, a nd we ’ll
e xplore it in gre a te r de ta il in Cha pte r 9.

Note

CL ISP (a nd othe r Com m on L isps) c a n de a l with the printing of c irc ula r lists ve ry se nsibly. Som e how, it m ust a ddre ss the fa c t tha t
one pa rt of the list re fe rs to a nothe r pa rt. As you c a n se e , it use s a n e sote ric , but quite c le ve r, nota tion to link the se lf-re fe re ntia l
pa rts of the e xpre ssion. Howe ve r, I’m sure you c a n a lso a ppre c ia te tha t, a s the c om ple xity of a ny se lf-re fe re ntia l da ta inc re a se s, the
printe d re sults offe re d by a L isp printe r for this type of da ta c a n be c om e ha rd for a progra m m e r to grok.
Assoc iation Lists
One pa rtic ula rly use ful da ta struc ture tha t c a n be c re a te d out of c ons c e lls is a n assoc iation list, or alist for short. An a list
c onsists of ke y/va lue pa irs store d in a list.
By c onve ntion, if a ke y a ppe a rs m ultiple tim e s in the list, it is a ssum e d tha t the first a ppe a ra nc e of the ke y c onta ins the de sire d
va lue . For insta nc e , he re is a n a list re pre se nting a n orde r for c offe e drinks pla c e d by Bill, L isa , a nd John:
(defparameter *drink-order* '((bill . double-espresso)
(lisa . small-drip-coffee)
(john . medium-latte)))
T o look up the orde r for a give n pe rson, use the func tion assoc:
> (assoc 'lisa *drink-order*)
(LISA . SMALL-DRIP-COFFEE)
T his func tion se a rc he s the list from the be ginning for the de sire d ke y, a nd the n re turns the ke y/va lue pa ir. Now suppose tha t,
be fore pic king up the drink orde r, L isa fla gs you down a nd opts to c ha nge he r orde r to som e thing slightly m ore de c a de nt. You c a n
c ha nge he r orde r using the push func tion:
> (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))
T his func tion sim ply a dds a ne w ite m to the front of a n e xisting list.
Be c a use , by de fa ult, the first re fe re nc e to a ke y in a n a ssoc ia tion list ta ke s pre c e de nc e ove r la te r re fe re nc e s to the sa m e ke y, the
orde r L isa pla c e d for a sm a ll drip c offe e is supe rse de d by he r m ore re c e nt orde r:
> (assoc 'lisa *drink-order*)
(LISA . LARGE-MOCHA-WITH-WHIPPED-CREAM)
As you c a n se e , a lists a re a gre a t wa y to ke e p tra c k of a ny c ha nge a ble c olle c tion of ke y/va lue pa irs. Alists a re e a sy to
unde rsta nd, to m a nipula te with L isp func tions, a nd to c om pre he nd whe n printe d out (the y’re just lists of pa irs, a fte r a ll).
Furthe rm ore , onc e a va lue is store d in a n a list, it re m a ins the re fore ve r, m a king it e a sy to a udit the history of a ny da ta . For
insta nc e , in our c offe e e xa m ple , the orde r L isa pla c e d for he r drip c offe e is still a va ila ble e ve n a fte r it ha s be e n re pla c e d.
Howe ve r, a lists do ha ve one se rious lim ita tion: T he y a re not a ve ry e ffic ie nt wa y to store a nd re trie ve da ta , unle ss you’re de a ling
with ve ry short lists (unde r a doz e n ite m s). Be c a use of this ine ffic ie nc y, a lthough a lists a re ofte n one of the first tools in the L isp
progra m m e r’s toolbox, the y m a y be re pla c e d by othe r type s of da ta struc ture s a s a progra m m a ture s. (In Cha pte r 9, we ’ll disc uss the
pe rform a nc e lim ita tions of list-ba se d da ta struc ture s, suc h a s a lists, in gre a te r de ta il. )
Coping with Complic ate d Data
Cons c e lls a re a gre a t tool for re pre se nting a wide va rie ty of list-like struc ture s. In fa c t, m ost L isp progra m m e rs, whe n fa c e d with
a progra m m ing ta sk tha t is not bound by pe rform a nc e c onstra ints, will re ly on the m a lm ost e xc lusive ly. Be c a use the m a nipula tion
a nd visua liz a tion of struc ture s m a de of c ons c e lls a re c e ntra l to the de sign of L isp, the se struc ture s a re e xtre m e ly c onve nie nt to use
a nd de bug.
In fa c t, e ve n if you do ha ve pe rform a nc e c onstra ints, struc ture s m a de of c ons c e lls c a n ofte n be a gre a t c hoic e . A L isp c om pile r
c a n ofte n re duc e a c ha nge to a c ons c e ll down to a single a sse m bly instruc tion!
Visualiz ing Tr e e -like Data
As disc usse d in Cha pte r 3, the da ta (a nd c ode ) in a L isp progra m is re pre se nte d with synta x e xpre ssions. In this form a t, da ta is
re pre se nte d using ne ste d lists, ofte n with L isp sym bols a t the front of e a c h list e xpla ining the struc ture of the da ta .
For e xa m ple , suppose we wa nte d to re pre se nt the c om pone nt pa rts of a house in L isp:
(defparameter *house* '((walls (mortar (cement)
(water)
(sand))
(bricks))

(windows (glass)
(frame)

(curtains))
(roof (shingles)
(chimney))))
T his da ta struc ture ve ry e le ga ntly c a pture s the hie ra rc hic a l na ture of the pa rts tha t m a ke up a house . Sinc e it is struc ture d a s a
L isp synta x e xpre ssion, we c a n se e the lists tha t m a ke up the le ve ls of the hie ra rc hy. Also, it follows the c onve ntion of a synta x
e xpre ssion by putting a sym bol a t the front of e a c h list. For insta nc e , we c a n se e how the list de sc ribing the windows first c onta ins

the L isp sym bol windows , whic h is the n followe d by thre e ite m s, re pre se nting the gla ss, fra m e , a nd fina lly the c urta ins .
As you c a n se e , da ta tha t is hie ra rc hic a l a nd tre e -like in na ture c a n be ve ry na tura lly e xpre sse d in this wa y. In fa c t, m a ny
L ispe rs c onside r XML (a popula r form a t for re pre se nting hie ra rc hic a l da ta ) som e wha t of a re inve ntion of the synta x e xpre ssion
form a t tha t L isp pione e re d.
If, howe ve r, we m ove be yond tre e -like struc ture s, da ta store d in a synta x e xpre ssion c a n sta rt be c om ing ha rd to visua liz e , e ve n if
it’s re la tive ly e a sy to store the da ta in c ons c e lls. For insta nc e , suppose we ha ve a m a the m a tic a l gra ph store d in a synta x
e xpre ssion. T he se type s of gra phs, whe re a ny a rbitra ry node of the gra ph m a y be c onne c te d to a nothe r by a n e dge , a re notoriously
ha rd to visua liz e in a c om pute r progra m . E ve n L isp’s e le ga nt syste m for re pre se nting c ons c e lls c a n’t he lp m uc h for suc h da ta .
Ne xt, we ’ll look a t our options for visua liz ing suc h gra phs.
Visualiz ing G r aphs
In m a the m a tic s, a graph c onsists of a bunc h of node s c onne c te d by e dge s. T he se node s or e dge s m ight ha ve a dditiona l da ta
a ssoc ia te d with the m .
Suc h gra phs c a n be store d in c ons c e lls, but the y a re diffic ult to visua liz e . W e sa w this in Cha pte r 5, whe n we store d the m a p of
the wiz a rd’s house (whic h c onsiste d of a dire c te d gra ph) in two a lists: one c onta ining the node inform a tion a nd one c onta ining the
e dge inform a tion. I’ve re na m e d the m *wizard-nodes* a nd *wizard-edges* for this c ha pte r, a s shown he re :
(defparameter *wizard-nodes* '((living-room (you are in the living-room.
a wizard is snoring loudly on the couch.))
(garden (you are in a beautiful garden.
there is a well in front of you.))
(attic (you are in the attic. there
is a giant welding torch in the corner.))))
(defparameter *wizard-edges* '((living-room (garden west door)
(attic upstairs ladder))
(garden (living-room east door))
(attic (living-room downstairs ladder))))
As you c a n se e , it is ha rd to ge t a de c e nt unde rsta nding of the struc ture of this ga m e world from the se ra w da ta ta ble s.
Unfortuna te ly, da ta tha t ha s the sha pe of a gra ph or c onta ins othe r prope rtie s tha t go be yond sim ple tre e struc ture s a re ve ry
c om m on. W ouldn’t it be gre a t if we ha d a tool tha t c ould optim a lly a rra nge this da ta to c re a te a pre tty dra wing of a gra ph?
L uc kily, the re is a fa nta stic ope n sourc e tool tha t pe rform s e xa c tly this ta sk, whic h you’ll try out ne xt.
Cr e ating a G r aph
Gra phviz ge ne ra te s gra phs from your da ta . Inde e d, you sa w a sim ple Gra phviz re pre se nta tion of the wiz a rd’s house in Cha pte r 5:

Gra phviz is ope n sourc e a nd a va ila ble from the Gra phviz we bsite (http://www. gra phviz . org/). Afte r you’ve downloa de d a nd
insta lle d it, c re a ting a gra ph is e a sy. First, you’ll c re a te a DOT file tha t de sc ribe s the sha pe of your gra ph. For e xa m ple , in
Gra phviz , c re a te a file na m e d te st. dot on your c om pute r a nd e nte r the following inform a tion:
digraph {
a->b;
}
T his de fine s a dire c te d gra ph with node s A a nd B c onne c te d by a n a rrow. (T he re a re num e rous synta x options a va ila ble in the
DOT file form a t, a s doc um e nte d a t the Gra phviz we bsite . )
Now, to ge ne ra te a gra phic a l bitm a p from the DOT file , run neato (one of the Gra phviz utilitie s) from the c om m a nd line , a s
follows:
neato -Tpng -O test.dot
T his should c re a te a pic ture in the file te st. dot. png tha t looks like this:

As you c a n se e , Gra phviz is sim ple to use . It c a n e ve n ge ne ra te la rge , c om plic a te d gra phs quic kly, with only m inor gra phic a l
glitc he s. (Sinc e pe rfe c t gra ph la youts a re still a n unsolve d proble m in c om pute r sc ie nc e , Gra phviz la youts a re n’t pe rfe c t. T he y a re ,
howe ve r, c lose r to pe rfe c t tha n you m ight e xpe c t. )
Now tha t you ha ve Gra phviz up a nd running, le t’s c re a te a libra ry of c om m a nds tha t will le t us c onve nie ntly dra w gra phs with
L isp. W e c a n use this to dra w som e gra phs of our a dve nture ga m e world.

Note

T he gra ph utilitie s use d in the e xa m ple s in this c ha pte r pe rform c e rta in syste m c a lls in a wa y tha t is not pa rt of the Com m on
L isp sta nda rd. T he y a re a va ila ble only in the CL ISP e nvironm e nt. T he c ode would re quire som e m odific a tions to run within othe r
L isp syste m s.
G e ne r ating the DO T Infor mation
In orde r to c re a te a gra ph dra wing libra ry, we wa nt to ge ne ra te a Gra phviz DOT file tha t c a pture s a ll the de ta ils of a gra ph. T o
do this, we will ne e d to c onve rt the ide ntifie rs of the node s the pla ye r c a n visit, c onve rt the e dge s c onne c ting the se node s, a nd
ge ne ra te la be ls for e ve ry node a nd e dge . W e will te st our libra ry using the node s re pre se nting the m a p of the wiz a rd’s world.

Conve r ting Node Ide ntifie r s

W he n c onve rting node s into DOT form a t, the first thing we ne e d to do is to c onve rt the node ide ntifie rs into va lid DOT
ide ntifie rs. W e do this by writing a dot-name func tion:
(defun dot-name (exp)
(substitute-if #\_ (complement #'alphanumericp) (prin1-to-string exp)))
A node in DOT form a t c a n c onta in only le tte rs, digits, a nd the unde rsc ore c ha ra c te r. T o m a ke sure the node ide ntifie r we ’re
using is le ga l, we ’ll c ha nge a ny forbidde n c ha ra c te rs to unde rsc ore s. He re a re e xa m ple s of the dot-name func tion in use :
> (dot-name 'living-room)
"LIVING_ROOM"
> (dot-name 'foo!)
"FOO_"
> (dot-name '24)
"24"
T his func tion a c c e pts a ny ba sic L isp type , whic h we c a n the n c onve rt to a string using the prin1-to-string func tion. W e c a n
proc e ss the re sulting string a nd substitute unde rsc ore s a s ne e de d.

Note

For the sa ke of sim plic ity, our dot-name func tion a ssum e s tha t no node ide ntifie rs diffe r only in the ir nona lpha num e ric
c om pone nts. For insta nc e , if we ha d one node c a lle d foo? a nd a nothe r node c a lle d foo*, the dot-name func tion would c onve rt
the m both to foo, c a using the na m e s to c la sh.

T he substitute-if func tion substitute s va lue s ba se d on the re sult of a te st func tion:


> (substitute-if #\e #'digit-char-p "I'm a l33t hack3r!")
"I'm a leet hacker!"
T he te st func tion in this e xa m ple , digit-char-p, te lls us if a c ha ra c te r in a string is a num e ric a l digit. T e st func tions like this,
whic h a c c e pt a va lue a nd de te rm ine truth ba se d on tha t va lue , a re ofte n re fe rre d to a s pre dic ate s.
Anothe r inte re sting prope rty of the substitute-if func tion is tha t we c a n use it on lists a s we ll:
> (substitute-if 0 #'oddp '(1 2 3 4 5 6 7 8))
'(0 2 0 4 0 6 0 8)
He re , a ll odd num be rs in a list ha ve be e n re pla c e d by the num be r 0. T he substitute-if func tion is one e xa m ple of a ge ne ric
func tion— a func tion tha t c a n a c c e pt m ultiple da ta type s a s pa ra m e te rs a nd ha ndle the m a ppropria te ly. (Ge ne ric progra m m ing is
disc usse d in Cha pte r 9. )
W he n we use substitute-if in our dot-name func tion, we substitute only those c ha ra c te rs tha t a re n’t a lpha num e ric . W hile no
pre dic a te tha t te sts for e xa c tly this is a va ila ble for us in Com m on L isp, it is e a sy to c re a te this pre dic a te on the fly. T he following
fra gm e nt in the dot-name func tion c re a te s a pre dic a te func tion for us with e xa c tly the right be ha vior:
(complement #'alphanumericp)
L isp a lre a dy ha s a pre dic a te func tion tha t te lls us if a c ha ra c te r is a lpha num e ric , c a lle d alphanumericp. Howe ve r, we wa nt to
substitute only c ha ra c te rs tha t a re not a lpha num e ric . W e c a n c re a te this opposite (or c omple me nt) func tion to alphanumericp by
pa ssing it to a highe r-orde r func tion na m e d complement.
By pa ssing this func tion into substitute-if, we ge t the be ha vior we wa nt, without ne e ding to use defun to pollute the top le ve l
with a ne w func tion just to fe e d to substitute-if.

Note

Com m on L isp ha s a func tion c a lle d substitute-if-not tha t c ould ha ve be e n use d in the dot-name func tion in lie u of
substitute-if to a llow us to le a ve the not out of the lambda func tion. Howe ve r, L isp func tions tha t e nd in not a re be tte r a voide d.
T he y m a y be re m ove d from future ve rsions in the ANSI Com m on L isp sta nda rd, whic h m e a ns the y a re c onside re d de pre c a te d.

Adding Labe ls to G r aph Node s

Now tha t we c a n twe a k our node ide ntifie rs to m a ke the m a ppropria te for DOT , le t’s write a nothe r func tion tha t will ge ne ra te
the la be l tha t should a ppe a r in the node whe n it is dra wn. T he la be l will c onsist of the node na m e a nd the da ta linke d to the node
in the node a list. But we a lso ne e d to m a ke sure tha t we a re not trying to put too m uc h te xt in the la be l. He re is the c ode tha t
ge ne ra te s the la be l:

(defparameter *max-label-length* 30)

(defun dot-label (exp)


(if exp

(let ((s (write-to-string exp :pretty nil)))

(if (> (length s) *max-label-length*)

(concatenate 'string (subseq s 0 (- *max-label-length* 3)) "...")


s))
""))

*max-label-length* is a globa l va ria ble tha t de te rm ine s the m a xim um num be r of c ha ra c te rs for the la be l. If a node la be l

is la rge r tha n the lim it , it ge ts c roppe d, a nd a n e llipsis is a dde d to indic a te tha t fa c t . T he write-to-string func tion

is sim ila r to the prin1-to-string func tion we use d e a rlie r— it write s a n e xpre ssion to a string.
T he :pretty pa ra m e te r is a n e xa m ple of a k e y word parame te r, whic h is use d by c e rta in L isp func tions to le t you c hoose whic h
pa ra m e te rs you wa nt to pa ss in. In the c a se of write-to-string, it te lls L isp not to a lte r the string to m a ke it pre tty. W ithout this,
L isp would pla c e ne w line s or ta bs into our c onve rte d string to m a ke it look m ore ple a sing to the e ye . By se tting the :pretty
ke yword pa ra m e te r to nil, we a re te lling L isp to output the e xpre ssion without a ny de c ora tions. (Ha ving ne w line s in a la be l c a n
c onfuse Gra phviz , so we don’t wa nt to give L isp a ny ide a s. )

G e ne r ating the DO T Infor mation for the Node s

Now tha t we c a n ge ne ra te both a na m e a nd la be l for e a c h node , we c a n write a func tion tha t ta ke s a n a list of node s a nd
ge ne ra te s the DOT inform a tion tha t e nc ode s the m , like so:
(defun nodes->dot (nodes)

(mapc (lambda (node)


(fresh-line)

(princ (dot-name (car node)))


(princ "[label=\"")

(princ (dot-label node))


(princ "\"];"))
nodes))

T his func tion use s mapc to go through e ve ry node in the list of node s , a nd princ prints e a c h node in the DOT form a t
dire c tly to the sc re e n. mapc is a slightly m ore e ffic ie nt va ria nt of mapcar; the diffe re nc e is tha t it doe s not re turn the tra nsform e d

list. T he nodes->dot func tion use s the dot-name a nd dot-label func tions we c re a te d to c onve rt the da ta .
L a te r, whe n we wa nt to ge ne ra te a file tha t c onta ins this inform a tion, we ’ll write a func tion tha t ta ke s this da ta from the
c onsole .
It m a y se e m a bit odd to use the c onsole a s a n inte rm e dia ry for ge ne ra ting a file , inste a d of just writing dire c tly into a file , but
this is a c tua lly a c om m on pa ra digm in L isp. One im m e dia te be ne fit of this a pproa c h is tha t we c a n e a sily de bug the c ode in the
RE PL , whe re the printe d line s a re e a sy to se e .
Now le t’s try using the nodes->dot func tion to ge ne ra te the DOT inform a tion for the node s in the wiz a rd’s house :
> (nodes->dot *wizard-nodes*)
LIVING_ROOM[label="(LIVING-ROOM (YOU ARE IN TH..."];
GARDEN[label="(GARDEN (YOU ARE IN A BEAUT..."];
ATTIC[label="(ATTIC (YOU ARE IN THE ATTI..."];
He re , you c a n se e the node s of the wiz a rd’s house a nd a n a bbre via te d ve rsion of the inform a tion a tta c he d to e a c h node , shown in
DOT form a t. Notic e tha t we a re not inte re ste d in the va lue re turne d from the nodes->dot func tion— only in the inform a tion it
prints in the RE PL . L ispe rs would sa y tha t we a re only inte re ste d in the side e ffe c ts of this func tion. Although mapc doe s not re turn
the list, it still c a use s the c ode to ite ra te through the list a nd ge ne ra te the sa m e printe d output tha t using mapcar would ha ve , so it
ge ne ra te s the sa m e side e ffe c ts a s mapcar, a bit m ore quic kly.

Conve r ting Edge s into DO T F or mat

T he ne xt ste p is to ge ne ra te the DOT inform a tion for the e dge s tha t link our node s. T he se will be c om e the a rrows in our visua l
gra ph. T he func tion edges->dot ge ne ra te s the ne c e ssa ry da ta , a ga in by printing it dire c tly to the c onsole .
(defun edges->dot (edges)
(mapc (lambda (node)
(mapc (lambda (edge)
(fresh-line)
(princ (dot-name (car node)))
(princ "->")
(princ (dot-name (car edge)))
(princ "[label=\"")
(princ (dot-label (cdr edge)))
(princ "\"];"))
(cdr node)))
edges))
L e t’s use this func tion to ge ne ra te the DOT inform a tion for the e dge s of the wiz a rd’s house :
> (edges->dot *wizard-edges*)

LIVING_ROOM->GARDEN[label="(WEST DOOR)"];
LIVING_ROOM->ATTIC[label="(UPSTAIRS LADDER)"];
GARDEN->LIVING_ROOM[label="(EAST DOOR)"];
ATTIC->LIVING_ROOM[label="(DOWNSTAIRS LADDER)"];
He re , we c a n c le a rly se e the re la tionships be twe e n the node s in the wiz a rd’s house , in the DOT form a t. For insta nc e , the first
line indic a te s tha t the pla ye r c a n wa lk from the LIVING_ROOM node to the GARDEN node by using a n e dge la be le d (WEST
DOOR).

G e ne r ating All the DO T Data

T o c om ple te our ge ne ra tion of the DOT da ta , we c a ll both nodes->dot a nd edges->dot, a nd wra p it up with som e e xtra
de c ora tion, a s follows:
(defun graph->dot (nodes edges)

(princ "digraph{")

(nodes->dot nodes)

(edges->dot edges)
(princ "}"))

T his func tion tie s e ve rything toge the r by de fining our gra ph a s a dire c tiona l gra ph , a nd the n c a lling our nodes->dot

a nd edges->dot func tions.


He re ’s wha t the fina l DOT inform a tion for our wiz a rd ga m e looks like , a s c re a te d by our ne w libra ry:
> (graph->dot *wizard-nodes* *wizard-edges*)
digraph{
LIVING_ROOM[label="(LIVING-ROOM (YOU ARE IN TH..."];
GARDEN[label="(GARDEN (YOU ARE IN A BEAUT..."];
ATTIC[label="(ATTIC (YOU ARE IN THE ATTI..."];
LIVING_ROOM->GARDEN[label="(WEST DOOR)"];
LIVING_ROOM->ATTIC[label="(UPSTAIRS LADDER)"];
GARDEN->LIVING_ROOM[label="(EAST DOOR)"];
ATTIC->LIVING_ROOM[label="(DOWNSTAIRS LADDER)"];}
W e c a n now ge ne ra te a prope r Gra phviz DOT file tha t c a pture s a ll the de ta ils of our wiz a rd m a p tha t we ne e d to ge ne ra te a
pre tty pic ture . T he se inc lude the node s the pla ye r c a n visit, the e dge s c onne c ting the se node s, a nd la be ls for e ve ry node a nd e dge .
Tur ning the DO T F ile into a P ic tur e
T o turn the DOT file into a n a c tua l bitm a p, we c a pture the DOT file da ta , put it into a file , a nd the n e xe c ute the dot c om m a nd
dire c tly from the syste m c om m a nd line , like this:

(defun dot->png (fname thunk)

(with-open-file (*standard-output*
fname
:direction :output
:if-exists :supersede)
(funcall thunk))
(ext:shell (concatenate 'string "dot -Tpng -O " fname)))
T his func tion pe rform s the m ost c ritic a l a c tions in our gra ph dra wing libra ry, using som e a dva nc e d L isp te c hnique s.
First, to ke e p this dot->png func tion a s re usa ble a s possible , the graph->dot func tion isn’t c a lle d dire c tly. Inste a d, we write dot-

>png to a c c e pt a thunk .

Using Thunks

It is c om m on in L isp to c re a te sm a ll func tions tha t ha ve z e ro a rgum e nts. T he se func tions a re offic ia lly c a lle d nullary func tions.
Howe ve r, L ispe rs will ofte n c re a te suc h func tions in orde r to de sc ribe a c om puta tion tha t the y don’t wa nt to run until la te r. In this
sc e na rio, a func tion without a rgum e nts is c om m only c a lle d a thunk or a suspe nsion. In this c a se , the thunk our dot->png func tion
ne e ds would be a func tion tha t, whe n c a lle d, prints a DOT file to the c onsole .
W hy is a thunk use ful in our dot->png func tion? Re m e m be r tha t the e a sie st wa y for us to write a nd de bug graph->dot a nd othe r
DOT file func tions is to ha ve the m print the ir re sults dire c tly to the c onsole . W he n we c a ll graph->dot, it doe sn’t re turn its re sults
a s a va lue , but, inste a d, prints the m a t the c onsole a s a side e ffe c t. T he re fore , we c a n’t just pa ss the va lue of graph->dot to dot-
>png. Inste a d, we pa ss in graph->dot a s a thunk. T he n dot->png is re sponsible for c a lling graph->dot, c a pturing the re sults, a nd
se nding the m to a file .
Sinc e it is so c om m on to ge ne ra te te xtua l da ta with a c om pute r progra m , this pa rtic ula r te c hnique is use d a lot in L isp c ode :
First, we print stuff right to the c onsole ; ne xt, we wra p it in a thunk; fina lly, we re dire c t the re sults to som e othe r loc a tion.
As you’ll se e in Cha pte r 14, L ispe rs who follow the func tiona l progra m m ing style e sc he w this te c hnique , be c a use side e ffe c ts a re
re quire d whe n printing to the c onsole .

W r iting to a F ile

T he func tion with-open-file e na ble s dot->png to write inform a tion to a file . T o give you a fe e l for how this func tion
works, he re ’s a n e xa m ple tha t c re a te s a ne w file na m e d te stfile . tx t a nd write s the te xt “ He llo File !” to it:

(with-open-file (my-stream
"testfile.txt"
:direction :output
:if-exists :supersede)
(princ "Hello File!" my-stream))

In this e xa m ple , you c a n se e tha t the first ite m pa sse d into with-open-file be c om e s the na m e of a spe c ia l Com m on L isp
da ta type c a lle d a stre am, whic h is c re a te d for us by with-open-file.

Cr e ating a Str e am

Printing func tions, suc h a s princ, c a n a c c e pt a stre a m a s a n optiona l pa ra m e te r. In this c a se , the se printing func tions won’t print
a nything to the c onsole , but inste a d will print to the stre a m obje c t.
It is im porta nt to unde rsta nd tha t with-open-file c re a te s a stre a m va ria ble from a stre a m va ria ble na m e , in the sa m e wa y tha t
let c re a te s a va ria ble from a va ria ble na m e :

(with-open-file (my-stream ...)

...body has my-stream defined...)

(let ((my-variable ...))

...body has my-variable defined...)

So if we pa ss the na m e my-stream in a t the front of the first list to with-open-file , this is a na logous to de fining my-

variable a t the sta rt of a let . A va ria ble na m e d my-stream will be a va ila ble to us in the body of with-open-file , in

the sa m e wa y tha t my-variable will be a va ila ble to us in the body of the let .
But don’t worry too m uc h a bout e xa c tly wha t a stre a m is just ye t. W e ’ll be looking a t the m m ore c lose ly in Cha pte r 12. For
now, you just ne e d to know tha t a stre a m is a n obje c t tha t c a n be c onne c te d to a file , a nd we c a n pa ss it to func tions (suc h a s
princ) to write stuff to the c onne c te d file .
Unde r standing K e ywor d P ar ame te r s

T h e with-open-file c om m a nd a lso m a ke s he a vy use of ke yword pa ra m e te rs. L e t’s look a t our pre vious e xa m ple of this
c om m a nd a ga in:
(with-open-file (my-stream
"testfile.txt"

:direction :output

:if-exists :supersede)
(princ "Hello File!" my-stream))
A ke yword pa ra m e te r ha s two pa rts: the na m e of the pa ra m e te r a nd the va lue of the pa ra m e te r. T he na m e of the pa ra m e te r is

a lwa ys a sym bol be ginning with a c olon. T his e xa m ple ha s two ke yword pa ra m e te rs: :direction , whic h is se t to :output

(we ’re only writing to the file a nd not re a ding it), a nd :if-exists , whic h is se t to :superseded (if a file by tha t na m e
a lre a dy e xists, just toss out the old ve rsion).
with-open-file ha s ke yword pa ra m e te rs be c a use ope ning a file is a c om ple x ope ra tion, a nd m a ny e sote ric options a re a va ila ble .
If with-open-file just ga ve you re gula r pa ra m e te rs to se t a ll this, e ve ry c a ll to with-open-file would be long a nd c um be rsom e
due to a ll the pa ra m e te rs. Also, hum a ns ha ve a ha rd tim e looking a t a long list of pa ra m e te rs a nd re m e m be ring whic h one doe s
wha t.
As you’ve proba bly notic e d, sym bols in Com m on L isp som e tim e s be gin with a c olon. T his inc lude s ke yword pa ra m e te rs, whic h
a lwa ys sta rt with a c olon. T his is be c a use a re gula r sym bol in L isp c a n re fe r to som e thing e lse . For insta nc e , we c ould se t a
va ria ble cigar e qua l to 5 a nd the n re turn it:
> (let ((cigar 5))
cigar)
5
Howe ve r, som e tim e s we don’t wa nt a sym bol to re fe r to som e thing e lse . W e wa nt to use the sym bol outright, a nd we wa nt it to
ha ve its own m e a ning. A c olon-pre pe nde d sym bol in Com m on L isp (not surprisingly, c a lle d a k e y word sy mbol) a lwa ys m e a ns itse lf:

> :cigar
:CIGAR

> (let ((:cigar 5))


:cigar)
*** - LET: :CIGAR is a constant, may not be used as a variable

As you c a n se e , the ke yword sym bol :cigar c a n be e va lua te d right a t the RE PL a nd a lre a dy ha s a va lue . Its va lue is,

c onve nie ntly, :cigar. If we try to re de fine :cigar to som e thing e lse , Com m on L isp won’t le t us . T he fa c t tha t it is c onsta nt
is use ful, be c a use a L isp c om pile r c a n pote ntia lly optim iz e this sim ple type of sym bol m ore tha n it c a n optim iz e othe r type s. Also,
we c a n re duc e e rrors in our c ode by using ke yword sym bols in pla c e s whe re we know a sym bol just ha s a m e a ning in its own right.
Som e tim e s a c iga r is just a c iga r.

Captur ing the Console O utput

Our dot->png se nds our da ta to the file in a slightly diffe re nt wa y tha n is shown in this e xa m ple : by de c la ring the na m e of the
stre a m to be *standard-output* (a spe c ia l globa l va ria ble in Com m on L isp tha t c ontrols the de fa ult loc a tion to whic h printing
func tions se nd the ir output). As a re sult, a ny printing done inside the thunk will be re dire c te d to our DOT file .
L e t’s look a t our dot->png func tion a ga in to se e this:
(defun dot->png (fname thunk)

(with-open-file (*standard-output*
fname
:direction :output
:if-exists :supersede)

(funcall thunk))
(ext:shell (concatenate 'string "dot -Tpng -O " fname)))
So how e xa c tly doe s the dot->png func tion c a use our DOT da ta to ge t sa ve d to a file inste a d of just going to the c onsole ? T o
a nswe r this, you’ll ne e d to e xe rc ise your bra in a bit. Also, you’ll ne e d to re c a ll our disc ussion of loc a l a nd dyna m ic va ria ble s in
Cha pte r 2.
Re m e m be r tha t the le t c om m a nd usua lly c re a te s a le x ic al, or loc a l, va ria ble . As we ’ve disc usse d, the stre a m va ria ble c re a te d by
with-open-file is a na logous to using let to c re a te a va ria ble . He nc e , it usua lly le a ds to the c re a tion of a le xic a l stre a m va ria ble
for us.
Howe ve r, if a dyna m ic va ria ble a lre a dy e xists with the sa m e na m e , let will inste a d, te m pora rily, ove rride the va lue of the
dyna m ic va ria ble to the ne w va lue . *standard-output* is suc h a dyna m ic va ria ble . T his m e a ns tha t we c a n te m pora rily ove rride

the va lue of *standard-output* to a ne w va lue by pa ssing it into our with-open-file c om m a nd .

In the body of the with-open-file, whe re we c a ll our thunk , a ny va lue s printe d to the c onsole will now be a utom a gic a lly
route d to our file , inste a d. T he surprising thing (e na ble d by the de sign of le xic a l a nd dyna m ic va ria ble s in Com m on L isp) is tha t
this is a lso true for the princ sta te m e nts in our graph->dot func tion, e ve n though the y a re c a lle d indire c tly from dot->png.
Cr e ating a P ic tur e of O ur G r aph
L a stly, we ne e d a func tion tha t tie s toge the r a ll the pie c e s to le t us e a sily c re a te a gra ph from som e node s a nd e dge s:

(defun graph->png (fname nodes edges)

(dot->png fname

(lambda ()

(graph->dot nodes edges))))

T his func tion ta ke s the na m e of a DOT file (a s the va ria ble fname), a s we ll a s the gra ph’s node s a nd e dge s , a nd use s the m

to ge ne ra te the gra ph. T o do this, it c a lls dot->png a nd c re a te s the a ppropria te thunk— a lambda func tion . As is usua l
for a thunk, it ta ke s no pa ra m e te rs.

T he graph->dot func tion is c a lle d inside the thunk a s a de lay e d c omputation. Spe c ific a lly, if we ha d c a lle d graph->dot
dire c tly, its output would just show up in the c onsole . Howe ve r, whe n inside the thunk, it will be c a lle d a t the le isure of the dot-
>png func tion, a nd the output will be use d to ge ne ra te the DOT file with the file na m e pa sse d in a s the first pa ra m e te r to graph-
>png.
L e t’s try out our ne w func tion to dra w a gra ph of the wiz a rd’s house !
(graph->png "wizard.dot" *wizard-nodes* *wizard-edges*)
Afte r c a lling this func tion, you should now se e a file na m e d wizard. dot. png, a pic ture of the m a p of the wiz a rd’s house :

T his m a y not be the pre ttie st gra ph on the pla ne t, but it’s pa c ke d with inform a tion a nd is ve ry e a sy to unde rsta nd. Also, the c ode
is e xtre m e ly fle xible , a nd pla c e s fe w de pe nde nc ie s on our node a nd e dge da ta .
W ith the se utilitie s in our a rse na l, we c a n now e a sily c re a te gra phs from a ny inte rc onne c te d da ta in our L isp progra m s. You’ll
find this te c hnique to be a va lua ble de bugging tool whe n you ne e d to de a l with c om plic a te d da ta .
Cr e ating Undir e c te d G r aphs
A gra ph tha t ha s a rrows on its e dge s is c a lle d a dire c te d graph:

But som e tim e s we ha ve da ta tha t is undire c te d, a llowing us to tra ve l in both dire c tions a long a n e dge . Suc h a gra ph is le ss busy
tha n a dire c te d gra ph, a nd c a n be e a sie r to unde rsta nd:
T he following c ode e xpa nds our gra ph utilitie s with ne w func tions tha t le t us dra w undire c te d gra phs:
(defun uedges->dot (edges)

(maplist (lambda (lst)


(mapc (lambda (edge)

(unless (assoc (car edge) (cdr lst))


(fresh-line)
(princ (dot-name (caar lst)))
(princ "--")
(princ (dot-name (car edge)))
(princ "[label=\"")
(princ (dot-label (cdr edge)))
(princ "\"];")))
(cdar lst)))
edges))

(defun ugraph->dot (nodes edges)

(princ "graph{")
(nodes->dot nodes)
(uedges->dot edges)
(princ "}"))

(defun ugraph->png (fname nodes edges)


(dot->png fname
(lambda ()
(ugraph->dot nodes edges))))
T his c ode is ve ry sim ila r to the c ode for c re a ting our dire c te d gra phs. L e t’s look a t som e of the diffe re nc e s.

T h e uedges->dot func tion is ve ry sim ila r to the edges->dot func tion . Howe ve r, the gra ph we ’re dra wing m a y ha ve
m ultiple dire c te d e dge s be twe e n the sa m e node s tha t we wa nt to re pla c e with a single , undire c te d e dge . For insta nc e , on our
wiz a rd m a p, we c a n wa lk from the ga rde n to the living room by going e ast through the door. Of c ourse , we c a n a lso wa lk from the
living room to the ga rde n by going we st through the e xa c t sa m e door. In our undire c te d gra ph, we ’ll wa nt to c olla pse this; in
e sse nc e , we just wa nt to sa y, “ T he re ’s a door be twe e n the ga rde n a nd living room . ”
T he uedges->dot func tion e ra se s suc h duplic a te e dge s by running through the list of e dge s using the maplist func tion. T his is
like the mapcar func tion, e xc e pt tha t the func tion inside it re c e ive s the e ntire re m a inde r of the list, not just the c urre nt ite m in the
list:
> (mapcar #'print '(a b c))
A
B
C
...
> (maplist #'print '(a b c))
(A B C)
(B C)
(C)
...

T he maplist func tion se nds the print func tion e ve rything in the list from the c urre nt ite m until the e nd. uedges->dot
the n use s the inform a tion a bout future node s it ge ts from maplist to c he c k whe the r the de stina tion of the node a ppe a rs la te r in the
e dge list. T he a c tua l c he c king is done with the assoc func tion, looking for the c urre nt e dge in the list of re m a ining e dge s,

c a lc ula te d a s (cdr lst) . In this c a se , it skips the e dge so tha t only one of a ny pa ir of e dge s will be printe d.

T he ugraph->dot func tion is sim ila r to the graph->dot func tion, e xc e pt tha t it de sc ribe s the gra ph a s just a gra ph

whe n ge ne ra ting the DOT da ta , inste a d of a digra ph. T he ugraph->png func tion is e sse ntia lly ide ntic a l to the graph->png
func tion, e xc e pt tha t it c a lls ugraph->dot inste a d of graph->dot.
W e de signe d the dot->png func tion to a c c e pt diffe re nt thunks so it c ould work with diffe re nt DOT da ta ge ne ra tors. Now we ’ve
use d this fle xibility to ge ne ra te the se func tions tha t output pic ture s for undire c te d gra phs. For e xa m ple , le t’s try ge ne ra ting a n
undire c te d gra ph for the wiz a rd’s house :
(ugraph->png "uwizard.dot" *wizard-nodes* *wizard-edges*)
He re , "uwizard.dot" is the na m e of the DOT file we wa nt to c re a te . T he *wizard-nodes* a nd *wizard-edges* va ria ble s
c onta in the da ta de sc ribing the node s a nd e dge s of the m a p of the wiz a rd’s world. T his c ode ge ne ra te s the uwizard. dot. png file ,
whic h looks like this:

Now tha t you ha ve a full suite of utilitie s for both dire c te d a nd undire c te d gra phs, write the se func tions to a file na m e d graph-
util. lisp, so you c a n a c c e ss the m from othe r progra m s.
W hat You've Le ar ne d
In this c ha pte r, we disc usse d e xotic type s of lists a nd c re a te d a dra wing libra ry for m a the m a tic a l gra phs. Along the wa y, you
le a rne d the following:
You c a n c re a te lists in L isp tha t e nd in a va lue othe r tha n nil. Suc h lists a re displa ye d with a n e xtra dot be fore the la st ite m
a nd a re c a lle d dotte d lists.
P airs a re wha t you ge t whe n you c ons toge the r two ite m s tha t a re not lists the m se lve s. T he y c a n a lso be thought of a s dotte d
lists tha t c onta in only two ite m s.
Circ ular lists a re lists whe re the la st c ons c e ll points to a n e a rlie r c ons c e ll in the sa m e list.
A ssoc iation lists (alists) a re lists of pa irs. T he y c a n be use d to store da ta tha t is in the form of ke ys a ssoc ia te d with va lue s.
L isp synta x e xpre ssions a re gre a t for storing a nd visua liz ing list-like a nd hie ra rc hic a l da ta . E xtra tools m a y be he lpful for
visua liz ing m ore c om ple x da ta .
If your da ta is in the form of a m a the m a tic a l gra ph, it’s he lpful to be a ble to ge ne ra te pic ture s of your da ta using Gra phviz .
A c om m on te c hnique for ge ne ra ting te xtua l da ta in a L isp progra m is to write func tions tha t print the te xt to the c onsole for
e a sy de bugging a nd wra p the se func tions in thunks. T he n you c a n se nd the se thunks to othe r func tions, whic h c a pture the c onsole
output a nd route the te xt to the a ppropria te de stina tion, suc h a s writing it to a file .
Chapte r 8. This Ain't Your Daddy's W umpus
In the pre vious c ha pte r, we worke d with m a the m a tic a l gra phs in a sim ple ga m e . Howe ve r, a s a n old-sc hool ge e k, the first thing I
think of whe n I se e the se gra phs is the old ga m e Hunt the W um pus. W he n I wa s nine , I c ould think of nothing m ore fun tha n sitting
in front of m y T I-99/4A a nd pla ying this e xc e lle nt ga m e .
He re is the origina l title sc re e n:
In Hunt the W um pus, you a re a hunte r se a rc hing through a ne twork of c a ve s to find a m yste rious m onste r— the fa ble d W um pus.
Along the wa y, you a lso de a l with ba ts a nd ta r pits. Ah, those we re the da ys!

But, unfortuna te ly, those da ys a re long gone . W e ’re in a ne w m ille nnium now, a nd no one would be im pre sse d by the se c rude
gra phic s a nym ore . And the story line , we ll, le t’s just sa y it sounds a bit c orny by m ode rn sta nda rds. I think we c a n a ll a gre e tha t
Hunt the W um pus is in se rious ne e d of a m a ke ove r. T ha t’s quite a c ha lle nge , but one I think we c a n ha ndle .
T he re fore , I pre se nt to you . . .
The G r and The ft W umpus G ame
In this ne w ve rsion of Hunt the W um pus, you a re the L isp a lie n. You a nd the W um pus ha ve just robbe d a liquor store a nd m a de
off with the loot. Howe ve r, during the e sc a pe , the W um pus de c ide s to double -c ross you a nd run off with the m one y a nd your c a r.
But be fore he drive s off, you m a na ge to c a p him a c ouple of tim e s in the kidne y.

Now you’re in a pre tty tough situa tion. You don’t ha ve a c a r or a ny m one y, a nd no wa y to tra c k down your form e r pa rtne r in
c rim e . But you a lso ha ve no c hoic e . You ha ve your princ iple s, so you’re going to hunt the Wumpus. You know he won’t be a ble to
ge t ve ry fa r with his injurie s. He will m ost like ly ne e d to lie low for a fe w da ys to re c ove r, whic h m e a ns he will still be som e whe re
in Conge stion City. T he proble m is tha t the roa ds in this town a re im possibly c onvolute d, a nd no one c a n find the ir wa y a round,
e spe c ia lly a n out-of-towne r like yourse lf. How a re you e ve r going to find the W um pus in this im possible m a z e ?
L uc kily, be ing the L isp a lie n, you a lwa ys c a rry your trusty poc ke t c om pute r. Using L isp a nd your gra ph utilitie s, you’re fully
e quippe d to a na lyz e c om plic a te d da ta suc h a s the Conge stion City roa dwa ys a nd inte rse c tions. Sure ly, you ha ve the tools to
c onque r this im pe ne tra ble roa d syste m .

T he W um pus ha s be e n your pa rtne r in c rim e for a while now, so you know his MO quite we ll. He will a lwa ys c a re fully sc out out
a ny ne w hiding pla c e be fore he use s it. And sinc e he is injure d, a ny loc a tion one or two bloc ks a wa y (tha t is, one or two gra ph
e dge s a wa y) from his hiding pla c e should be m a rke d with som e te llta le signs: his blood sta ins.
A proble m is tha t he still ha s his trusty AK-47, while you ha ve only a ha ndgun with a single bulle t. If you’re going to ta ke him
out, you’ll ne e d to be a bsolute ly sure you’ve tra c ke d him down. You’ll ne e d to c ha rge into his hide out a nd shoot him down
im m e dia te ly, a nd you’ll ha ve only one c ha nc e to pull this off.

Unfortuna te ly, you a nd the W um pus a re n’t the only c rim ina ls in this town. T he m ost fe a re d outla w group in Conge stion City is
the Grue som e Glowworm Ga ng. T he se guys a re a ba nd of ruthle ss kidna ppe rs. If you run into the m , the y will kidna p you, be a t you
up, rob you, blindfold you, a nd the n kic k you out of the ir c a r a nd le a ve you in som e ra ndom pa rt of town.
L uc kily, the y c a n be a voide d if you know to ke e p a n e ye out for the ir glowing thora xe s (he nc e the ir na m e ). If you se e som e
blinking lights, you know tha t the se guys a re one stre e t a wa y from your c urre nt loc a tion. Also, you know the ga ng ha s e xa c tly thre e
se pa ra te te a m s tha t work the c ity from thre e se pa ra te loc a tions.
Fina lly, you still ne e d to c onte nd with the c ops. You know the y’ve proba bly se t up som e roa dbloc ks in town to try to c a tc h you
a nd the W um pus. You should still be a ble to visit a ny loc a tion in Conge stion City, but you ne e d to be c a re ful whic h stre e ts you
tra ve l. (In othe r words, the c ops will c a tc h you if you tra ve l a long the wrong e dge . ) Unfortuna te ly, you don’t know how m a ny of
the se roa dbloc ks the re m a y be .
As you c a n se e , finding the W um pus a nd ge tting ba c k your m one y a nd c a r will be tough. If you think you’re L isp a lie n e nough to
ta ke on the W um pus, the n le t’s write this ga m e a nd hunt him down!
De fining the Edge s of Conge stion City
T he m a p of Conge stion City will be a n undire c te d gra ph with da ta a ssoc ia te d with e a c h node store d in the va ria ble
*congestion-city-nodes*. T he possible da ta a t e a c h node will inc lude the pre se nc e of the W um pus, a Glowworm te a m , a nd
va rious da nge r signs.
A se t of e dge s store d in *congestion-city-edges* will c onne c t the node s, a nd da ta linke d to the se e dge s will a le rt us to the
pre se nc e of a ny polic e roa dbloc ks. W e de c la re the se a nd othe r globa l va ria ble s a t the top of our progra m using defparameter:

(load "graph-util")

(defparameter *congestion-city-nodes* nil)


(defparameter *congestion-city-edges* nil)
(defparameter *visited-nodes* nil)

(defparameter *node-num* 30)

(defparameter *edge-num* 45)

(defparameter *worm-num* 3)

(defparameter *cop-odds* 15)

W e first loa d our gra ph utilitie s with the load c om m a nd , whic h e va lua te s a ll the c ode in graph-util. lisp (whic h we c re a te d

in the pre vious c ha pte r) so the gra ph utility func tions will be a va ila ble . Notic e tha t Conge stion City will ha ve 30 loc a tions

(node s, de fine d with *node-num*), 45 e dge s (roa ds, de fine d with *edge-num*), a nd 3 worm te a m s (de fine d with *worm-

num*). E a c h stre e t will ha ve a 1-in-15 c ha nc e of c onta ining a roa dbloc k (de fine d with *cop-odds*).
G e ne r ating Random Edge s
Ne xt, we c re a te a ra ndom list of e dge s to c onne c t a ll the node s:

(defun random-node ()
(1+ (random *node-num*)))

(defun edge-pair (a b)
(unless (eql a b)
(list (cons a b) (cons b a))))

(defun make-edge-list ()

(apply #'append (loop repeat *edge-num*

collect (edge-pair (random-node) (random-node)))))

First, we de c la re the random-node func tion , whic h re turns a ra ndom node ide ntifie r. It use s the random func tion, whic h
re turns a ra ndom na tura l num be r le ss tha n the inte ge r you pa ss to it. Sinc e we ’re going to be showing the node ide ntifie rs in our
use r inte rfa c e , we use the 1+ func tion to num be r our node s 1 through 30 (the uppe r lim it be c a use the *node-num* va ria ble is se t to
30), inste a d of 0 through 29.

T he make-edge-list func tion ge ne ra te s the a c tua l list of ra ndom e dge s. It use s the loop c om m a nd to loop *edge-num*

tim e s , a nd the n c olle c ts the re quisite num be r of e dge s . W e ’ll ta ke a c lose r look a t the loop c om m a nd in the ne xt

se c tion. T he gra ph of the c ity is undire c te d, so this func tion use s a he lpe r func tion, edge-pair , to c re a te two dire c te d e dge s
be twe e n the ra ndom ly se le c te d node s. T his e xtra ste p m a ke s se nse onc e you re m e m be r tha t a n undire c te d gra ph is the sa m e a s a
dire c te d gra ph, with two opposing dire c te d e dge s m irroring e a c h undire c te d e dge . (W he n we build our e dge s into a n a list la te r in
this c ha pte r, this ste p will e nsure tha t the list is prope rly form e d. )

L e t’s try the make-edge-list func tion in the CL ISP RE PL :


> (make-edge-list)
((16 . 20) (20 . 16) (9 . 3) (3 . 9) (25 . 18) (18 . 25) (30 . 29)
(29 . 30) (26 . 13) (13 . 26) (12 . 25) (25 . 12) (26 . 22) (22 . 26)
(30 . 29) (29 . 30) (3 . 14) (14 . 3) (28 . 6) (6 . 28) (4 . 8) (8 . 4)
(27 . 8) (8 . 27) (3 . 30) (30 . 3) (25 . 16) (16 . 25) (5 . 21) (21 . 5)
(11 . 24) (24 . 11) (14 . 1) (1 . 14) (25 . 11) (11 . 25) (21 . 9) (9 . 21)
(12 . 22) (22 . 12) (21 . 11) (11 . 21) (11 . 17) (17 . 11) (30 . 21) (21 . 30)
(3 . 11) (11 . 3) (24 . 23) (23 . 24) (1 . 24) (24 . 1) (21 . 19) (19 . 21) (25 . 29)
(29 . 25) (1 . 26) (26 . 1) (28 . 24) (24 . 28) (20 . 15) (15 . 20)
(28 . 25) (25 . 28)
(2 . 11) (11 . 2) (11 . 24) (24 . 11) (29 . 24) (24 . 29)
(18 . 28) (28 . 18) (14 . 15)
(15 . 14) (16 . 10) (10 . 16) (3 . 26) (26 . 3) (18 . 9) (9 . 18) (5 . 12)
(12 . 5) (11 . 18) (18 . 11) (20 . 17) (17 . 20) (25 . 3) (3 . 25))
You se e the pa irs of node num be rs tha t m a ke up the e dge s. T his list of e dge pa irs will form the ske le ton of the Conge stion City
roa d syste m .
Looping with the loop Command
Our make-edge-list func tion e m ploys the powe rful loop c om m a nd, whic h c a n be use d to loop ove r va rious type s of da ta . W e ’ll
be looking a t loop in de ta il in Cha pte r 10. Howe ve r, our ga m e use s loop a fe w tim e s, so le t’s c onside r som e sim ple e xa m ple s to
c la rify how it works.
One ha ndy thing you c a n do with loop is c re a te a list of num be rs. For insta nc e , the following c om m a nd will c re a te a list of 10
one s:
> (loop repeat 10
collect 1)
(1 1 1 1 1 1 1 1 1 1)
W ithin the loop c om m a nd, we spe c ify how m a ny tim e s to repeat, a nd the n spe c ify a n obje c t to collect with e ve ry loop (in this
c a se , the num be r 1).
Som e tim e s, we wa nt to ke e p a running c ount a s we ’re looping. W e c a n do this with the following synta x:
> (loop for n from 1 to 10
collect n)
(1 2 3 4 5 6 7 8 9 10)
In this e xa m ple , we a re sa ying tha t n should loop from 1 to 10. T he n we collect e a c h n a nd re turn it a s a list.
Ac tua lly, we c a n put a ny L isp c ode in the collect pa rt of the loop. In the following e xa m ple , we a dd 100 a s we do our
c olle c ting:
> (loop for n from 1 to 10
collect (+ 100 n))
(101 102 103 104 105 106 107 108 109 110)
P r e ve nting Islands
W e now c a n ge ne ra te ra ndom e dge s. Of c ourse , if we just c onne c t ra ndom node s with ra ndom e dge s, the re ’s no gua ra nte e tha t a ll
of Conge stion City will be c onne c te d be c a use of a ll tha t ra ndom ne ss. For e xa m ple , som e pa rts of the c ity m ight form a n isla nd,
with no c onne c tions to the m a in roa d syste m .

T o pre ve nt this, we ’ll ta ke our list of e dge s, find unc onne c te d node s, a nd c onne c t the se isla nds to the re st of the c ity ne twork
using this c ode :

(defun direct-edges (node edge-list)

(remove-if-not (lambda (x)


(eql (car x) node))
edge-list))

(defun get-connected (node edge-list)

(let ((visited nil))


(labels ((traverse (node)
(unless (member node visited)

(push node visited)

(mapc (lambda (edge)


(traverse (cdr edge)))
(direct-edges node edge-list)))))
(traverse node))
visited))
(defun find-islands (nodes edge-list)
(let ((islands nil))

(labels ((find-island (nodes)


(let* ((connected (get-connected (car nodes) edge-list))
(unconnected (set-difference nodes connected)))
(push connected islands)

(when unconnected
(find-island unconnected)))))
(find-island nodes))
islands))
(defun connect-with-bridges (islands)

(when (cdr islands)


(append (edge-pair (caar islands) (caadr islands))
(connect-with-bridges (cdr islands)))))
(defun connect-all-islands (nodes edge-list)
(append (connect-with-bridges (find-islands nodes edge-list)) edge-list))

First, we de c la re a utility func tion c a lle d direct-edges , whic h finds a ll the e dge s in a n e dge list tha t sta rt from a give n

node . It doe s this by c re a ting a ne w list with a ll e dge s re m ove d (using remove-if-not ) tha t don’t ha ve the c urre nt node in the
car position.

T o find isla nds, we write the get-connected func tion . T his func tion ta ke s a n e dge list a nd a sourc e node a nd builds a list
of a ll node s c onne c te d to tha t node , e ve n if it re quire s wa lking a c ross m ultiple e dge s.

T he usua l wa y to find c onne c te d node s is to sta rt a visited list , a nd the n pe rform a se a rc h a long c onne c te d node s, sta rting

with the sourc e node . Ne wly found node s a re a dde d to the visite d list with the push c om m a nd . W e a lso tra ve rse a ll the

c hildre n of this found node , using mapc .


If, on the othe r ha nd, we e nc ounte r a node tha t ha s a lre a dy be e n visite d, we know we c a n ignore it. Onc e the se a rc h is c om ple te ,
the visited list will c onsist of a ll c onne c te d node s.
Now tha t we ha ve a func tion for finding node s tha t a re c onne c te d, we c a n use it to c re a te a func tion tha t will find a ll the isla nds

in our gra ph. T he find-islands func tion first de fine s a loc a l func tion, c a lle d find-island . T his func tion c he c ks whic h
node s a re c onne c te d to the first node in our list of node s using the connected func tion. It the n subtra c ts the se node s from the full
list of node s using the set-difference func tion. (set-difference ta ke s two lists, a nd re turns a ll ite m s tha t a re in the first list but
not the se c ond. )

Any re m a ining node s a re de e m e d unc onne c te d. If a ny unc onne c te d node e xists , we c a ll the find-islands func tion a ga in
re c ursive ly to find a dditiona l isla nds.
Onc e we ’ve found a ll the isla nds, we ne e d a wa y of bridging the m toge the r. T his is the job of the connect-with-bridges
func tion. It re turns a list of a dditiona l e dge s tha t join a ll the isla nds toge the r. T o do this, it ta ke s the list of isla nds a nd c he c ks if

the re is a cdr in this list . If the re is, it m e a ns the re a re a t le a st two la nd m a sse s, whic h c a n be c onne c te d with a bridge . It
use s the edge-pair func tion to c re a te this bridge , a nd the n c a lls itse lf re c ursive ly on the ta il of the isla nd list, in c a se a dditiona l
bridge s a re ne e de d.

Fina lly, we tie a ll of our isla nd pre ve ntion func tions toge the r using the func tion connect-all-islands . It use s find-
islands to find a ll the la nd m a sse s, a nd the n c a lls connect-with-bridges to build a ppropria te bridge s. It the n a ppe nds the se
bridge s to the initia l list of e dge s to produc e a fina l, fully c onne c te d la nd m a ss.
Building the F inal Edge s for Conge stion City
T o c om ple te our e dge s for Conge stion City, we ne e d to c onve rt the e dge s from a n e dge list into a n a list. W e a lso will a dd the
polic e roa dbloc ks, whic h will a ppe a r ra ndom ly on som e of the e dge s. For the se ta sks, we will c re a te the make-city-edges, edges-
to-alist, a nd add-cops func tions:
(defun make-city-edges ()

(let* ((nodes (loop for i from 1 to *node-num*


collect i))

(edge-list (connect-all-islands nodes (make-edge-list)))

(cops (remove-if-not (lambda (x)


(zerop (random *cop-odds*)))
edge-list)))

(add-cops (edges-to-alist edge-list) cops)))


(defun edges-to-alist (edge-list)

(mapcar (lambda (node1)


(cons node1

(mapcar (lambda (edge)


(list (cdr edge)))
(remove-duplicates (direct-edges node1 edge-list)

:test #'equal))))
(remove-duplicates (mapcar #'car edge-list))))

(defun add-cops (edge-alist edges-with-cops)

(mapcar (lambda (x)


(let ((node1 (car x))
(node1-edges (cdr x)))
(cons node1

(mapcar (lambda (edge)


(let ((node2 (car edge)))

(if (intersection (edge-pair node1 node2)


edges-with-cops
:test #'equal)
(list node2 'cops)
edge)))
node1-edges))))
edge-alist))
T he se a re the m ost c um be rsom e func tions in Gra nd T he ft W um pus. L e t’s ta ke a c lose r look a t the m .

The make -c ity-e dge s F unc tion

First, the make-city-edges func tion c re a te s a list of node s, using a loop . (T his is sim ply a list of num be rs from 1 to
*node-num*. ) Ne xt, it c re a te s a ra ndom (but fully c onne c te d) e dge list by c a lling the make-edge-list a nd connect-edge-list

func tions . T his re sult is store d in the edge-list va ria ble . It the n c re a te s a ra ndom list of e dge s tha t c onta ins cops . We
de fine the se va ria ble s with the let* c om m a nd, whic h a llows us to re fe re nc e pre viously de fine d va ria ble s.
T he following e xa m ple shows the diffe re nc e be twe e n de fining va ria ble s with let a nd let*:
> (let ((a 5)
(b (+ a 2)))
b)
*** - EVAL: variable A has no value
> (let* ((a 5)
(b (+ a 2)))
b)
7
As you c a n se e , let won’t a llow you to re fe r to othe r de fine d va ria ble s (the va ria ble b c a n’t re fe re nc e the va lue of a). W he n
de fining va ria ble s with let*, on the othe r ha nd, this kind of re fe re nc e is a llowe d. For our purpose s, using let* a llows our de finition

of cops to c onta in a re fe re nc e to edge-list.


Onc e we ’ve c re a te d the e dge list a nd de te rm ine d whe re the c ops a re , we ne e d to c onve rt our e dge list into a n a list a nd a dd the

c ops to it . T he e dge s a re c onve rte d to a n a list with the edges-to-alist func tion, a nd the c ops a re a dde d with the add-cops
func tion.

The e dge s-to-alist F unc tion


The e dge s-to-alist F unc tion

T he edges-to-alist func tion c onve rts a list of e dge s into a n a list of e dge s. For e xa m ple , a ssum e we ha ve the following c ity,
with only thre e loc a tions a nd two e dge s c onne c ting those loc a tions:

W e would de sc ribe this using a n e dge list a s '((1 . 2) (2 . 1) (2 . 3) (3 . 2)). Re m e m be r tha t e a c h of the e dge s is
re pe a te d, sinc e the e dge s a re undire c te d a nd c a n be use d in both dire c tions. If we de sc ribe d this sa m e c ity a s a n a list, wha t would
tha t look like ?
Re m e m be r tha t a n a list is a list tha t le ts us look up a ke y (in this e xa m ple , one of the thre e node s in our c ity) a nd find the
inform a tion a ssoc ia te d with tha t ke y (in this c a se , a list of the roa ds c onne c te d to it). For this sm a ll c ity, the a list would be '((1
(2)) (2 (1) (3)) (3 (2))).

T o build this a list, the edges-to-list func tion first mapcars ove r the node s found in the e dge list. T o build the list of
node s, we use the remove-duplicates func tion, whic h re m ove s duplic a te ite m s from a list. By de fa ult, remove-duplicates use s
t h e eql func tion to c he c k for e qua lity, though it a lso a llows you to c hoose a diffe re nt te st func tion using the :te st ke yword

pa ra m e te r. Sinc e we ’re c he c king for e qua lity of c ons pa irs in our make-city-edges func tion, we se t :test to #'equal .

W ithin this oute r mapcar , we use a nothe r mapcar to m a p a c ross a ll the direct-edges to this node . T oge the r, the se
ne ste d mapcar func tions a llow edges-to-alist to c onve rt the e dge s of a c ity into a n a list.

The add-c ops F unc tion

W he n we wrote the make-city-edges func tion, we ra ndom ly m a rke d som e of the e dge s to show tha t the y ha ve c ops on the m

. W e a re now going to use this list of c op e dge s to m a rk the e dge s in our a list tha t c onta in c ops. T his is the job of the add-
cops func tion.

T o do this, we use ne ste d mapcar c om m a nds to m a p a c ross the e dge s within e a c h node . W e the n c he c k whe the r

the re a re a ny c ops on a give n e dge , using the intersection func tion . (T he intersection func tion te lls us whic h ite m s a re
sha re d be twe e n two lists. )
T o unde rsta nd e xa c tly wha t the add-cops func tion is doing, it will he lp to onc e a ga in im a gine our c ity with only thre e loc a tions
a nd two stre e ts. In this e xa m ple , one of the stre e ts ha s c ops on it:

T he ge ne ra te d a list for this c ity, c re a te d by add-cops, would look like this:


((1 (2)) (2 (1) (3 COPS)) (3 (2 COPS)))
T his is a c tua lly a ne ste d a list. T he oute r a list is orga niz e d ba se d on the first node , a nd the inne r a lists a re orga niz e d ba se d on the
se c ond node .
W ith the e dge s in this form a t, we c a n e a sily find a ll e dge s c onne c te d to a give n node by c a lling (cdr (assoc node1 edges)).
T o se e if a give n e dge c onta ins c ops, we c a n c a ll (cdr (assoc node2 (cdr (assoc node1 edges)))), whic h goe s down two le ve ls
to gra b the a c tua l da ta linke d to a spe c ific e dge be twe e n two node s. (One a dditiona l be ne fit of using this ne ste d a list form a t is tha t
it is fully c om pa tible with our gra ph libra rie s— a fe a ture tha t we ’ll ta ke a dva nta ge of shortly. )
Building the Node s for Conge stion City
Now we ’ll build a n a list for the node s in our c ity. T he se node s m a y c onta in the W um pus or the Glowworm s, or the y m ight
c onta in va rious c lue s, suc h a s blood, lights, or sire ns.
Most of the c lue s in our ga m e a re ba se d on proxim ity to a nothe r node , so we ne e d to write som e func tions tha t te ll us if two
node s a re one node a pa rt in the c ity gra ph. T he neighbors func tion looks up the node ’s ne ighbors using the a list of e dge s. If the
se c ond node is in tha t list, we know we ’re one a wa y.
(defun neighbors (node edge-alist)
(mapcar #'car (cdr (assoc node edge-alist))))

(defun within-one (a b edge-alist)


(member b (neighbors a edge-alist)))
First, this func tion looks up the first node (a) in the a list of e dge s with neighbors. T he n it use s member to se e if the othe r node
(b) is a m ong the se node s.
T he blood sta in c lue s for the W um pus c a n a lso be se e n from two node s a wa y. W e c a n write a se c ond func tion for c he c king two
node s like this:
(defun within-two (a b edge-alist)

(or (within-one a b edge-alist)


(some (lambda (x)

(within-one x b edge-alist))

(neighbors a edge-alist))))

First, we c he c k if we a re within one node of our goa l , sinc e if we ’re within one , we ’re a lso within two. Ne xt, we e xtra c t

a ll the node s tha t a re one a wa y (sim ila r to wha t we did in the within-one func tion). Fina lly, we c he c k if a ny of the se ne w

node s a re within one , whic h would m a ke the m within two of the origina l node .
Now tha t we ha ve those utility func tions, le t’s write the func tion tha t builds the fina l node a list (ba sic a lly, the fina l m a p of our
c ity. ) He re ’s the listing:
(defun make-city-nodes (edge-alist)

(let ((wumpus (random-node))

(glow-worms (loop for i below *worm-num*


collect (random-node))))

(loop for n from 1 to *node-num*

collect (append (list n)

(cond ((eql n wumpus) '(wumpus))

((within-two n wumpus edge-alist) '(blood!)))

(cond ((member n glow-worms)


'(glow-worm))

((some (lambda (worm)


(within-one n worm edge-alist))
glow-worms)
'(lights!)))

(when (some #'cdr (cdr (assoc n edge-alist)))


'(sirens!))))))

T he make-city-nodes func tion first pic ks ra ndom node s for the W um pus a nd the Glowworm s , a nd the n it use s loop

to run through the node num be rs. As it runs through the node s, it builds a list de sc ribing e a c h node in the c ity, appended

toge the r from va rious sourc e s . By using append, e a c h pa rt of the c ode tha t de sc ribe s the se node s (a nd is within the body of
the append) c a n c hoose to a dd z e ro, one , or m ultiple ite m s to the de sc ription, c re a ting its own c hild lists with z e ro, one , or
m ultiple ite m s.

At the front of the list, we put the node na m e , n . If the W um pus is a t the c urre nt node , we a dd the word Wumpus

(but wra ppe d in a list, a s we just de sc ribe d). If we ’re within two node s of the W um pus, we show its blood . If the node ha s a
Glowworm ga ng, we show it ne xt , a nd if the Glowworm ga ng is one node a wa y, we show its lights . Fina lly, if a n e dge

from the node c onta ins c ops, we indic a te tha t sire ns c a n be he a rd .


T o c he c k for the sire ns c lue , we sim ply gra b the e dge s with (cdr (assoc n edges)) a nd se e if som e of the se node s ha ve a va lue
in the c dr. T he 'cops sym bol would be a tta c he d to the e dge s a t the cdr. Sinc e we ha ve only one da ta point for e dge s in this ga m e ,
looking for the pre se nc e of a cdr is a n a de qua te c he c k for the pre se nc e of c ops. For e xa m ple , if we use our e a rlie r e xa m ple of a n
a list with c ops on it:

((1 (2)) (2 (1) (3 COPS) ) (3 (2 COPS)))

You c a n se e tha t if a n e dge in the list ha s c ops, suc h a s he re , the cdr will le a d to a non-nil va lue . An e dge without c ops

will ha ve a cdr tha t is nil.


Initializ ing a Ne w G ame of G r and The ft W umpus
W ith our gra ph c onstruc tion stuff out of the wa y, we c a n write a sim ple func tion tha t initia liz e s a bra nd-ne w ga m e of Gra nd
T he ft W um pus:
(defun new-game ()
(setf *congestion-city-edges* (make-city-edges))
(setf *congestion-city-nodes* (make-city-nodes *congestion-city-edges*))

(setf *player-pos* (find-empty-node))


(setf *visited-nodes* (list *player-pos*))
(draw-city))

T he re a re two ne w func tions he re . One , the find-empty-node func tion , e nsure s tha t the pla ye r doe sn’t e nd up on top of a
ba d guy right a t the be ginning of the ga m e . He re ’s the c ode for tha t func tion:
(defun find-empty-node ()

(let ((x (random-node)))

(if (cdr (assoc x *congestion-city-nodes*))

(find-empty-node)
x)))

T he find-empty-node func tion is pre tty sim ple . First, it pic ks a ra ndom node to c onside r a s the pla ye r’s sta rting position.

T he n it c he c ks whe the r it is a c om ple te ly e m pty node . If the re ’s stuff in tha t node , it sim ply c a lls itse lf a ga in, trying a nothe r

ra ndom spot .

W ar ning

If you e ve r de c ide to m odify the ga m e a nd m a ke it m ore c rowde d with ba d guys, you c ould e nd up in a situa tion whe re no e m pty
node s e xist. In tha t c a se , this func tion will se a rc h fore ve r a nd loc k up your L isp RE PL , sinc e we didn’t put in a ny c he c ks to de te c t
this situa tion.

T he othe r ne w func tion in our new-game c om m a nd is draw-city, whic h we ’ll write ne xt.
Dr awing a M ap of O ur City
W e ’re fina lly re a dy to dra w a m a p of our ne w c ity. W e ’re using a sta nda rd form a t for our gra ph da ta , so writing this func tion is a
bre e z e :
(defun draw-city ()
(ugraph->png "city" *congestion-city-nodes* *congestion-city-edges*))
W e c re a te d the ugraph->png func tion in the pre vious c ha pte r, a s pa rt of our gra ph libra ry.
Now c a ll (new-game) from the RE PL , a nd ope n the c ity . dot. png pic ture in your we b browse r:

Note

Sinc e e ve ry c ity m a p c re a te d by our c ode is unique , your m a p will look c om ple te ly diffe re nt from the one in this pic ture .

Fina lly, we c a n m a rve l a t the re sults of our urba n pla nning!


Dr awing a City fr om P ar tial K nowle dge
Of c ourse , it’s a wfully boring to hunt som e thing if you a lre a dy know whe re it is be fore the hunt sta rts. T o solve this proble m , we
wa nt a m a p of the c ity tha t shows only the node s tha t we ’ve visite d so fa r. T o tha t e nd, we use a globa l list c a lle d *visited-
nodes* tha t is initia lly se t to the pla ye r’s position only, but whic h we ’ll upda te a s we wa lk a round the c ity visiting othe r node s.
Using this *visited-nodes* va ria ble , we c a n c a lc ula te a sm a lle r gra ph tha t inc lude s only those pa rts of the c ity tha t a re known to
us.

K nown Node s

First, we c a n build a n a list of just the known node s:


(defun known-city-nodes ()

(mapcar (lambda (node)

(if (member node *visited-nodes*)


(let ((n (assoc node *congestion-city-nodes*)))

(if (eql node *player-pos*)


(append n '(*))
n))

(list node '?)))


(remove-duplicates

(append *visited-nodes*

(mapcan (lambda (node)


(mapcar #'car
(cdr (assoc node
*congestion-city-edges*))))
*visited-nodes*)))))
At the bottom of known-city-nodes, we ne e d to figure out whic h node s we c a n “ se e ” ba se d on whe re we ’ve be e n. W e ’ll be a ble

to se e a ll visite d node s , but we a lso wa nt to tra c k a ll node s within one node of a visite d node . (W e will disc uss the
mapcan func tion shortly. ) W e c a lc ula te who is “ within one ” using c ode sim ila r to the pre viously disc usse d within-one func tion.

Ne xt, we mapcar ove r this list of re le va nt node s, proc e ssing e a c h . If the c urre nt node is oc c upie d by the pla ye r, we m a rk it

with a n a ste risk . If the node ha sn’t be e n visite d ye t , we m a rk it with a que stion m a rk .

K nown Edge s

Now, we ne e d to c re a te a n a list strippe d of a ny c op sire ns tha t we ha ve n’t re a c he d ye t:


(defun known-city-edges ()
(mapcar (lambda (node)
(cons node (mapcar (lambda (x)
(if (member (car x) *visited-nodes*)
x

(list (car x))))


(cdr (assoc node *congestion-city-edges*)))))
*visited-nodes*))

T his func tion is sim ila r to the known-city-nodes func tion. T he note worthy line of c ode is he re whe re we strip the cdr
from the e dge list for e dge s so tha t c ops a re indic a te d on the m a p only if we ’ve visite d the node s on both e nds of a n e dge
c onta ining c ops.

The mapc an F unc tion

T he mapcan func tion we use d in known-city-nodes is a va ria nt of mapcar. Howe ve r, unlike mapcar, mapcan a ssum e s tha t the
va lue s ge ne ra te d by the m a pping func tion a re a ll lists tha t should be a ppe nde d toge the r. T his is use ful whe n the re isn’t a one -to-one
re la tionship be twe e n the ite m s in a list a nd the re sult you wa nt to ge ne ra te .
For e xa m ple , suppose we run a burge r shop a nd se ll thre e type s of burge rs: the single , the double , a nd the double c he e se . T o
c onve rt a list of burge rs into a list of pa ttie s a nd c he e se slic e s, we c ould write the following func tion:
> (defun ingredients (order)
(mapcan (lambda (burger)
(case burger
(single '(patty))
(double '(patty patty))
(double-cheese '(patty patty cheese))))
order))
INGREDIENTS
> (ingredients '(single double-cheese double))
'(PATTY PATTY PATTY CHEESE PATTY PATTY)

Dr awing O nly the K nown P ar ts of the City


Dr awing O nly the K nown P ar ts of the City

Be c a use we now ha ve func tions tha t c a n ge ne ra te the known inform a tion a bout node s a nd e dge s, we c a n write a func tion tha t
turns this inform a tion into a pic ture , a s follows:
(defun draw-known-city ()
(ugraph->png "known-city" (known-city-nodes) (known-city-edges)))
Now le t’s re de fine our new-game func tion to dra w the known c ity whe n the ga m e sta rts:
(defun new-game ()
(setf *congestion-city-edges* (make-city-edges))
(setf *congestion-city-nodes* (make-city-nodes *congestion-city-edges*))
(setf *player-pos* (find-empty-node))
(setf *visited-nodes* (list *player-pos*))
(draw-city)

(draw-known-city))
T his func tion is a lm ost e xa c tly the sa m e a s the pre vious ve rsion of new-game, e xc e pt tha t we a lso c re a te a dra wing c om pose d

only of the known pa rts of the c ity .


Now, if we c a ll the new-game func tion from the RE PL , we ’ll ge t a ne w pic ture na m e d k nown-c ity . dot. png tha t we c a n vie w in
our browse r. It will look som e thing like this:

Now we ’re re a dy to wa lk a round our m a p of Conge stion City!


W alking Ar ound Town
W e ’ll ne e d two func tions for tra ve ling be twe e n the node s in our c ity: a re gula r walk func tion a nd one for whe n we think we ’ve
found the W um pus, a nd we wa nt to charge tha t loc a tion with our fina l bulle t. Sinc e the se two func tions a re ve ry sim ila r, we ’ll
ha ve both of the m de le ga te the bulk of the work to a c om m on handle-direction func tion:
(defun walk (pos)
(handle-direction pos nil))
(defun charge (pos)
(handle-direction pos t))
T he only diffe re nc e be twe e n the se two func tions is the fla g the y pa ss to handle-direction, whic h is se t to e ithe r nil or t,
de pe nding on the kind of tra ve ling.
T he handle-direction func tion’s m a in job is to m a ke sure tha t a m ove is le ga l, whic h it doe s by c he c king the e dge s of the c ity:
(defun handle-direction (pos charging)
(let ((edge (assoc pos

(cdr (assoc *player-pos* *congestion-city-edges*)))))


(if edge

(handle-new-place edge pos charging)

(princ "That location does not exist!"))))

First, this func tion looks up the le ga l dire c tions pla ye rs c a n m ove to from the ir c urre nt loc a tion . It the n use s the pos the
pla ye r wa nts to m ove to a nd looks it up in tha t list of possible dire c tions. Onc e we ’ve de te rm ine d tha t a dire c tion is le ga l (tha t is,
a node with tha t num be r sha re s a n e dge with the pla ye r’s c urre nt position), we ne e d to find out wha t surprise s a re wa iting a s the

pla ye r tra ve ls to this ne w pla c e , using the handle-new-place func tion, whic h we ’ll c re a te ne xt . Othe rwise , we displa y a

he lpful e rror m e ssa ge .


Now le t’s c re a te the handle-new-place func tion, whic h ge ts c a lle d whe n the pla ye r ha s tra ve le d to a ne w pla c e :
(defun handle-new-place (edge pos charging)

(let* ((node (assoc pos *congestion-city-nodes*))

(has-worm (and (member 'glow-worm node)


(not (member pos *visited-nodes*)))))

(pushnew pos *visited-nodes*)

(setf *player-pos* pos)

(draw-known-city)

(cond ((member 'cops edge) (princ "You ran into the cops. Game Over."))

((member 'wumpus node) (if charging


(princ "You found the Wumpus!")
(princ "You ran into the Wumpus")))

(charging (princ "You wasted your last bullet. Game Over."))

(has-worm (let ((new-pos (random-node)))


(princ "You ran into a Glow Worm Gang! You're now at ")
(princ new-pos)
(handle-new-place nil new-pos nil))))))

First, we re trie ve the node the pla ye r is tra ve ling to from the a list of node s . Ne xt, we figure out if the node c onta ins a

Glowworm ga ng . W e ignore the ga ng if the y’re in a node a lre a dy visite d, be c a use the y’ll only a tta c k onc e .

Ne xt, the handle-new-place func tion upda te s *visited-nodes* (a dding the ne w position to the list) a nd *player-pos*

. T he n it c a lls draw-known-city a ga in, sinc e we now ha ve a ne w pla c e we know a bout.

Ne xt, it c he c ks to se e if the re a re a ny c ops on the e dge , a nd the n whe the r the W um pus is a t tha t loc a tion . If the
pla ye r e nc ounte rs the W um pus, our handle-new-place func tion ne e ds to know whe the r we we re c ha rging the loc a tion. If we a re
c ha rging a t the W um pus, we win the ga m e . Othe rwise , the W um pus kills us a nd the ga m e is ove r.
If, on the othe r ha nd, we c ha rge a t a loc a tion tha t doe s not c onta in the W um pus, we wa ste our single bulle t a nd we lose the

ga m e a s we ll . Fina lly, if the loc a tion ha s a pre viously une nc ounte re d Glowworm ga ng, jum p to a ra ndom ne w loc a tion,
c a lling handle-new-place re c ursive ly .
Our ga m e is now c om ple te !
Le t's H unt Some W umpus!
T o pla y our ga m e , sim ply e nte r the tra ve ling c om m a nds we c re a te d (walk a nd charge) a t the RE PL , the n switc h to your browse r
a nd re fre sh k nown-c ity . dot. png to pla n your ne xt m ove .
For e xa m ple , he re ’s whe re we le ft off in our sa m ple ga m e :

Sinc e we ha ve no c lue s, we know tha t a ny of the se node s will be sa fe to visit. L e t’s try (walk 20):

Uh oh! T he re ’s blood he re . T ha t m e a ns the W um pus m ust be two node s a wa y! It should still be sa fe to (walk 11) though, be c a use
tha t’s only one node a wa y:
Oh no! One of the se stre e ts ha s a polic e roa dbloc k. L e t’s ba c ktra c k with (walk 20) (walk 19), a nd the n we c a n try (walk 7):

Da rn! Now we ha ve the W um pus a nd som e Glowworm s ne a rby. L e t’s ta ke a shot in the da rk a nd try (walk 10):
W e ll, tha t didn’t he lp, sinc e the re a re c ops down this pa th. Howe ve r, be c a use node 10 ha s only one othe r une xplore d stre e t, we
c a n sa y with c e rta inty tha t the stre e t be twe e n 1 a nd 10 ha s c ops on it.
You c a n se e tha t it ta ke s som e se rious thinking to be c om e a m a ste r in Gra nd T he ft W um pus! Re m e m be r, you c a n a lwa ys sta rt a
ne w ga m e , with a ne w m a p, by using the new-game func tion. Onc e you’ve tra c ke d down the W um pus, use the charge func tion to
a tta c k him .
If you m a ste r the ba sic ve rsion of this ga m e , try inc re a sing the num be r of node s, e dge s, c ops, a nd Glowworm s for a n e ve n gre a te r
c ha lle nge !
W hat You've Le ar ne d
In this c ha pte r, we ’ve use d gra ph utilitie s with L isp to m a ke a m ore sophistic a te d ga m e . Along the wa y, you le a rne d the
following:
T he loop func tion a llows us to loop a c ross va rious type s of da ta . It will be disc usse d in m ore de ta il in Cha pte r 10.
T he set-difference func tion te lls you whic h ite m s a re in one list but not in a nothe r list.
T he intersection func tion te lls you whic h ite m s a re sha re d by lists.
T he remove-duplicates func tion re m ove s duplic a te ite m s from a list.
Chapte r 9. Advanc e d Datatype s and G e ne r ic P r ogr amming
As you’ve se e n so fa r, a lot c a n be a c c om plishe d in L isp by using c ons c e lls, sym bols, strings, a nd num e ric da ta type s. As a ve ry
m a ture la ngua ge , Com m on L isp c onta ins m a ny m ore da ta type s tha t m ove we ll be yond the se ba sic s. In this c ha pte r, we will disc uss
the m ost use ful of the se a dva nc e d da ta type s, inc luding a rra ys, ha sh ta ble s, a nd struc ture s.
Ar r ays
T he Common Lisp array is ve ry sim ila r to a list. T he m a in a dva nta ge of using a rra ys is tha t the y re quire only a c onsta nt a m ount
of tim e to a c c e ss a va lue a t a ny spe c ific loc a tion. W e ’ll be disc ussing wha t this m e a ns shortly.
W or king with Ar r ays
T o c re a te a ne w a rra y, use the make-array c om m a nd, spe c ifying the a rra y’s siz e :
> (make-array 3)
#(NIL NIL NIL)
T his c re a te s a n a rra y of le ngth 3. In orde r to indic a te tha t the va lue c re a te d is not just a list, Com m on L isp pre pe nds a ha sh
m a rk (#) in front of the a rra y.
T o ge t a nd se t ite m s in a n a rra y, use the aref func tion. For e xa m ple , he re ’s how we ge t the ite m a t inde x 1:
> (defparameter x (make-array 3))
#(NIL NIL NIL)
> (aref x 1)
NIL
Of c ourse , our a rra y is just fille d with nils right now, so the re ’s not m uc h worth ge tting. T o se t ite m s in the a rra y to m ore
inte re sting va lue s, use aref in c onjunc tion with the setf c om m a nd:
> (defparameter x (make-array 3))
#(NIL NIL NIL)
> (setf (aref x 1) 'foo)
FOO
> x
#(NIL FOO NIL)
> (aref x 1)
FOO
Although aref is usua lly a c om m a nd use d to ge t a va lue out of a n a rra y, whe n use d in this spe c ia l wa y indic a te d in the e xa m ple ,
it le ts us se t a va lue in a n a rra y, inste a d. T his a bility to use the setf a nd aref c om m a nds toge the r shows off a fe a ture in Com m on
L isp: its support for ge ne ric progra m m ing. L e t’s ta ke a c lose r look a t the setf c om m a nd to le a rn m ore a bout how this fe a ture
works.
Using a G e ne r ic Se tte r
T he Com m on L isp la ngua ge is sa id to support ge ne ric se tte rs. T his m e a ns tha t in m ost c a se s, the c ode for pulling a v alue out of
a da ta struc ture (whe the r a n a rra y, list, string, or som e thing e lse ) is ide ntic a l to the c ode for putting data into tha t sa m e da ta
struc ture . T he setf c om m a nd c a n be use d in c onjunc tion with func tions tha t pe rform ge tting ope ra tions a nd c a n use the sa m e
func tions to pe rform se tting ope ra tions, inste a d.
W e ’ve a lre a dy se e n tha t aref c a n be use d to ge t va lue s out of a n a rra y, a nd whe n use d with setf, it c a n be use d for se tting
va lue s in the sa m e a rra y. T he setf c om m a nd c a n pe rform this tric k in a ge ne ra l wa y a c ross m ost of the c om m a nds in Com m on
L isp tha t ge t ite m s from a da ta struc ture . T a ke , for insta nc e , the following e xa m ple involving a list:
> (setf foo '(a b c))
(A B C)
> (second foo)
B

> (setf (second foo) 'z)


Z
> foo
(A Z C)

As you would e xpe c t, the e xpre ssion (second foo) re turns B. But, whe n we pa ss (second foo) to the setf c om m a nd , it
som e how knows whe re the B c a m e from , a nd it is a ble to tre a t the e xpre ssion (second foo) a s if it we re a re gula r va ria ble .
Ba sic a lly, the setf c om m a nd a sks itse lf the que stion, “ W he re did the ite m in m y first a rgum e nt origina lly c om e from ? ” In this
c a se , the va lue c a m e from the se c ond ite m in the list na m e d foo. T he re fore , if we try to setf this loc a tion, the sourc e va ria ble ,
foo, is m odifie d in re sponse .
In fa c t, the first a rgum e nt in setf is a spe c ia l subla ngua ge of Com m on L isp, c a lle d a ge ne ralize d re fe re nc e . Not e ve ry L isp
c om m a nd is a llowe d in a ge ne ra liz e d re fe re nc e , but you c a n still put in som e pre tty c om plic a te d stuff:
> (setf foo (make-array 4))
#(NIL NIL NIL NIL)

> (setf (aref foo 2) '(x y z))


(X Y Z)
> foo

#(NIL NIL (X Y Z) NIL)

> (setf (car (aref foo 2)) (make-hash-table))


#S(HASH-TABLE)

> (setf (gethash 'zoink (car (aref foo 2))) 5)


5
> foo
#(NIL NIL (#S(HASH-TABLE (ZOINK . 5)) Y Z) NIL)
T his e xa m ple de m onstra te s the true powe r of setf in Com m on L isp. In the first use , we put the list (x y z) into a n a rra y a s the

third ite m . If we now print foo, we c a n se e tha t it worke d . In the se c ond use , we re pla c e the first ite m in this list

inside the foo a rra y with a ha sh ta ble . Ha sh ta ble s a re a nothe r a dva nc e d da ta type we ’ll be le a rning a bout shortly, in Ha sh
T a ble s. It is surprisingly e a sy to do this with setf, be c a use the ge ne ra liz e d re fe re nc e in the first a rgum e nt to setf c a n be
a rbitra rily c om plic a te d.

Fina lly, we go a ll out a nd inse rt the va lue 5 into this ha sh ta ble with the ke y of zoink . T he gethash func tion le ts you ge t
a va lue out of a ha sh ta ble , a s we ’ll se e shortly. He re , with the he lp of setf, we a re putting the num be r 5 into the ha sh ta ble
inste a d.
I hope you c a n a ppre c ia te from this e xa m ple how use ful setf c a n be whe n m odifying c om plic a te d da ta struc ture s in a progra m .
Anothe r c ool fe a ture of setf is tha t you c a n e xpa nd the ge ne ra liz e d re fe re nc e synta x to support ne w wa ys of a c c e ssing va lue s.
setf is a truly ge ne ric wa y of m odifying va lue s, re ga rdle ss of the le ve l of ne sting or the da ta type s be ing use d.
Ar r ays vs. Lists
You’ve now se e n som e ba sic e xa m ple s of working with a rra ys in L isp. Howe ve r, to fully unde rsta nd the be ne fits of a rra ys, we
ne e d to c om pa re the m with lists.
Alm ost a nything tha t c a n be done with a list c a n a lso be done with a n a rra y. Howe ve r, a rra ys a re usua lly m uc h fa ste r tha n lists
whe n a c c e ssing spe c ific e le m e nts, so the diffe re nc e is in pe rform a nc e .
For e xa m ple , the a rra y-ha ndling func tion aref is ve ry sim ila r to a list-ha ndling func tion c a lle d nth, whic h a llows you a c c e ss to
a n ite m a t a spe c ific loc a tion in a re gula r list without using a n a rra y. He re is a n e xa m ple using nth on a list:
> (nth 1 '(foo bar baz))
BAR
Howe ve r, it m a ke s se nse to use the nth func tion only with ve ry sm a ll lists. If, for e xa m ple , list X ha d thousa nds of ite m s in it,
running the c om m a nd (nth 1000 x) would be e xc ruc ia tingly slow, be c a use L isp lists a re m a de out of c ha ins of c ons c e lls. He nc e ,
the only wa y L isp c a n find the thousa ndth ite m in a list is to c hurn through the 999 pre c e ding obje c ts first.
In c ontra st, running the c om m a nd (aref x 1000) on a la rge a rra y a c c e sse s the thousa ndth ite m dire c tly, without c ounting
through the pre vious 999 ite m s. T his m e a ns aref will e xe c ute m uc h m ore quic kly on a la rge a rra y tha n the nth c om m a nd would on
a la rge list. In fa c t, a n aref c a ll will ha ppe n ve ry quic kly no m a tte r how la rge the a rra y. E ve n if you ha d a n a rra y with a billion
ite m s, re trie ving the la st ite m would still ha ppe n ve ry quic kly. T he only re a l lim iting fa c tor is your syste m : the a m ount of RAM
your c om pute r ha s a nd how c a pa ble your L isp e nvironm e nt is of ta king a dva nta ge of it.

Not only c a n we quic kly a c c e ss a rra y va lue s, but we c a n a lso c ha nge va lue s a t a ny spe c ific loc a tion, usua lly fa ste r tha n we c a n
by pe rform ing the sa m e ope ra tions on a list.
Be c a use se tting a nd ge tting spe c ific va lue s in a la rge da ta struc ture is so im porta nt, ke e p a rra ys in m ind a s a tool to he lp you ge t
the be st pe rform a nc e possible with your c ode .
H ash Table s
In the sa m e wa y tha t a rra ys a re sort of like lists, hash table s a re sort of like alists, e xc e pt tha t the y a lso a llow you to a c c e ss
a rbitra ry e le m e nts m ore quic kly.
In fa c t, ha sh ta ble s a re so e ffic ie nt tha t the y c a n, a t tim e s, se e m like m a gic . T hink of the Ba be l fish in the Hitc hhik e r’s Guide
to the Galax y trilogy— som e thing so im possibly use ful tha t it re a lly ha s no busine ss e xisting in the first pla c e . T ha t’s why a lm ost a ll
m ode rn la ngua ge s now offe r the ha sh ta ble da ta type .
W or king with H ash Table s
Cre a te a ne w ha sh ta ble with the make-hash-table c om m a nd:
> (make-hash-table)
#S(HASH-TABLE ...)
L ike a lists, ha sh ta ble s store ite m s using a lookup ke y a nd a va lue . W e c a n re trie ve a n ite m from the ha sh ta ble using the ite m ’s
ke y with the gethash func tion:
> (defparameter x (make-hash-table))
#S(HASH-TABLE ...)
> (gethash 'yup x)

NIL ;

NIL
So fa r, our ha sh ta ble re m a ins e m pty. T his m e a ns tha t whe n we look up a ny ke y in the ha sh ta ble , suc h a s 'yup in this e xa m ple ,

we re c e ive NIL a s a n a nswe r . Ac tua lly, we re c e ive two NIL s — the gethash c om m a nd re turns m ultiple va lue s,
whic h you c a n do in Com m on L isp (disc usse d in the ne xt se c tion). T he first re turne d va lue is the a c tua l va lue store d in the ha sh
ta ble , a nd the se c ond indic a te s whe the r the ke y wa s found in the ta ble (in this c a se , it wa sn’t).
Just a s with a rra ys, we c a n onc e a ga in c om bine a c om m a nd use d for re fe re nc ing da ta e le m e nts— in this c a se , gethash— with the
setf c om m a nd in orde r to fill our ta ble with da ta :
> (defparameter x (make-hash-table))
#S(HASH-TABLE ...)

> (setf (gethash 'yup x) '25)


25
> (gethash 'yup x)

25 ;

In this e xa m ple , we ’ve store d the va lue 25 in the ha sh ta ble with a lookup ke y of yup . T he n, whe n we look up yup in the

ta ble , we ge t the a nswe r of 25 . W e a lso ge t a se c ond va lue of t , whic h m e a ns, “ Ye s, I found the ke y in the ta ble . ”
Re m e m be r whe n we disc usse d a lists, we se t up a da ta struc ture c onta ining a n orde r for c offe e drinks? He re is tha t sa m e da ta , but
this tim e it’s store d using a ha sh ta ble :
> (defparameter *drink-order* (make-hash-table))
#S(HASH-TABLE ...)
> (setf (gethash 'bill *drink-order*) 'double-espresso)
DOUBLE-ESPRESSO
> (setf (gethash 'lisa *drink-order*) 'small-drip-coffee)
SMALL-DRIP-COFFEE
> (setf (gethash 'john *drink-order*) 'medium-latte)
MEDIUM-LATTE
Ac c e ssing the drink orde r for a ny pe rson is now sim ple :
> (gethash 'lisa *drink-order*)
'small-drip-coffee ;
T
Re tur ning M ultiple Value s
Com m on L isp a llows you to re turn m ore tha n one va lue a s a re sult. Som e of the c ore Com m on L isp c ore func tions do this,
inc luding the gethash func tion, a s you’ve se e n. Anothe r c om m only use d func tion tha t doe s this is the round func tion, whic h rounds
off a num be r:
> (round 2.4)

2 ;

0.4

Ca lling this func tion a ppropria te ly rounde d our num be r to 2 , but the n it a lso ge ne ra te d a se c ond va lue , whic h is the

re m a inde r of the rounding ope ra tion . Both va lue s a re re turne d from this func tion c a ll.
You c a n a lso c re a te m ultiple va lue s in your own c ode by using the values func tion. He re , for insta nc e , we c a n write a foo
func tion tha t re turns two se pa ra te num be rs, 3 a nd 7:
> (defun foo ()
(values 3 7))
FOO
> (foo)

3 ;

Both of the se va lue s a re printe d out a t the RE PL , just a s with the round func tion. Howe ve r, L isp c onside rs the first
va lue to be m ore im porta nt, a nd it will a lwa ys be use d by de fa ult during follow-up c a lc ula tions. For insta nc e , we c a n pe rform a n
a ddition a fte r c a lling foo, like this:
> (+ (foo) 5)
8
In this c a se , the a ddition ope ra tor just ignore s the se c ond va lue tha t foo re turns.
Howe ve r, som e tim e s you m ight ne e d to use tha t a dditiona l re turne d va lue . You c a n do this by using the multiple-value-bind
c om m a nd:
> (multiple-value-bind (a b) (foo)
(* a b))
21
In this e xa m ple , we ’ve bound the va ria ble s a a nd b to both of the va lue s re turne d by foo (3 a nd 7). Ca lling our func tion with
multiple-value-bind le ts us use the e xtra va lue s re turne d from the func tion, whic h would othe rwise be ignore d.
You m ight be wonde ring whe the r you c ould just re turn a list from your func tion inste a d of using the m ultiple -va lue fe a ture . T he
a nswe r is, ye s, you c ould. Howe ve r, it’s possible tha t using the m ultiple -va lue fe a ture c a n le a d to m ore optim iz e d a nd c le a ne r
c ode .
In this book, we will not be m a king m uc h use of m ultiple va lue s. In fa c t, m ore re c e nt L isp dia le c ts, suc h a s Arc a nd Clojure , do
not support m ultiple va lue s a t a ll. Inste a d, the y just re turn a list in the fe w c a se s whe re m ore tha n one va lue ne e ds to be re turne d.
H ash Table P e r for manc e
As with a rra ys, a c c e ssing a nd m odifying a va lue inside a ha sh ta ble re quire s only a c onsta nt a m ount of tim e , no m a tte r how
m a ny ite m s your ha sh ta ble c onta ins. For insta nc e , suppose we ha ve a ha sh ta ble with only 10 ite m s in it. W e a c c e ss a va lue in the
ta ble , using a ke y, a nd find it ta ke s on a ve ra ge 1 m illise c ond to do so. Now suppose tha t the ha sh ta ble ha s 1, 000, 000 ite m s in it.
Be c a use of how ha sh ta ble s a re de signe d, we c ould still e xpe c t it to ta ke only a bout 1 m illise c ond to re trie ve a va lue . In othe r
words, no m a tte r how big the ta ble is, we c a n a c c e ss ite m s a t a c onsta nt tim e of 1 m illise c ond.
T hink of how inc re dible tha t is! E ve n if your ha sh ta ble c onta ine d 1, 000, 000 ite m s, the gethash func tion c ould ta ke the ke y you
ga ve it a nd de te rm ine in a c onsta nt a m ount of tim e e xa c tly whe re your de sire d ite m c ould be found!
In this e ra of we b-ba se d progra m s ba c ke d by e norm ous a m ounts of da ta , the a bility of ha sh ta ble s to store la rge num be rs of va lue s
with fa st re trie va l m a ke s the m indispe nsa ble . T he e ffic ie nt stora ge of ke y/va lue pa irs is e sse ntia l for m ost online stora ge syste m s.
E ve n the la te st tools for storing va st a m ounts of online da ta , like Google ’s BigT a ble or Am a z on’s S3, a re built a round the quic k
re trie va l of va lue s using ke ys, whic h m a ke s the m sim ila r to ha sh ta ble s.
Howe ve r, you c a n’t a lwa ys e xpe c t ha sh ta ble s to provide the be st pe rform a nc e . He re ’s why: Vir tual me mor y paging and c ac he
misse s:
As with a rra ys, la rge ha sh ta ble s m a y c a use your ope ra ting syste m to sta rt pa ging virtua l m e m ory to your ha rd drive , thus
de gra ding pe rform a nc e . Sim ila rly, the y c a n inc re a se the num be r of c a c he m isse s within your CPU.
H ash c ollisions:
Inte rna lly, ha sh ta ble s use a spe c ia l func tion c a lle d a hash func tion, whic h c onve rts ke ys into num be rs. Suc h a ha sh func tion
c a n c a use hash c ollisions. Ba sic a lly, a ha sh c ollision ha ppe ns whe n, by c ha nc e , two ke ys a re c onve rte d by the ha sh func tion
into the sa m e num be r. In this c a se , the ha sh ta ble will still be ha ve c orre c tly, but a t a slightly de gra de d pe rform a nc e . In ra re
c a se s, c e rta in type s of ke ys c a n inte ra c t with a ha sh func tion to inc re a se the num be r of c ollisions a nd im pe de a n a pplic a tion’s
a bility to pe rform lookups, de gra ding pe rform a nc e e ve n m ore .
Ine ffic ie nc y with small table s:
W ith ve ry sm a ll ta ble s, the c re a tion a nd lookup tim e re quire d by ha sh ta ble s c a n m a ke the m le ss ine ffic ie nt tha n sim ple r
struc ture s, suc h a s a lists. T he pe rform a nc e be ne fits of ha sh ta ble s a re notic e a ble only whe n the y c onta in la rge r a m ounts of da ta
in the m .
Var ying spe e d for ope r ations:
In Com m on L isp, if you c re a te a sm a ll ha sh ta ble , a nd the n fill it with va lue s, you will find tha t oc c a siona lly, a dding a ne w
va lue will be unusua lly slow. T his is be c a use the make-hash-table func tion is de signe d to m inim iz e the c ost for c re a ting
sm a ll ha sh ta ble s. Howe ve r, a s you sta rt a dding va lue s to m a ke the ta ble big, L isp will ne e d to ta ke e xtra tim e to a lloc a te
m ore m e m ory so tha t the ta ble c a n hold m ore ite m s. T he se e xtra a lloc a tions will le a d to oc c a siona l slow inse rtions into the
ta ble a s it grows.
T he re is one fina l re a son why ha sh ta ble s a re not a lwa ys the be st solution: T he y a re sim ply not a s L ispy a s tra ditiona l L isp
struc ture s built from c ons c e lls. T his m e a ns the y c a n be ha rde r to de bug tha n c ons c e lls, sinc e the y c a nnot be re a d a nd printe d a s
na tura lly in the L isp RE PL . T he re fore , a good rule of thum b is to sta y a wa y from a rra ys a nd ha sh ta ble s a s you c onc e ive a ne w
pie c e of c ode . T he n, if pe rform a nc e e nds up be c om ing a n issue , a nd only the n, judic iously m odify the c ritic a l se c tions of your
c ode to ta ke a dva nta ge of a rra ys a nd ha sh ta ble s to re solve a ny pe rform a nc e proble m s.
A F aste r G r and The ft W umpus Using H ash Table s
L e t’s look a t a pra c tic a l e xa m ple of wha t ha sh ta ble s c a n do for your c ode . T he re is a gla ring ine ffic ie nc y in our la te st ga m e ,
Gra nd T he ft W um pus, tha t we c a n now c orre c t with ha sh ta ble s.
Re c a ll from the pre vious c ha pte r tha t Gra nd T he ft W um pus use s lists of node s a nd e dge s to re pre se nt the gra ph of the c ity. T his
m e a ns tha t in orde r to find c onne c tions to a give n node , we m ust do a line a r se a rc h through a list. T his isn’t a big de a l in Gra nd
T he ft W um pus, be c a use Conge stion City doe sn’t ha ve a lot of inte rse c tions. But wha t if our c ity ha d a thousa nd node s with a
thousa nd e dge s? L e t’s tim e the get-connected func tion a nd se e wha t kind of num be rs we ge t:
> (setf *edge-num* 1000)
1000
> (setf *node-num* 1000)
1000
> (time (dotimes (i 100) (get-connected 1 (make-edge-list))))
Real time: 57.699303 sec.
Run time: 57.687607 sec.
Space: 39566832 Bytes
GC: 43, GC time: 0.120005 sec.
T he time c om m a nd is a L isp utility tha t outputs a ll kinds of use ful tim ing inform a tion a bout a c hunk of c ode , a nd the dotimes
func tion le ts us run our c ode 100 tim e s, building 100 c itie s. Using the se c om m a nds, it took a bout a m inute to run this c ode on m y
c om pute r. Give n how m a ny ga z illion instruc tions a CPU c a n c runc h in a m inute , this is a bsolute ly horrifyingly ba d pe rform a nc e .
T o fix this proble m , we ’ll re pla c e our e dge list for this c ode with a ha sh ta ble so tha t the get-connected func tion will be a ble to
find c onne c tions to a node in c onsta nt tim e . W e ’ll a lso re pla c e our visite d list with a visite d ta ble , so the func tion c a n quic kly te ll
whe the r a node ha s a lre a dy be e n visite d.
He re is the c ode tha t m a ke s this ha ppe n, c onsisting of ha she d ve rsions of our pre vious func tions:

(defun hash-edges (edge-list)

(let ((tab (make-hash-table)))

(mapc (lambda (x)


(let ((node (car x)))

(push (cdr x) (gethash node tab))))


edge-list)

tab))

First, we ne e d hash-edges, a func tion tha t c onve rts our e dge list into a ha sh ta ble . At the be ginning of the func tion, we

c re a te a ne w ha sh ta ble a nd na m e it tab . T he n, we ite ra te through the ta ble with mapc . Re m e m be r tha t mapc is just
like mapcar, e xc e pt tha t you use it in pla c e s whe re you c a re only a bout the side e ffe c ts a nd don’t c a re a bout ge ne ra ting a fina l list
a s a re sult.
For e ve ry node , we wa nt the ta ble to c onta in a list of node s c onne c te d to it. T he re fore , a s we ite ra te through the list, we push a

ne w ne ighbor onto the c urre nt list of ne ighbors for the c urre nt sta rting node . W e c a n use the push c om m a nd on ha sh ta ble
va lue s just a s for re gula r L isp va ria ble va lue s. T his, a ga in, m a ke s use of the ge ne ra l va ria ble s syste m built into Com m on L isp,
whic h we ’ll disc uss in Ha ndling Da ta in a Ge ne ric W a y in Ha ndling Da ta in a Ge ne ric W a y.
You m a y be wonde ring why we don’t ne e d to de a l with the c a se whe re the re is no va lue ye t for a node in the ta ble . How c a n we
push som e thing into a va lue in the ta ble if no va lue e xists? W e ll, it turns out tha t be c a use the gethash func tion re turns NIL whe n a
ke y is not found in the ta ble , this c ode will sim ply push the ne w ne ighbor onto a n e m pty list a nd stic k a ne w re c ord into the ta ble
whe re none wa s found be fore . In this wa y, the push c om m a nd m a gic a lly doe s the “ right thing, ” no m a tte r whe the r the node is ne w
or old.

Fina lly, onc e our ta ble is popula te d, we re turn it a s a re sult . It c onta ins the sa m e da ta a s the origina l e dge list. T he
diffe re nc e is tha t we c a n now find the ne ighbors of a ny node in Conge stion City a t bla z ing spe e ds.

(defun get-connected-hash (node edge-tab)

(let ((visited (make-hash-table)))


(labels ((traverse (node)

(unless (gethash node visited)

(setf (gethash node visited) t)

(mapc (lambda (edge)


(traverse edge))
(gethash node edge-tab)))))
(traverse node))

visited))
Now we ’re re a dy to write get-connected-hash, whic h re trie ve s a ll the node s c onne c te d to a sta rting node in Conge stion City
. It is ide ntic a l in be ha vior to get-connected, but is optim iz e d through ha sh ta ble s.

T he first thing this func tion doe s is c re a te a ha sh ta ble of visite d node s . T he n we tra ve l through the node s of Conge stion
City, be ginning with the sta rting node . E ve ry tim e we visit a ne w node , we a sk ourse lve s if we ’ve visite d it be fore . W e c a n now

a nswe r this que stion ve ry e ffic ie ntly by looking up the c urre nt node in the visited ta ble . If the a nswe r is no, we ’ll ne e d to

m a rk this node a s visite d a nd c he c k a ll of its ne ighbors by mapcing through the m — c he c king our e dge ta ble . Fina lly,

we re turn our visited ta ble , whic h in the e nd will hold a ll node s tha t a re c onne c te d to the sta rting node .
Now we c a n re run our te st with this ne w logic :
> (time (dotimes (i 100)
(get-connected-hash 1 (hash-edges (make-edge-list)))))
Real time: 1.221269 sec.
Run time: 1.224076 sec.
Space: 33096264 Bytes
GC: 36, GC time: 0.10801 sec. :
As you c a n se e , inste a d of ta king a m inute to c a lc ula te the c onne c tions in the gra ph, it now ta ke s only one se c ond to do the
sa m e thing! T his is why you m ust know how to use ha sh ta ble s.
Common Lisp Str uc tur e s
A struc ture is a n a dva nc e d da ta type a va ila ble in Com m on L isp. Struc ture s a nd the ir prope rtie s c a n be a use ful wa y to re pre se nt
da ta in your c ode .
W or king with Str uc tur e s
Struc ture s c a n be use d to re pre se nt obje c ts with prope rtie s, a s you m ight find in a typic a l obje c t-orie nte d progra m m ing (OOP)
la ngua ge using the defstruct c om m a nd, like so:
> (defstruct person
name
age
waist-size
favorite-color)
PERSON
Ac c ording to the de finition in this struc ture , a person ha s four prope rtie s (a lso c a lle d slots by L ispe rs): name, age, waist-size,
a nd favorite-color.
Ha ving de fine d this struc ture , we c a n c re a te insta nc e s of a pe rson using the make-person c om m a nd, a spe c ia l func tion tha t
defstruct ha s a utom a tic a lly c re a te d for us:
> (defparameter *bob* (make-person :name "Bob"
:age 35
:waist-size 32
:favorite-color "blue"))
*BOB*
Now whe n we e nte r *bob* into the RE PL , we se e our ne w pe rson m a rke d a s a struc ture with the #S pre fix. W e a lso se e tha t the
struc ture is of type person, a nd the va lue s of e a c h of its prope rtie s (name, age, waist size, a nd favorite-color):
> *bob*
#S(PERSON :NAME "Bob" :AGE 35 :WAIST-SIZE 32 :FAVORITE-COLOR "blue")
W e c a n de te rm ine Bob’s a ge by c a lling a nothe r a utom a tic a lly c re a te d func tion, person-age:
> (person-age *bob*)
35
W e c a n a lso use setf with the se c om m a nds to c ha nge Bob’s a ge . (Ha ppy birthda y, Bob!)
> (setf (person-age *bob*) 36)
36
T he L isp re a de r c a n a lso c re a te a pe rson dire c tly from the printe d re pre se nta tion of the pe rson, a nothe r gre a t e xa m ple of the
print/re a d sym m e try in L isp:

> (defparameter *that-guy* #S(person :name


"Bob" :age 35 :waist-size 32 :favorite-color "blue"))

> (person-age *that-guy*)


35
He re , we ’re c re a ting a ne w va ria ble c a lle d *that-guy*, a nd we se t its va lue using only the printe d re pre se nta tion of the pe rson

. T his va ria ble now ha s a re a l person struc ture in it, just a s if we ha d use d the make-person func tion .
As you c a n se e , defstruct is quite a powe rful c om m a nd tha t c a n be use d to build spe c ia l func tions tha t m a ke it e a sy to c re a te
insta nc e s of a ne w obje c t a nd a c c e ss its prope rtie s.
W he n to Use Str uc tur e s
T he se da ys, m a ny m a instre a m progra m m e rs be lie ve tha t obje c t orie nta tion is a ne c e ssity whe n de ve loping la rge a nd robust
a pplic a tions. Ma ny L ispe rs, on the othe r ha nd, be lie ve tha t it’s possible to build high-qua lity softwa re without ta king a pure ly OOP
a pproa c h.
Be ginning with Cha pte r 14, we ’ll e xa m ine som e of the se a lte rna te a pproa c he s, inc luding highe r-orde r func tiona l progra m m ing
a nd dom a in-spe c ific la ngua ge progra m m ing. T he de sign of the L isp la ngua ge m a ke s it m uc h e a sie r to ta ke a dva nta ge of the se
a lte rna te a pproa c he s tha n is possible with othe r, m ore obje c t-orie nte d la ngua ge s.
Re ga rdle ss, e ve n if you’re not writing pure ly OOP-style softwa re , struc ture s a nd the ir prope rtie s c a n still prove to be a use ful wa y
to re pre se nt da ta in your c ode . For insta nc e , inste a d of c re a ting a person c la ss with defstruct, we c ould do the sa m e thing with a
sta nda rd list a nd our own make-person func tion. Afte r a ll, why bothe r with struc ture s if we c a n just roll our own pe rson using lists,
like so:
> (defun make-person (name age waist-size favorite-color)
(list name age waist-size favorite-color))
MAKE-PERSON
> (defun person-age (person)
(cadr person))
PERSON-AGE
> (defparameter *bob* (make-person "bob" 35 32 "blue"))
*BOB*
> *bob*

("bob" 35 32 "blue")
> (person-age *bob*)
35
Although this a pproa c h will work, it ha s se ve ra l downside s. First, in orde r to c he c k a pe rson’s a ge or othe r prope rtie s, we would
ne e d to write a lot of e rror-prone func tions tha t pull prope rtie s out of the list from the c orre c t loc a tions. Also, the printe d ve rsion of

our a d hoc obje c t is ve ry ha rd to unde rsta nd. How do we know BOB is a pe rson? Is Bob’s a ge 35 or 32? Re gula r lists just don’t
le nd the m se lve s we ll to e nc oding obje c ts with m ultiple prope rtie s.
Anothe r proble m with using lists to re pre se nt a n obje c t in the re a l world is tha t the prope rtie s of a n obje c t (like a person obje c t)
m a y c ha nge ove r tim e . L ists in L isp work be st whe n you a re de a ling with inform a tion tha t ne ve r c ha nge s onc e the list is c re a te d.
W he n Bob turns 36, howe ve r, we ne e d to c ha nge his a ge prope rty.
Ha ving pa rt of a da ta struc ture c ha nge ove r tim e is c a lle d a mutation by c om pute r sc ie ntists. It’s e a sy to c ha nge the va lue of a
spe c ific prope rty (m uta te the prope rty) in a struc ture c re a te d with defstruct, so the se struc ture s a re ve ry suita ble for ha ndling da ta
tha t ne e ds to be m uta ble . T he re fore , it m a ke s se nse to store a person (or a ny othe r obje c t tha t c ha nge s ove r tim e ) in a struc ture .
W e will be disc ussing the issue of m uta tion in gre a te r de ta il in Cha pte r 14.

Note

T he defstruct fa c ility is not the only tool tha t c a n be use d to c re a te obje c ts in Com m on L isp. For e xa m ple , in the e pilogue of
the book, you’ll se e tha t Com m on L isp’s Com m on L isp Obje c t Syste m (CL OS) a llows you to build ve ry sophistic a te d obje c t-ba se d
syste m s. If you c a re to c ode with a strongly obje c t-orie nte d m indse t, you will proba bly find a ll the OOP la ngua ge func tiona lity you
ne e d in Com m on L isp. Inde e d, CL OS ha s m a ny a dva nc e d obje c t-orie nte d fe a ture s tha t you won’t find in m a ny othe r pla c e s.
Be c a use of this, CL OS ha s ofte n be e n use d a s a re se a rc h tool for studying OOP ide a s.
H andling Data in a G e ne r ic W ay
Com m on L isp ha s m a ny diffe re nt da ta type s a va ila ble for writing e le ga nt a nd e ffic ie nt progra m s. But without som e c a re , ha ving
so m a ny da ta type s c a n le a d to ugly a nd re pe titive c ode .
For e xa m ple , suppose we wa nt to a dd se ve ra l groups of num be rs, whic h a re store d a s both lists a nd a rra ys. Sinc e lists a nd a rra ys
be ha ve diffe re ntly, will we ne e d to write two diffe re nt a ddition func tions— one for lists a nd the othe r for a rra ys? It would be gre a t
if we c ould write a single c hunk of c ode to ha ndle both c a se s without c a ring a bout how the num be rs a re store d.
Com m on L isp ha s a ll the fe a ture s we ne e d to write suc h ge ne ric c ode , inc luding ge ne ric libra ry func tions, type pre dic a te s,
defmethod, a nd ge ne ric a c c e ssors. W e c a n use the se fe a ture s to write c ode tha t works with m a ny type s of da ta — inc luding built-in
a s we ll a s c ustom type s tha t we m ight c re a te with defstruct— without supe rfluous re pe tition in our c ode .
W or king with Se que nc e s
T he e a sie st wa y to write c ode tha t works with a ny type of a rgum e nt is to ha nd the type -c he c king work to som e one e lse . T he
Com m on L isp libra rie s a re pa c ke d with func tions tha t c a n ge ne ric a lly ha ndle da ta of va rying type s in the ir a rgum e nts, the m ost
c om m only use d of whic h a re the se que nc e func tions. T he se que nc e func tions work ge ne ric a lly a c ross the thre e m a in wa ys of
se que nc ing obje c ts in L isp: lists, a rra ys, a nd strings.
You’ve a lre a dy se e n one of the se se que nc e func tions without e ve n re a liz ing it: the length func tion. You c a n use the length
func tion to c he c k for the le ngth of a ll thre e se que nc e type s:
> (length '(a b c))
3
> (length "blub")
4
> (length (make-array 5))
5
W ithout the ge ne ric length func tion, you would ne e d to use thre e se pa ra te func tions to de te rm ine the le ngth of strings, a rra ys,
a nd lists.

Note

Com m on L isp ha s a spe c ific func tion for c he c king the le ngth of lists, c a lle d list-le ngth. Be c a use ge ne ric func tions te nd to
re quire e xtra type -c he c king to de te rm ine the c orre c t be ha vior, the y c a n be slowe r to e xe c ute . T he list-length func tion is use ful
for pe rform a nc e -se nsitive c ode , but m ost L ispe rs pre fe r using the ge ne ric length func tion in re gula r c ode .

Se que nc e F unc tions for Se ar c hing

Som e se que nc e func tions le t you se a rc h se que nc e s:

find-if finds the first va lue tha t sa tisfie s a pre dic a te .


count finds out how ofte n a c e rta in obje c t a ppe a rs in se que nc e .
position te lls you whe re a n ite m is loc a te d.
some a nd every te ll you if som e or e ve ry va lue in a se que nc e obe ys a spe c ific pre dic a te .

He re a re som e e xa m ple s:

> (find-if #'numberp '(a b 5 d))


5

> (count #\s "mississippi")


4

> (position #\4 "2kewl4skewl")


5

> (some #'numberp '(a b 5 d))


T

> (every #'numberp '(a b 5 d))


NIL

In the se e xa m ple s, we use find-if to find the first num be r in a se que nc e , whic h is the num be r 5 . W e use count to find

out how m a ny tim e s the c ha ra c te r s a ppe a rs in "mississippi" . W e use position to find a t wha t position the c ha ra c te r 4

a ppe a rs. In this c a se , it is in the fifth position, sta rting the c ount from z e ro . W e use some to se e if a ny ite m s in a se que nc e

a re num be rs. Inde e d, the re is a num be r . Fina lly, we use every to se e if e ve ry ite m in the list is a num be r, whic h is not the

c a se .

Se que nc e F unc tions for Ite r ating Ac r oss a Se que nc e

One pa rtic ula rly use ful ge ne ric se que nc e func tion is reduce. T he reduce func tion a llows you to ite ra te through a se que nc e a nd
distill it down into a single re sult. He re , we use reduce to a dd toge the r ite m s in a list:
> (reduce #'+ '(3 4 6 5 2))
20
T he sum of those num be rs turns out to be 20. He re is a dia gra m tha t shows e xa c tly wha t is ha ppe ning in this e xa m ple :
On the right side , shown in gra y, is our list. On the le ft side , you c a n se e the pa irs of num be rs tha t a re fe d into the plus (+)
func tion a nd the inte rm e dia te re sults tha t a re c a lc ula te d. T his shows tha t the plus func tion a lwa ys re c e ive s a single inte rm e dia te
re sult a s we ll a s the ne xt num be r in the list a s its a rgum e nts. T he only e xc e ption to this is in the ve ry first c a ll to the plus func tion.
Sinc e no inte rm e dia te re sult e xists whe n we sta rt, the first tim e we c a ll the plus func tion, we prom ote the num be r 3, whic h is a t
the sta rt of the list, into our inte rm e dia te re sult c olum n. T he re fore , the first tim e the plus func tion is c a lle d, it a c tua lly re c e ive s
two ite ms stra ight off the top of the list.
L e t’s look a t a slightly m ore c om plic a te d e xa m ple , this tim e using our own re duc tion func tion. W e ’re going to find the la rge st
e ve n num be r in the list:

> (reduce (lambda (best item)

(if (and (evenp item) (> item best))

item

best))
'(7 4 6 5 2)

:initial-value 0)
6

Our re duc tion func tion, whic h we pa ss to reduce to distill down our a nswe r from the list, ha s two a rgum e nts . T he first
a rgum e nt is the be st va lue we ’ve found so fa r— in othe r words, the la rge st e ve n num be r we ’ve found so fa r. T he se c ond a rgum e nt is
the ne xt num be r from the list.
Our reduce func tion ne e ds to re turn a s a re sult the ne w be st num be r. T he re fore , if the la te st num be r is be tte r tha n the pre vious

be st , we re turn it . Othe rwise , we re turn the pre vious be st .


Re m e m be r tha t the first num be r in the list we ’re re duc ing will be use d a s a sta rting va lue . If this is a proble m , we c a n inste a d

pa ss a n e xplic it initia l va lue to the reduce func tion by pa ssing in a ke yword pa ra m e te r na m e d :initial-value .
Spe c ifying a n initia l va lue for the reduce func tion is ofte n ne c e ssa ry, or a bug c a n sne a k into your c ode . In our e xa m ple , it
c ould a llow a n odd num be r a t the front of the list to e rrone ously be de e m e d the be st la rge e ve n num be r. L e t’s se e wha t ha ppe ns if
we le a ve out the initia l va lue .
> (reduce (lambda (best item)
(if (and (evenp item) (> item best))
item
best))
'(7 4 6 5 2))
7
Ye s, things go horribly, horribly wrong, a s a re sult of not spe c ifying a n initia l reduce va lue .
Anothe r gre a t be ne fit of the reduce func tion is tha t it is ge ne ric , a s is true for a ll the se se que nc e func tions. T his m e a ns tha t it
c a n reduce lists, a rra ys, or strings in e xa c tly the sa m e wa y, a nd you c a n use reduce to write func tions tha t a re oblivious to the
diffe re nc e be twe e n the se diffe re nt se que nc e type s.
E a rlie r, I m e ntione d tha t it would be c onve nie nt to be a ble to write a single func tion tha t c ould sum toge the r num be rs in lists or
a rra ys e qua lly we ll. Now we c a n write suc h a func tion:
> (defun sum (lst)
(reduce #'+ lst))
SUM
> (sum '(1 2 3))
6
> (sum (make-array 5 :initial-contents '(1 2 3 4 5)))
15
> (sum "blablabla")
Error: The value #\b is not of type NUMBER.

sum is blissfully una wa re of the diffe re nc e be twe e n a rra ys a nd lists; it works on both. Howe ve r, sinc e a ddition doe sn’t m a ke a ny
se nse for c ha ra c te rs, the sum func tion re turns a n e rror whe n use d on a string.
Anothe r func tion tha t is use ful for ite ra ting a c ross a se que nc e is the map func tion. T his func tion is ide ntic a l in be ha vior to
mapcar. Howe ve r, unlike mapcar, the map func tion works on a ll se que nc e type s, not just lists. You spe c ify the type of se que nc e to
re turn from the m a pping by pa ssing a n e xtra a rgum e nt to the map func tion.
He re is a n e xa m ple of map:

> (map 'list


(lambda (x)

(if (eq x #\s)


#\S
x))
"this is a string")

(#\t #\h #\i #\S #\ #\i #\S #\ #\a #\ #\S #\t #\r #\i #\n #\g)
In this e xa m ple , we ’re turning e ve ry s c ha ra c te r in a string to its uppe rc a se ve rsion. T he m a pping func tion we pa ss into map

sim ply c he c ks if the c urre nt c ha ra c te r is a n s a nd re turns the uppe rc a se S if it is .

T he re sult of this c a lc ula tion is a list of c ha ra c te rs . T his is be c a use we told the map func tion tha t we wa nte d a list a s a

re sult . Ha d we a ske d for a string inste a d, a string would ha ve be e n our re sult.

Two M or e Impor tant Se que nc e F unc tions

T he subseq func tion le ts you pull a subse que nc e out of a la rge r se que nc e by spe c ifying sta rting a nd e nding points:
> (subseq "america" 2 6)
"eric"
As you c a n se e , the word america c onta ins the na m e eric, sta rting from the se c ond c ha ra c te r a nd e nding a t the sixth c ha ra c te r.
T he sort func tion le ts you pa ss it a n a rbitra ry func tion to use for the sorting. In this c a se , we ’re just using the le ss-tha n (<)
func tion:
> (sort '(5 8 2 4 9 3 6) #'<)
(2 3 4 5 6 8 9)
T he re a re m a ny m ore se que nc e func tions tha n we ’ve disc usse d so fa r, but the e xa m ple s in this c ha pte r will ge t you off to a good
sta rt.

Note

For a c om pre he nsive list of se que nc e func tions, a nd inde e d a ll Com m on L isp func tions, visit the Common Lisp Hy pe rspe c a t
http://www. snipurl. c om /rz 3h0— a n e xha ustive , but da unting, de sc ription of a ll Com m on L isp ha s to offe r.
Cr e ating Your O wn G e ne r ic F unc tions with Type P r e dic ate s
Com m on L isp, like virtua lly a ll othe r L isps, is a dyna m ic a lly type d la ngua ge . T his m e a ns tha t pa ra m e te rs or va ria ble s in your
c ode c a n hold a ny type of da ta — sym bols, strings, num be rs, func tions, or wha te ve r e lse you wa nt to pla c e in the m . In fa c t, the
sa m e pa ra m e te r or va ria ble c a n e ve n hold diffe re nt type s of da ta a t diffe re nt tim e s in a running progra m .
T he re fore , it m a ke s se nse to ha ve a bunc h of func tions tha t te ll you whe the r a va ria ble ha s a c e rta in type of da ta in it. For
insta nc e , you c a n c he c k whe the r you ha ve a num be r with numberp:
> (numberp 5)
T
T he type pre dic a te s you will proba bly use m ost fre que ntly a re arrayp, characterp, consp, functionp, hash-table-p, listp,
stringp, a nd symbolp.
You c a n use type pre dic a te s to write func tions tha t ha ndle diffe re nt type s of da ta ge ne ric a lly. Suppose we wa nte d to write a
func tion tha t le ts us a dd both num be rs or lists. He re ’s one wa y we c ould write suc h a func tion:
> (defun add (a b)
(cond ((and (numberp a) (numberp b)) (+ a b))
((and (listp a) (listp b)) (append a b))))
ADD
> (add 3 4)
7
> (add '(a b) '(c d))
(A B C D)
In this add func tion, we use pre dic a te s to se e if the a rgum e nts pa sse d in a re num be rs or lists, a nd the n we a c t a ppropria te ly. If we
a re n’t give n two num be rs or two lists, it sim ply re turns nil.
Although you c a n write func tions supporting m ultiple type s of da ta using type pre dic a te s, m ost L ispe rs wouldn’t write a n add
func tion this wa y, for the following re a sons:
A single , monolithic func tion for all type s:
T his is fine for just two type s, but if we wa nte d to ha ndle a doz e n or m ore type s, our func tion would quic kly turn into a
gia nt m onstrosity.
M odific ations r e quir e d to ac c ommodate ne w c ase s:
W e would ne e d to c ha nge the a dd func tion whe ne ve r we wa nt to support a ne w type , inc re a sing the c ha nc e tha t we would
bre a k e xisting c ode . Ide a lly, we would like to ha ndle e a c h ne w situa tion by itse lf without touc hing a lre a dy working c ode .
H ar d to unde r stand:
It is ha rd to se e e xa c tly wha t the m a in c ond sta te m e nt is doing a nd if the type s a re a ll be ing route d to the right pla c e .
P e r for manc e :
T he re sulting func tion m ight be slow. For insta nc e , a L isp inte rpre te r/c om pile r m ight be a ble to c re a te fa ste r c ode for
a ppe nding two lists if it kne w for sure tha t both ite m s we re lists whe n the a ppe nding ha ppe ns. Howe ve r, in our first a tte m pt a t
the a dd func tion, the type of the two a rgum e nts is ne ve r re a lly c om ple te ly obvious. Our c om pile r would ne e d a bit of sm a rts to
be a ble to te ll from the c ondition (and (listp a) (listp b)) tha t both va ria ble s a re gua ra nte e d to be lists. L ife would be
e a sie r for the c om pile r if we e xplic itly sta te d the type s of a rgum e nts for e a c h type situa tion.
Be c a use it is so use ful to be a ble to ha ve a single func tion tha t doe s diffe re nt things whe n give n c e rta in da ta type s, the Com m on
L isp c om m a nd defmethod le ts us de fine m ultiple ve rsions of a func tion tha t e a c h supports diffe re nt type s. W he n tha t func tion is
c a lle d, L isp c he c ks the a rgum e nt type s a t the tim e of the c a ll a nd c hoose s the c orre c t ve rsion of the func tion a utom a tic a lly. T he
prope r te rm for ha ving a c om pile r/inte rpre te r c hoose a m ong diffe re nt ve rsions of a func tion ba se d on a rgum e nt type s is ty pe
dispatc hing.
He re ’s how we would write our add func tion using defmethod:
> (defmethod add ((a number) (b number))
(+ a b))
ADD
> (defmethod add ((a list) (b list))
(append a b))
ADD
> (add 3 4)
7
> (add '(a b) '(c d))
(A B C D)
As you c a n se e , this ve rsion of the add func tion ha ndle s e ve ry type of situa tion with a se pa ra te func tion, a nd ne w c a se s c a n be
a dde d without m odifying e xisting c ode . Ove ra ll, the c ode is m uc h e a sie r to unde rsta nd. Also, the c om pile r c a n se e the type of the
pa ra m e te rs a nd m a y be a ble to write fa ste r c ode using this knowle dge .
T he defmethod func tion is like defun, e xc e pt tha t it a llows us to write m ultiple func tions with the sa m e na m e . W he n using
defmethod, we c a n e xplic itly sta te the type of e a c h pa ra m e te r in the func tion’s a rgum e nt list so tha t L isp c a n use the se type
de c la ra tions to figure out the c orre c t ve rsion of add for e a c h situa tion.
If you’re fa m ilia r with the world of OOP, the word me thod proba bly ha s a spe c ia l m e a ning to you. Sinc e this ne w c om m a nd is
c a lle d defmethod, doe s it ha ve a nything to do with OOP? In short, ye s. T his c om m a nd c a n be use d not only with Com m on L isp’s
built-in type s, but a lso with struc ture s you’ve c re a te d with defstruct. T he c om bina tion of defstruct a nd defmethod ba sic a lly
c onstitute s a sim ple obje c t syste m .
Now we ’ll use this obje c t syste m to write a ga m e !
The O r c Battle G ame
In the Orc Ba ttle ga m e , you’re a knight surrounde d by 12 m onste rs, e nga ge d in a fight to the de a th. W ith your supe rior wits a nd
your re pe rtoire of sword-fighting m a ne uve rs, you m ust c a re fully stra te giz e in your ba ttle with orc s, hydra s, a nd othe r na sty e ne m ie s.
One wrong m ove a nd you m a y be una ble to kill the m a ll be fore be ing worn down by the ir supe rior num be rs. Using defmethod a nd
defstruct, le t’s dispa tc h som e whoop a ss on the se ve rm in!
G lobal Var iable s for the P laye r and M onste r s
W e ’ll wa nt to tra c k thre e pla ye r sta ts: he a lth, a gility, a nd stre ngth. W he n a pla ye r’s he a lth re a c he s z e ro, tha t pla ye r will die .
Agility will c ontrol how m a ny a tta c ks a pla ye r c a n pe rform in a single round of ba ttle , a nd stre ngth will c ontrol the fe roc ity of the
a tta c ks. As the ga m e progre sse s, e a c h of the se will c ha nge a nd a ffe c t ga m e pla y a nd stra te gy in subtle wa ys.
(defparameter *player-health* nil)
(defparameter *player-agility* nil)
(defparameter *player-strength* nil)
W e ’ll store our m onste rs in a n a rra y c a lle d *monsters*. T his a rra y will be he te roge ne ous, m e a ning it c a n c onta in diffe re nt type s
of m onste rs, be the y orc s, hydra s, or a nything e lse . W e ’ll c re a te our m onste r type s with defstruct. Of c ourse , we still ne e d to
figure out how to ha ndle e a c h type in the list in a m e a ningful wa y— tha t’s whe re we ’ll use L isp’s ge ne ric fe a ture s.
W e ’ll a lso de fine a list of func tions for building m onste rs tha t we ’ll store in the va ria ble *monster-builders*. As we write the
c ode for e a c h type of m onste r, we ’ll c re a te a func tion tha t builds a m onste r of e a c h type . W e ’ll the n push e a c h of the se m onste r
builde rs onto this list. Ha ving a ll the builde r func tions in this list will m a ke it e a sy for us to c re a te ra ndom m onste rs a t will for our
ga m e .
Fina lly, we ’ll c re a te the va ria ble *monster-num* to c ontrol how m a ny oppone nts our knight m ust fight. Cha nge this va ria ble to
inc re a se (or de c re a se ) the diffic ulty le ve l of Orc Ba ttle .
(defparameter *monsters* nil)
(defparameter *monster-builders* nil)
(defparameter *monster-num* 12)
M ain G ame F unc tions
Now we ’re re a dy to write our first re a l c ode for the ga m e , sta rting with the big pic ture func tions tha t drive the re st of the syste m .
First, we ’ll de fine a func tion c a lle d orc-battle. T his func tion will initia liz e the m onste rs a nd sta rt the ga m e loop a nd, onc e the
ba ttle e nds, it will de te rm ine the vic tor a nd print the a ppropria te e nding m e ssa ge for the ga m e . As you c a n se e , orc-battle c a lls
ple nty of he lpe r func tions to do the a c tua l work:
(defun orc-battle ()

(init-monsters)
(init-player)

(game-loop)

(when (player-dead)
(princ "You have been killed. Game Over."))

(when (monsters-dead)
(princ "Congratulations! You have vanquished all of your foes.")))

At the top, we c a ll the initia liz a tion func tions for the m onste rs a nd the pla ye r . T he n we sta rt the m a in ga m e loop .
T he ga m e loop will ke e p running until e ithe r the pla ye r or the m onste rs a re de a d. W e ’ll print a ga m e -e nding m e ssa ge de pe nding

on whe the r the pla ye r or m onste rs die d.


Ne xt, we ’ll c re a te the func tion game-loop to ha ndle the ga m e loop. T his func tion ha ndle s a round of the ba ttle , a nd the n c a lls
itse lf re c ursive ly for the following round:
(defun game-loop ()
(unless (or (player-dead) (monsters-dead))

(show-player)

(dotimes (k (1+ (truncate (/ (max 0 *player-agility*) 15))))


(unless (monsters-dead)
(show-monsters)
(player-attack)))
(fresh-line)

(map 'list
(lambda(m)

(or (monster-dead m) (monster-attack m)))


*monsters*)

(game-loop)))
T he game-loop func tion ha ndle s the re pe a te d c yc le s of m onste r a nd pla ye r a tta c ks. As long a s both pa rtie s in the fight a re still

a live , the func tion will first show som e inform a tion a bout the pla ye r in the RE PL .
Ne xt, we a llow the pla ye r to a tta c k the m onste rs. T he game-loop func tion use s the pla ye r’s a gility to m odula te how m a ny
a tta c ks c a n be la unc he d in a single round of ba ttle , using som e fudge fa c tors to tra nsform the a gility to a sm a ll, a ppropria te

num be r . W he n the ga m e be gins, the pla ye r will ha ve thre e a tta c ks pe r round. L a te r sta ge s of ba ttle c ould c a use this num be r
to drop to a single a tta c k pe r round.

T he c a lc ula te d a gility fa c tor for our pla ye r a tta c k loop is pa sse d into the dotimes c om m a nd, whic h ta ke s a va ria ble na m e
a nd a num be r n, a nd runs a c hunk of c ode n tim e s:
> (dotimes (i 3)
(fresh-line)
(princ i)
(princ ". Hatchoo!"))
0. Hatchoo!
1. Hatchoo!
2. Hatchoo!
T he dotimes func tion is one of Com m on L isp’s looping c om m a nds (looping is c ove re d in m ore de ta il in Cha pte r 10).
Afte r the pla ye r ha s a tta c ke d, we a llow the m onste rs to a tta c k. W e do this by ite ra ting through our list of m onste rs with the map

func tion . E ve ry type of m onste r ha s a spe c ia l monster-attack c om m a nd, whic h we ’ll c a ll a s long a s the m onste r is still

a live .
Fina lly, the game-loop func tion c a lls itse lf re c ursive ly, so tha t the ba ttle c a n c ontinue until one side or the othe r ha s be e n

va nquishe d .
P laye r M anage me nt F unc tions
T he func tions we ne e d for m a na ging the pla ye r’s a ttribute s (he a lth, a gility, a nd stre ngth) a re ve ry sim ple . Following a re the
func tions we ne e d to initia liz e pla ye rs, to se e if the y’ve die d, a nd to output the ir a ttribute s:
(defun init-player ()
(setf *player-health* 30)
(setf *player-agility* 30)
(setf *player-strength* 30))
(defun player-dead ()
(<= *player-health* 0))
(defun show-player ()
(fresh-line)
(princ "You are a valiant knight with a health of ")
(princ *player-health*)
(princ ", an agility of ")
(princ *player-agility*)
(princ ", and a strength of ")
(princ *player-strength*))
T he player-attack func tion le ts us m a na ge a pla ye r’s a tta c k:
(defun player-attack ()
(fresh-line)

(princ "Attack style: [s]tab [d]ouble swing [r]oundhouse:")

(case (read)

(s (monster-hit (pick-monster)

(+ 2 (randval (ash *player-strength* −1)))))

(d (let ((x (randval (truncate (/ *player-strength* 6)))))


(princ "Your double swing has a strength of ")
(princ x)
(fresh-line)

(monster-hit (pick-monster) x)
(unless (monsters-dead)

(monster-hit (pick-monster) x))))

(otherwise (dotimes (x (1+ (randval (truncate (/ *player-strength* 3)))))


(unless (monsters-dead)

(monster-hit (random-monster) 1))))))

First, this func tion prints out som e diffe re nt type s of a tta c ks from whic h the pla ye r c a n c hoose . As you c a n se e , the pla ye r
is offe re d thre e possible a tta c ks: a sta b, a double swing, a nd a roundhouse swing. W e re a d in the pla ye r’s se le c tion, a nd the n ha ndle

e a c h type of a tta c k in a case sta te m e nt .


T he sta b a tta c k is the m ost fe roc ious a tta c k a nd c a n be de live re d a ga inst a single foe . Sinc e a sta b is pe rform e d a ga inst a single

e ne m y, we will first c a ll the pick-monster func tion to le t the pla ye r c hoose whom to a tta c k . T he a tta c k stre ngth is
c a lc ula te d from the *player-strength*, using a ra ndom fa c tor a nd som e othe r little twe a ks to ge ne ra te a nic e , but ne ve r too

powe rful, a tta c k stre ngth . Onc e the pla ye r ha s c hose n a m onste r to a tta c k a nd the a tta c k stre ngth ha s be e n c a lc ula te d, we

c a ll the monster-hit func tion to a pply the a tta c k .


Unlike the sta b a tta c k, the double swing is we a ke r, but a llows two e ne m ie s to be a tta c ke d a t onc e . An a dditiona l be ne fit of the
a tta c k is tha t the knight c a n te ll, a s the swing be gins, how strong it will be — inform a tion tha t c a n the n be use d to c hoose the be st
e ne m ie s to a tta c k m idswing. T his e xtra fe a ture of the double swing a dds stra te gic de pth to the ga m e . Othe rwise , the double -swing

c ode is sim ila r to the sta b c ode , printing a m e ssa ge a nd a llowing the pla ye r to c hoose whom to a tta c k. In this c a se , howe ve r,

two m onste rs c a n be c hose n .


T he fina l a tta c k, the roundhouse swing, is a wild, c ha otic a tta c k tha t doe s not disc rim ina te a m ong the e ne m ie s. W e run through

a dotimes loop ba se d on the pla ye r’s stre ngth a nd the n a tta c k ra ndom foe s m ultiple tim e s. Howe ve r, e a c h a tta c k is ve ry

we a k, with a stre ngth of only 1 .


T he se a tta c ks m ust be use d c orre c tly, a t the right sta ge s of a ba ttle , in orde r to a c hie ve vic tory. T o a dd som e ra ndom ne ss to the
a tta c ks in the player-attack func tion, we use d the randval he lpe r func tion to ge ne ra te ra ndom num be rs. It is de fine d a s follows:
(defun randval (n)
(1+ (random (max 1 n))))
T he randval func tion re turns a ra ndom num be r from one to n, while m a king sure tha t no m a tte r how sm a ll n is, a t le a st the
num be r 1 will be re turne d. Using randval inste a d of just the random func tion for ge ne ra ting ra ndom num be rs give s a re a lity c he c k
to the ra ndom ne ss of the ga m e , sinc e 0 doe sn’t m a ke se nse for som e of the va lue s we use in our c a lc ula tions. For insta nc e , e ve n the
we a ke st pla ye r or m onste r should a lwa ys ha ve a n a tta c k stre ngth of a t le a st 1.
T he random func tion use d by randval is the c a nonic a l ra ndom va lue func tion in L isp. It c a n be use d in se ve ra l diffe re nt wa ys,
though m ost fre que ntly it is use d by pa ssing in a n inte ge r n a nd re c e iving a ra ndom inte ge r from 0 to n−1:
> (dotimes (i 10)
(princ (random 5))
(princ " "))
1 2 2 4 0 4 2 4 2 3
H e lpe r F unc tions for P laye r Attac ks
Our player-attack func tion ne e ds two he lpe r func tions to do its job. First, it ne e ds a random-monster func tion tha t pic ks a
m onste r to ta rge t for the c ha otic roundhouse a tta c k, while e nsuring tha t the c hose n m onste r isn’t a lre a dy de a d:
(defun random-monster ()

(let ((m (aref *monsters* (random (length *monsters*)))))


(if (monster-dead m)

(random-monster)

m)))

T he random-monster func tion first pic ks a ra ndom m onste r out of the a rra y of m onste rs a nd store s it in the va ria ble m .
Sinc e we wa nt to pic k a living m onste r to a tta c k, we re c ursive ly try the func tion a ga in if we ina dve rte ntly pic ke d a de a d m onste r

. Othe rwise , we re turn the c hose n m onste r .


T he player-attack func tion a lso ne e ds a func tion tha t a llows the pla ye r to pic k a m onste r to ta rge t for the nonra ndom a tta c ks.
T his is the job of the pick-monster func tion:
(defun pick-monster ()
(fresh-line)

(princ "Monster #:")

(let ((x (read)))

(if (not (and (integerp x) (>= x 1) (<= x *monster-num*)))


(progn (princ "That is not a valid monster number.")
(pick-monster))

(let ((m (aref *monsters* (1-x))))

(if (monster-dead m)
(progn (princ "That monster is alread dead.")
(pick-monster))

m)))))

In orde r to le t the pla ye r pic k a m onste r to a tta c k, we first ne e d to displa y a prom pt a nd re a d in the pla ye r’s c hoic e .

T he n we ne e d to m a ke sure the pla ye r c hose a n inte ge r tha t isn’t too big or too sm a ll . If this ha s ha ppe ne d, we print a
m e ssa ge a nd c a ll pick-monster a ga in to le t the pla ye r c hoose a ga in. Othe rwise , we c a n sa fe ly pla c e the c hose n m onste r in the

va ria ble m .
Anothe r e rror the pla ye r c ould m a ke is to a tta c k a m onste r tha t is a lre a dy de a d. W e c he c k for this possibility ne xt a nd, onc e

a ga in, a llow the pla ye r to m a ke a nothe r se le c tion . Othe rwise , the pla ye r ha s suc c e ssfully m a de a c hoic e , a nd we re turn the

se le c te d m onste r a s a re sult .
Now le t’s work on our m onste rs.
M onste r M anage me nt F unc tions
W e ’ll use the init-monsters func tion to initia liz e a ll the ba d guys store d in the *monsters* a rra y. T his func tion will ra ndom ly
pic k func tions out of the *monster-builders* list a nd c a ll the m with funcall to build the m onste rs:
(defun init-monsters ()
(setf *monsters*

(map 'vector
(lambda (x)

(funcall (nth (random (length *monster-builders*))


*monster-builders*)))

(make-array *monster-num*))))

First, the init-monsters func tion builds a n e m pty a rra y to hold the m onste rs . T he n it maps a c ross this a rra y to fill it up

. In the lambda func tion, you c a n se e how ra ndom m onste rs a re c re a te d by funcalling ra ndom func tions in our list of m onste r

builde rs .
Ne xt, we ne e d som e sim ple func tions for c he c king if the m onste rs a re de a d. Notic e how we use the e ve ry c om m a nd on the
*monsters* a rra y to se e if the func tion monster-dead is true for e ve ry m onste r. T his will te ll us whe the r the e ntire m onste r
popula tion is de a d.
(defun monster-dead (m)
(<= (monster-health m) 0))

(defun monsters-dead ()
(every #'monster-dead *monsters*))
W e ’ll use the show-monsters func tion to displa y a listing of a ll the m onste rs. T his func tion will, in turn, de fe r pa rt of the work
to a nothe r func tion, so it doe sn’t a c tua lly ne e d to know a lot a bout the diffe re nt m onste r type s:
(defun show-monsters ()
(fresh-line)
(princ "Your foes:")

(let ((x 0))

(map 'list
(lambda (m)
(fresh-line)
(princ " ")

(princ (incf x))


(princ ". ")
(if (monster-dead m)

(princ "**dead**")
(progn (princ "(Health=")

(princ (monster-health m))


(princ ") ")

(monster-show m))))
*monsters*)))
Sinc e our pla ye r will ne e d to c hoose m onste rs with a num be r, we will m a inta in a c ount a s we loop through m onste rs in our list,

in the va ria ble x . T he n we map through our m onste r list, c a lling a lambda func tion on e a c h m onste r, whic h will print out

som e pre tty te xt for e a c h m onste r . W e use our x va ria ble to print out the num be r for e a c h m onste r in our num be re d list

. As we do this, we use the incf func tion, whic h will inc re m e nt x a s we work through the list.

For de a d m onste rs, we won’t print m uc h a bout the m , just a m e ssa ge showing tha t the y a re de a d . For living m onste rs, we

c a ll ge ne ric m onste r func tions, c a lc ula ting the he a lth a nd ge ne ra ting the m onste r de sc ription in a spe c ia liz e d wa y for
e a c h diffe re nt type of foe .
The M onste r s
So fa r, we ha ve n’t se e n a ny func tions tha t re a lly give life to the m onste rs. L e t’s fix tha t.
First, we ’ll de sc ribe a ge ne ric m onste r.

The G e ne r ic M onste r

As you would e xpe c t, orc s, hydra s, a nd othe r ba d guys a ll ha ve one thing in c om m on: a he a lth m e te r tha t de te rm ine s how m a ny
hits the y c a n ta ke be fore the y die . W e c a n c a pture this be ha vior in a monster struc ture :
(defstruct monster (health (randval 10)))
T his use of the defstruct func tion ta ke s a dva nta ge of a spe c ia l fe a ture : W he n we de c la re e a c h slot in the struc ture (in this c a se ,
health) we c a n put pa re nthe se s a round the na m e a nd a dd a de fa ult va lue for tha t slot. But m ore im porta nt, we c a n de c la re a form
tha t will be e va lua te d whe n a ne w monster is c re a te d. Sinc e this form c a lls randval, e ve ry m onste r will sta rt the ba ttle with a
diffe re nt, ra ndom , he a lth.
L e t’s try c re a ting som e m onste rs:
> (make-monster)
#S(MONSTER :HEALTH 7)
> (make-monster)
#S(MONSTER :HEALTH 2)
> (make-monster)
#S(MONSTER :HEALTH 5)
W e a lso ne e d a func tion tha t ta ke s a wa y a m onste r’s he a lth whe n it’s a tta c ke d. W e ’ll ha ve this func tion output a m e ssa ge
e xpla ining wha t ha ppe ne d, inc luding a m e ssa ge to be displa ye d whe n the m onste r die s. Howe ve r, inste a d of c re a ting this func tion
wi t h defun, we ’ll use the ge ne ric defmethod, whic h will le t us displa y spe c ia l m e ssa ge s whe n the knight be a ts on pa rtic ula r
m onste rs:
(defmethod monster-hit (m x)

(decf (monster-health m) x)
(if (monster-dead m)
(progn (princ "You killed the ")

(princ (type-of m))


(princ "! "))
(progn (princ "You hit the ")

(princ (type-of m))


(princ ", knocking off ")
(princ x)
(princ " health points! "))))

T h e decf func tion is a va ria nt of setf tha t le ts us subtra c t a n a m ount from a va ria ble . T he type-of func tion le ts

monster-hit pre te nd it knows the type of the m onste r tha t wa s hit . T his func tion c a n be use d to find the type of a ny
L isp va lue :
> (type-of 'foo)
SYMBOL
> (type-of 5)
INTEGER
> (type-of "foo")
ARRAY
> (type-of (make-monster))
MONSTER
Curre ntly, the type of a m onste r will a lwa ys be monster, but soon we ’ll ha ve this va lue c ha nge for e a c h m onste r type .
W e c a n a lso use two m ore ge ne ric m e thods to c re a te m onste rs: monster-show a nd monster-attack.
T he monster-attack func tion doe sn’t a c tua lly do a nything. T his is be c a use a ll our m onste r a tta c ks will be so unique tha t the re ’s
no point in de fining a ge ne ric a tta c k. T his func tion is sim ply a pla c e holde r.
(defmethod monster-show (m)
(princ "A fierce ")
(princ (type-of m)))
(defmethod monster-attack (m))
Now tha t we ha ve som e ge ne ric m onste r c ode , we c a n fina lly c re a te som e a c tua l ba d guys!

The W ic ke d O r c

T he orc is a sim ple foe . He c a n de live r a strong a tta c k with his c lub, but othe rwise he is pre tty ha rm le ss. E ve ry orc ha s a c lub
with a unique a tta c k le ve l. Orc s a re be st ignore d, unle ss the re a re orc s with a n unusua lly powe rful c lub a tta c k tha t you wa nt to c ull
from the he rd a t the be ginning of a ba ttle .
T o c re a te the orc , we de fine a n orc da ta type with defstruct. He re , we will use a nothe r a dva nc e d fe a ture of defstruct to
de c la re tha t the orc inc lude s a ll the fie lds of monster.
By inc luding the fie lds from our monster type in our orc type , the orc will be a ble to inhe rit the fie lds tha t a pply to a ll
m onste rs, suc h a s the health fie ld. T his is sim ila r to wha t you c a n a c c om plish in popula r la ngua ge s suc h a s C++ or Ja va by de fining
a ge ne ric c la ss a nd the n c re a ting othe r, m ore spe c ia liz e d, c la sse s tha t inhe rit from this ge ne ric c la ss.
Onc e the struc ture is de c la re d, we push the make-orc func tion (a utom a tic a lly ge ne ra te d by the defstruct) onto our list of
*monster-builders*:
(defstruct (orc (:include monster)) (club-level (randval 8)))
(push #'make-orc *monster-builders*)
Note

Notic e how powe rful this a pproa c h is. W e c a n c re a te a s m a ny ne w m onste r type s a s we wa nt, ye t we ’ll ne ve r ne e d to c ha nge our
ba sic Orc Ba ttle c ode . T his is possible only in la ngua ge s like L isp, whic h a re dyna m ic a lly type d a nd support func tions a s first-c la ss
va lue s. In sta tic a lly type d progra m m ing la ngua ge s, the m a in Orc Ba ttle c ode would ne e d som e ha rdwire d wa y of c a lling the
c onstruc tor for e a c h ne w type of m onste r. W ith first-c la ss func tions, we don’t ne e d to worry a bout this.

Now le t’s spe c ia liz e our monster-show a nd monster-attack func tions for orc s. Notic e the se a re de fine d in the sa m e wa y a s the
e a rlie r ve rsions of the se func tions, e xc e pt tha t we e xplic itly de c la re tha t the se func tions a re orc -spe c ific in the a rgum e nt lists:
(defmethod monster-show ((m orc))
(princ "A wicked orc with a level ")

(princ (orc-club-level m))


(princ " club"))
(defmethod monster-attack ((m orc))

(let ((x (randval (orc-club-level m))))


(princ "An orc swings his club at you and knocks off ")
(princ x)
(princ " of your health points. ")
(decf *player-health* x)))
T he one unique thing a bout our orc type is tha t e a c h orc ha s a n orc-club-level fie ld. T he se orc -spe c ific ve rsions of monster-

show a nd monster-attack ta ke this fie ld into a c c ount. In the monster-show func tion, we displa y this c lub le ve l , so tha t the
pla ye r c a n ga uge the da nge r pose d by e a c h orc .

In the monster-attack func tion, we use the le ve l of the c lub to de c ide how ba dly the pla ye r is hit by the c lub .

The M alic ious H ydr a

T he hydra is a ve ry na sty e ne m y. It will a tta c k you with its m a ny he a ds, whic h you’ll ne e d to c hop off to de fe a t it. T he hydra ’s
spe c ia l powe r is tha t it c a n grow a ne w he a d during e a c h round of ba ttle , whic h m e a ns you wa nt to de fe a t it a s e a rly a s possible .
(defstruct (hydra (:include monster)))
(push #'make-hydra *monster-builders*)
(defmethod monster-show ((m hydra))
(princ "A malicious hydra with ")

(princ (monster-health m))


(princ " heads."))
(defmethod monster-hit ((m hydra) x)

(decf (monster-health m) x)
(if (monster-dead m)
(princ "The corpse of the fully decapitated and decapacitated
hydra falls to the floor!")

(progn (princ "You lop off ")


(princ x)
(princ " of the hydra's heads! "))))

(defmethod monster-attack ((m hydra))


(let ((x (randval (ash (monster-health m) −1))))
(princ "A hydra attacks you with ")
(princ x)
(princ " of its heads! It also grows back one more head! ")

(incf (monster-health m))


(decf *player-health* x)))
T he c ode for ha ndling the hydra is sim ila r to the c ode for ha ndling the orc . T he m a in diffe re nc e is tha t a hydra ’s he a lth a lso a c ts
a s a sta nd-in for the num be r of hydra he a ds. In othe r words, a hydra with thre e he a lth points will ha ve thre e he a ds, a s we ll.
T he re fore , whe n we write our hydra -spe c ific monster-show func tion, we use the m onste r’s he a lth to print a pre tty m e ssa ge a bout

the num be r of he a ds on the hydra .


Anothe r diffe re nc e be twe e n the orc a nd the hydra is tha t a n orc doe sn’t do a nything pa rtic ula rly inte re sting whe n it is hit by the
pla ye r. Be c a use of this, we didn’t ne e d to write a c ustom monster-hit func tion for the orc ; the orc sim ply use d the ge ne ric
monster-hit func tion we c re a te d for a ge ne ric monster.
A hydra , on the othe r ha nd, doe s som e thing inte re sting whe n it is hit: It lose s he a ds! W e the re fore c re a te a hydra -spe c ific

monster-hit func tion, whe re he a ds a re re m ove d with e ve ry blow, whic h a m ounts to lowe ring the hydra ’s he a lth . Also, we c a n

now print a dra m a tic m e ssa ge a bout how the knight loppe d off sa id he a ds .
T he hydra ’s monster-attack func tion is a ga in sim ila r to tha t for the orc . T he one inte re sting diffe re nc e is tha t we inc re m e nt the

he a lth with e ve ry a tta c k, so tha t the hydra grows a ne w he a d e ve ry turn .

The Slimy Slime M old

T he slim e m old is a unique m onste r. W he n it a tta c ks you, it will wra p itse lf a round your le gs a nd im m obiliz e you, le tting the
othe r ba d guys finish you off. It c a n a lso squirt goo in your fa c e . You m ust think quic kly in ba ttle to de c ide if it’s be tte r to finish
the slim e off e a rly in orde r to m a inta in your a gility, or ignore it to foc us on m ore vic ious foe s first. (Re m e m be r tha t by lowe ring
your a gility, the slim e m old will de c re a se the num be r of a tta c ks you c a n de live r in la te r rounds of ba ttle . )

(defstruct (slime-mold (:include monster)) (sliminess (randval 5)))


(push #'make-slime-mold *monster-builders*)
(defmethod monster-show ((m slime-mold))
(princ "A slime mold with a sliminess of ")
(princ (slime-mold-sliminess m)))
(defmethod monster-attack ((m slime-mold))

(let ((x (randval (slime-mold-sliminess m))))


(princ "A slime mold wraps around your legs and decreases your agility by ")
(princ x)
(princ "! ")

(decf *player-agility* x)

(when (zerop (random 2))


(princ "It also squirts in your face, taking away a health point! ")

(decf *player-health*))))
T he monster-attack func tion for the slim e m old m ust do som e spe c ia l things, whic h a llow it to im m obiliz e the pla ye r. First, it
use s the slim e m old’s slim ine ss (whic h is ge ne ra te d whe n e a c h slim e m old is built) to ge ne ra te a ra ndom a tta c k a ga inst the pla ye r,
store d in the va ria ble x . Unlike m ost othe r a tta c ks in the ga m e , this slim e m old a tta c k a ffe c ts the a gility of pla ye rs, ra the r

tha n the ir he a lth .


Howe ve r, it would be pointle ss if the slim e m old c ouldn’t a tta c k the pla ye r’s he a lth a t le a st a little , or the ba ttle c ould e nd
a wkwa rdly, with the pla ye r a nd slim e m old froz e n in pla c e for a ll tim e . T he re fore , the slim e m old a lso ha s a supe rwim py squirt

a tta c k tha t ha ppe ns during ha lf of a ll a tta c ks , but subtra c ts only a single he a lth point from the pla ye r .

The Cunning Br igand

T he briga nd is the sm a rte st of a ll your foe s. He c a n use his whip or slingshot a nd will try to ne utra liz e your be st a sse ts. His
a tta c ks a re not powe rful, but the y a re a c onsiste nt two points for e ve ry round.
(defstruct (brigand (:include monster)))
(push #'make-brigand *monster-builders*)
(defmethod monster-attack ((m brigand))

(let ((x (max *player-health* *player-agility* *player-strength*)))

(cond ((= x *player-health*)


(princ "A brigand hits you with his slingshot,
taking off 2 health points! ")
(decf *player-health* 2))

((= x *player-agility*)
(princ "A brigand catches your leg with his whip,
taking off 2 agility points! ")
(decf *player-agility* 2))

((= x *player-strength*)
(princ "A brigand cuts your arm with his whip,
taking off 2 strength points! ")
(decf *player-strength* 2)))))
T he first thing the wily briga nd doe s whe n pe rform ing a n a tta c k is to look a t the pla ye r’s he a lth, a gility, a nd stre ngth, a nd

c hoose the max of those thre e a s the foc us of his a tta c k . If se ve ra l of the a ttribute s a re e qua lly la rge , the briga nd will c hoose
he a lth ove r a gility a nd a gility ove r stre ngth a s the foc us of a tta c k. If he a lth is the la rge st va lue , the pla ye r is hit with a slingshot

. If a gility is the la rge st, the briga nd will whip the pla ye r’s le g . If stre ngth is the la rge st, the briga nd will whip the

pla ye r’s a rm .
W e ha ve now c om ple te ly de fine d a ll of our m onste rs for our ga m e !
To Battle !
T o sta rt the ga m e , c a ll orc-battle from the RE PL :
> (orc-battle)
You are a valiant knight with a health of 30, an agility of 30, and a strength of 30
Your foes:
1. (Health=10) A wicked orc with a level 5 club
2. (Health=3) A malicious hydra with 3 heads.
3. (Health=9) A fierce BRIGAND
4. (Health=3) A malicious hydra with 3 heads.
5. (Health=3) A wicked orc with a level 2 club
6. (Health=7) A malicious hydra with 7 heads.
7. (Health=6) A slime mold with a sliminess of 2
8. (Health=5) A wicked orc with a level 2 club
9. (Health=9) A fierce BRIGAND
10. (Health=2) A wicked orc with a level 6 club
11. (Health=7) A wicked orc with a level 4 club
12. (Health=8) A slime mold with a sliminess of 2
T ha t hydra with se ve n he a ds looks pre tty gna rly— le t’s finish it off first with a sta b:
Attack style: [s]tab [d]ouble swing [r]oundhouse:s
Monster #:6
The corpse of the fully decapitated and decapacitated hydra falls to the floor!
Your foes:
1. (Health=10) A wicked orc with a level 5 club
2. (Health=3) A malicious hydra with 3 heads.
3. (Health=9) A fierce BRIGAND
4. (Health=3) A malicious hydra with 3 heads.
5. (Health=3) A wicked orc with a level 2 club
6. **dead**
7. (Health=6) A slime mold with a sliminess of 2
8. (Health=5) A wicked orc with a level 2 club
9. (Health=9) A fierce BRIGAND
10. (Health=2) A wicked orc with a level 6 club
11. (Health=7) A wicked orc with a level 4 club
12. (Health=8) A slime mold with a sliminess of 2
No othe r ba d guy re a lly sta nds out, so we ’ll try a roundhouse to bring down som e of those he a lth num be rs ove ra ll:
Attack style: [s]tab [d]ouble swing [r]oundhouse:r
You hit the SLIME-MOLD,
knocking off 1 health points! You hit the SLIME-MOLD, knocking off 1 health points!
You hit the ORC, knocking off 1 health points! You lop off 1 of the hydra's heads!
You lop off 1 of the hydra's heads! You lop off 1 of the hydra's heads! You hit the
ORC, knocking off 1 health points! The corpse of the fully decapitated and decapaci
tated hydra falls to the floor! You hit the ORC, knocking off 1 health points! You hit
the ORC, knocking off 1 health points! You hit the ORC, knocking off 1 health points!
Your foes:
1. (Health=9) A wicked orc with a level 5 club
2. (Health=2) A malicious hydra with 2 heads.
3. (Health=9) A fierce BRIGAND
4. **dead**
5. (Health=2) A wicked orc with a level 2 club
6. **dead**
7. (Health=4) A slime mold with a sliminess of 2
8. (Health=3) A wicked orc with a level 2 club
9. (Health=9) A fierce BRIGAND
10. (Health=2) A wicked orc with a level 6 club
11. (Health=6) A wicked orc with a level 4 club
12. (Health=8) A slime mold with a sliminess of 2
Gre a t! T ha t e ve n kille d one of the we a ke r e ne m ie s. Now, with full a gility, we ha ve thre e a tta c ks pe r round. T his m e a ns we
should use our la st a tta c k to stra te gic a lly ta ke out som e of the m ore powe rful ba d guys. L e t’s use the double swing:
Attack style: [s]tab [d]ouble swing [r]oundhouse:d
Your double swing has a strength of 3
Monster #:8
You killed the ORC!
Monster #:10
You killed the ORC!
An orc swings his club at you and knocks off 5 of your health points. A hydra
attacks you with 1 of its heads! It also grows back one more head! A
brigand catches your leg with his whip, taking off 2 agility points! An orc
swings his club at you and knocks off 1 of your health points. A slime mold wraps
around your legs and decreases your agility by 2! It also squirts in your face,
taking away a health point! A brigand cuts your arm with his whip, taking off 2
strength points! An orc swings his club at you and knocks off 1 of your health
points. A slime mold wraps around your legs and decreases your agility by 1!
You are a valiant knight with a health of 21, an agility of 25, and a strength of 28
Your foes:
1. (Health=9) A wicked orc with a level 5 club
2. (Health=3) A malicious hydra with 3 heads.
3. (Health=9) A fierce BRIGAND
4. **dead**
5. (Health=2) A wicked orc with a level 2 club
6. **dead**
7. (Health=4) A slime mold with a sliminess of 2
8. **dead**
9. (Health=9) A fierce BRIGAND
10. **dead**
11. (Health=6) A wicked orc with a level 4 club
12. (Health=8) A slime mold with a sliminess of 2
T he y got us pre tty good, but we still ha ve ple nty of fight le ft. T his ba ttle isn’t ove r ye t!
As you c a n se e , c a re ful stra te gy is ne e de d if you wa nt to survive Orc Ba ttle . I hope you e njoy this ne w ga m e !
W hat You've Le ar ne d
In this c ha pte r, we disc usse d the m ore a dva nc e d da ta struc ture s in Com m on L isp. W e the n use d this to c re a te a m onste r-fighting
ga m e . Along the wa y, you le a rne d the following:
Arra ys a re sim ila r to lists, but a llow you to a c c e ss a n ite m a t a spe c ific offse t m ore e ffic ie ntly.
Ha sh ta ble s a re sim ila r to a lists, but le t you look up the va lue a ssoc ia te d with a ke y m ore e ffic ie ntly.
Using a rra ys a nd ha sh ta ble s in the a ppropria te pla c e s will usua lly m a ke your c ode m uc h fa ste r.
T he only true wa y to te ll if c ha nging a da ta struc ture or a lgorithm m a ke s your progra m fa ste r is to tim e your c ode with the
time c om m a nd.
Com m on L isp ha s ge ne ric func tions tha t c a n be use d a ga inst m ultiple da ta type s. T he m ost use ful of the se a re se que nc e
func tions tha t c a n tra nspa re ntly ha ndle lists, a rra ys, a nd strings.
You c a n c re a te obje c ts with prope rtie s in list using the defstruct c om m a nd.
P ar t III. Lisp is H ac king
loop and for mat: The Se e dy Unde r be lly of Lisp
Pre viously, we looke d a t the c ore of the Com m on L isp la ngua ge a nd a dm ire d its suc c inc tne ss a nd e le ga nc e . Howe ve r, the re a re
a lso som e da rke r, se e die r pa rts of L isp built a round this c ore tha t ha ve a c e rta in c ha rm of the ir own. T he y m a y la c k the be a uty of
the L isp c ore , but the y e a sily m a ke up for it with the ir powe r. T he se pa rts of the la ngua ge a re a re a l de light for a ny budding L isp
ha c ke r.
T he e xte nsions we ’ll c ove r in this se c tion, loop a nd format, pla c e a strong e m pha sis on powe r ove r m a the m a tic a l e le ga nc e . T his
ha s le d to oc c a siona l c ontrove rsy a m ong L isp progra m m e rs, som e of whom que stion whe the r the powe r provide d by the se c om m a nds
is worth the tra de -off in e le ga nc e . T he se progra m m e rs be lie ve tha t loop a nd format should be a voide d whe n writing a ny se rious
c ode .
But the re is one gre a t re a son to le a rn a nd use the se c om m a nds: T he y e m body the fle xibility a nd e xte nsibility of L isp. Sinc e L isp
is (a rgua bly) the m ost fle xible progra m m ing la ngua ge a va ila ble , ha c ke rs ha ve be e n e xte nding it with thousa nds of the ir own ha c ks
for de c a de s. loop a nd format, whic h a re a m ong the m ost suc c e ssful of the se e xte nsions, ha d to be re a lly spe c ta c ula r to survive in
the Da rwinia n ba ttle fie ld.
Chapte r 10. Looping with the loop Command
T h e loop a nd format c om m a nds a re powe rful a nd ha c ke r-frie ndly. T hough m ost of the func tiona lity the y offe r is a va ila ble
e lse whe re in the L isp la ngua ge , the se highly spe c ia liz e d c om m a nds a re worth le a rning if you like te rse c ode . W e ’ll look a t loop in
this c ha pte r. T he ne xt c ha pte r c ove rs format.
The loop M ac r o
Any type of looping you would e ve r wa nt to do inside a c om pute r progra m c a n be a c c om plishe d with the loop m a c ro. He re ’s a
sim ple e xa m ple :
> (loop for i
below 5
sum i)
10
T his c ode a dds toge the r the na tura l num be rs be low 5, like this:

0 + 1 + 2 + 3 + 4 = 10
You c a n se e tha t this loop c om m a nd doe sn’t work in the wa y a prope r L isp c om m a nd should. First of a ll, it’s pa re nthe tic a lly
c ha lle nge d. Ne ve r be fore ha ve we ha d se ve n toke ns in a row without pa re nthe se s!

W ha t m a ke s it e ve n le ss L ispy is tha t som e of the se e xtra toke ns (for, below, a nd sum) a ppe a r to ha ve spe c ia l m e a nings. Re c a ll
from Cha pte r 3 tha t the first toke n in a form (the one im m e dia te ly a fte r the ope ning pa re nthe sis) is typic a lly wha t de c ide s the
ba sic be ha vior of the c ode , while the re st of the form c onta ins pa ra m e te rs. W ithin the loop m a c ro, se ve ra l of the se “ m a gic toke ns”
funda m e nta lly a ffe c t the loop’s be ha vior. He re ’s wha t the y m e a n:

for a llows you to de c la re a va ria ble (in this c a se , na m e d i) tha t ite ra te s through a ra nge of va lue s. By de fa ult, it
will c ount through the inte ge rs sta rting a t z e ro.
below te lls the for c onstruc t to ha lt whe n it re a c he s the spe c ifie d va lue (in this c a se , 5), e xc luding the va lue
itse lf.
sum a dds toge the r a ll va lue s of a give n e xpre ssion (in this c a se , the e xpre ssion is just i) a nd m a ke s the loop re turn
tha t num be r.
Some loop Tr ic ks
T he loop m a c ro ha s a ve rita ble c ornuc opia of spe c ia l toke ns tha t m a ke just a bout a ny kind of be ha vior possible . L e t’s look a t
som e of the possibilitie s.

Counting fr om a Star ting P oint to an Ending P oint

By using from a nd to c la use s, you c a n m a ke the for c onstruc t c ount through a ny spe c ific ra nge of inte ge rs:
> (loop for i
from 5
to 10
sum i)
45

Ite r ating Thr ough Value s in a List

In the following e xa m ple , we ite ra te through va lue s in a list using the in toke n:
> (loop for i
in '(100 20 3)
sum i)
123

doing Stuff in a Loop

T he do toke n ta ke s a n a rbitra ry e xpre ssion a nd e xe c ute s it inside the loop:


> (loop for i
below 5
do (print i))
0
1
2
3
4

Doing Stuff Unde r Ce r tain Conditions

T he when toke n le ts you run the following pa rt of the loop only a s ne e de d:


> (loop for i
below 10
when (oddp i)
sum i)
25
Notic e tha t only the sum of the odd num be rs is re turne d.

Br e aking out of a Loop Ear ly

T he following loop use s se ve ra l ne w tric ks:


> (loop for i
from 0
do (print i)
when (= i 5)
return 'falafel)
0
1
2
3
4
5
FALAFEL
Notic e tha t the re ’s nothing in the for pa rt of the loop tha t te lls it to stop c ounting num be rs— it goe s from z e ro off to infinity.
Howe ve r, onc e we re a c h 5, the when c la use trigge rs the loop to im m e dia te ly re turn the va lue 'falafel.

Colle c ting a List of Value s


T he collect c la use le ts you re turn m ore tha n one ite m from the loop, in the form of a list. T his c om m a nd is use ful whe n you
ne e d to m odify e a c h ite m in a list, a s in the following e xa m ple :
> (loop for i
in '(2 3 4 5 6)
collect (* i i))
(4 9 16 25 36)

Using M ultiple for Clause s

It’s possible for a loop m a c ro to ha ve m ore tha n one for c la use . Conside r the following e xa m ple :
(loop for x below 10
for y below 10
collect (+ x y))
How m a ny num be rs do you think will be re turne d a s a re sult? T he re a re two possibilitie s: E ithe r it inc re m e nts x a nd y a t the
sa m e tim e a nd re turns a list of 10 ite m s, or it ite ra te s x a nd y in a ne ste d fa shion a nd re turns 100 num be rs. T he a nswe r is the
form e r:
> (loop for x below 10
for y below 10
collect (+ x y))
(0 2 4 6 8 10 12 14 16 18)
As you c a n se e , both num be rs inc re m e nte d a t the sa m e tim e be twe e n 0 a nd 9.
If the re a re m ultiple for c la use s in a Com m on L isp loop, e a c h one will be c he c ke d, a nd the loop will stop whe n a ny one of the
c la use s runs out of va lue s. T his m e a ns tha t for c la use s do not loop inde pe nde ntly a c ross m ultiple looping va ria ble s, so if you loop
on two ra nge s of 10 va lue s e a c h, it will still just loop 10 tim e s.
Howe ve r, som e tim e s you wa nt to ge ne ra te the Carte sian produc t be twe e n m ultiple ra nge s. In othe r words, you wa nt a loop to run
onc e for e ve ry possible c om bina tion of two or m ore ra nge s. T o a c c om plish this, you ne e d to use ne ste d loops for x a nd y:
> (loop for x below 10
collect (loop for y below 10
collect (+ x y)))
((0 1 2 3 4 5 6 7 8 9) (1 2 3 4 5 6 7 8 9 10) (2 3 4 5 6 7 8 9 10 11)
(3 4 5 6 7 8 9 10 11 12) (4 5 6 7 8 9 10 11 12 13) (5 6 7 8 9 10 11 12 13 14)
(6 7 8 9 10 11 12 13 14 15) (7 8 9 10 11 12 13 14 15 16)
(8 9 10 11 12 13 14 15 16 17) (9 10 11 12 13 14 15 16 17 18))
In this c a se , we ’ve c re a te d 10 lists of 10 ite m s e a c h, looping for a tota l of 100 ite m s.
Also, notic e tha t using a for va ria ble sta rting a t z e ro, suc h a s the i va ria ble in the following e xa m ple , provide s a c le a n wa y to
tra c k the inde x num be r of ite m s in a list:
> (loop for i
from 0
for day
in '(monday tuesday wednesday thursday friday saturday sunday)
collect (cons i day))
((0 . MONDAY) (1 . TUESDAY) (2 . WEDNESDAY)
(3 . THURSDAY) (4 . FRIDAY) (5 . SATURDAY) (6 . SUNDAY))
You m ight think we ’ve c ove re d e ve ry c onc e iva ble va ria tion of looping a t this point. If so, you a re gra ve ly m ista ke n. Be hold!
T he Pe riodic T a ble of the L oop Ma c ro!
Eve r ything You Eve r W ante d to K now About loop
T he individua l e xa m ple s we ’ve disc usse d so fa r give only the brie fe st hint of the full c a pa bilitie s of loop. But fe a r not! You now
ha ve the world’s first a nd only Pe riodic T a ble of the L oop Ma c ro. Just ta pe it to your m onitor, glue it to your wa lle t, or la se r-e tc h
it dire c tly into your re tina , a nd you’ll be gua ra nte e d to re a c h loop profic ie nc y in no tim e !
Alm ost e ve ry le ga l c om m a nd tha t c a n be use d in a loop m a c ro is c ove re d by the pe riodic ta ble . It shows how to m a nipula te ha sh
ta ble s a nd a rra ys, a nd pe rform spe c ia l looping ope ra tions. E a c h squa re in the pe riodic ta ble c onta ins a n e xa m ple . If you run the
e xa m ple , you should be a ble to figure out the be ha vior of the give n c om m a nd.
Using loop to Evolve !

L e t’s c re a te a nothe r ga m e , m a king full use of loop. But this won’t be a ga m e tha t we pla y. Inste a d, it will be a ga m e world tha t
e volve s a s we wa tc h it! W e ’re going to c re a te a n e nvironm e nt of ste ppe s a nd jungle s, fille d with a nim a ls running a round, fora ging,
e a ting, a nd re produc ing. And a fte r a fe w m illion units of tim e , we ’ll se e tha t the y’ve e volve d into diffe re nt spe c ie s!

Note

T his e xa m ple is a da pte d from A. K. De wdne y’s a rtic le “ Sim ula te d e volution: whe re in bugs le a rn to hunt ba c te ria , ” in the
“ Com pute r Re c re a tions” c olum n of Sc ie ntific A me ric an (Ma y 1989: 138-141).

Our ga m e world is e xtre m e ly sim ple . It c onsists of a sim ple re c ta ngula r pla ne , with e dge s tha t wra p a round to the opposite side .
(Ma the m a tic a lly spe a king, it ha s a toroida l topology. ) Most of this world is c ove re d in ste ppe s, m e a ning tha t ve ry fe w pla nts grow
for the a nim a ls to e a t. In the c e nte r of the world is a sm a ll jungle , whe re pla nts grow m uc h fa ste r. Our a nim a ls, who a re
he rbivore s, will fora ge this world in se a rc h for food.
L e t’s c re a te som e va ria ble s de sc ribing the e xte nt of our world:
(defparameter *width* 100)
(defparameter *height* 30)
(defparameter *jungle* '(45 10 10 10))
(defparameter *plant-energy* 80)
W e ’re giving the world a width of 100 units a nd a he ight of 30 units. Using the se dim e nsions should m a ke it e a sy to displa y the
world in our L isp RE PL . T he *jungle* list de fine s the re c ta ngle in the world m a p tha t c onta ins the jungle . T he first two num be rs
in the list a re the x-a nd y-c oordina te s of the jungle ’s top-le ft c orne r, a nd the la st two num be rs a re its width a nd he ight. Fina lly, we
give the a m ount of e ne rgy c onta ine d in e a c h pla nt, whic h is se t to 80. T his m e a ns tha t if a n a nim a l finds a pla nt, it will ga in 80
da ys’ worth of food by e a ting it.

Note
If your te rm ina l window isn’t la rge e nough to displa y the e ntire world, c ha nge the va lue s of the *width* a nd *height* va ria ble s.
Se t the *width* va ria ble to the width of your te rm ina l window m inus two, a nd the *height* va ria ble to the he ight of your te rm ina l
window m inus one .
G r owing P lants in O ur W or ld
As you m ight im a gine , sim ula ting e volution on a c om pute r is a slow proc e ss. In orde r to se e the c re a ture s e volve , we ne e d to
sim ula te la rge stre tc he s of tim e , whic h m e a ns we ’ll wa nt our c ode for this proje c t to be ve ry e ffic ie nt. As a nim a ls wa nde r a round
our world, the y will ne e d to be a ble to c he c k if the re is a pla nt a t a give n x, y loc a tion. T he m ost e ffic ie nt wa y to e na ble this is to
store a ll of our pla nts in a ha sh ta ble , inde xe d ba se d on e a c h pla nt’s x-a nd y-c oordina te s.
(defparameter *plants* (make-hash-table :test #'equal))
By de fa ult, a Com m on L isp ha sh ta ble use s eq whe n te sting for the e qua lity of ke ys. For this ha sh ta ble , howe ve r, we ’re de fining
:test to use equal inste a d of eq, whic h will le t us use c ons pa irs of x-a nd y-c oordina te s a s ke ys. If you re m e m be r our rule of thum b
for c he c king e qua lity, c ons pa irs should be c om pa re d using equal. If we didn’t m a ke this c ha nge , e ve ry c he c k for a ke y would fa il,
sinc e two diffe re nt c ons c e lls, e ve n with the sa m e c onte nts, te st a s be ing diffe re nt whe n using eq.
Pla nts will grow ra ndom ly a c ross the world, though a highe r c onc e ntra tion of pla nts will grow in the jungle a re a tha n in the
ste ppe s. L e t’s write som e func tions to grow ne w pla nts:
(defun random-plant (left top width height)

(let ((pos (cons (+ left (random width)) (+ top (random height)))))

(setf (gethash pos *plants*) t)))


(defun add-plants ()

(apply #'random-plant *jungle*)

(random-plant 0 0 *width* *height*))


T he random-plant func tion c re a te s a ne w pla nt within a spe c ifie d re gion of the world. It use s the random func tion to c onstruc t a

ra ndom loc a tion a nd store s it in the loc a l va ria ble pos . T he n it use s setf to indic a te the e xiste nc e of the pla nt within the

ha sh ta ble . T he only ite m a c tua lly store d in the ha sh ta ble is t. For this *plants* ta ble , the ke ys of the ta ble (the x, y
position of e a c h pla nt) a re a c tua lly m ore tha n the va lue s store d in the ta ble .
It m a y se e m a bit we ird to go through the trouble of c re a ting a ha sh ta ble to do nothing m ore tha n store t in e ve ry slot.
Howe ve r, Com m on L isp doe s not, by de fa ult, ha ve a da ta struc ture de signe d for holding m a the m a tic a l se ts. In our ga m e , we wa nt
to ke e p tra c k of the se t of a ll world positions tha t ha ve a pla nt in the m . It turns out tha t ha sh ta ble s a re a pe rfe c tly a c c e pta ble wa y
of e xpre ssing this. You sim ply use e a c h se t ite m a s a ke y a nd store t a s the va lue . Inde e d, doing this is a bit of a ha c k, but it is a
re a sona bly sim ple a nd e ffic ie nt ha c k. (Othe r L isp dia le c ts, suc h a s Clojure , ha ve a se t da ta struc ture built right into the m , m a king
this ha c k unne c e ssa ry. )

E ve ry da y our sim ula tion runs, the add-plants func tion will c re a te two ne w pla nts: one in the jungle a nd one in the re st

of the m a p . Be c a use the jungle is so sm a ll, it will ha ve de nse ve ge ta tion c om pa re d to the re st of the world.
Cr e ating Animals

T he pla nts in our world a re ve ry sim ple , but the a nim a ls a re a bit m ore c om plic a te d. Be c a use of this, we ’ll ne e d to de fine a
struc ture tha t store s the prope rtie s of e a c h a nim a l in our ga m e :
(defstruct animal x y energy dir genes)
L e t’s ta ke a look a t e a c h of the se fie lds in de ta il.

Anatomy of an Animal
W e ne e d to tra c k se ve ra l prope rtie s for e a c h a nim a l. First, we ne e d to know its x-a nd y-c oordina te s. T his indic a te s whe re the
a nim a l is loc a te d on the world m a p.
Ne xt, we ne e d to know how m uc h energy a n a nim a l ha s. T his is a Da rwinia n ga m e of surviva l, so if a n a nim a l c a n’t fora ge
e nough food, it will sta rve a nd die . T he e ne rgy fie ld tra c ks how m a ny da ys of e ne rgy a n a nim a l ha s re m a ining. It is c ruc ia l tha t a n
a nim a l find m ore food be fore its e ne rgy supply is e xha uste d.
W e a lso ne e d to tra c k whic h dire c tion the a nim a l is fa c ing. T his is im porta nt be c a use a n a nim a l will wa lk to a ne ighboring
squa re in the world m a p e a c h da y. T he dir fie ld will spe c ify the dire c tion of the a nim a l’s ne xt x, y position a s a num be r from 0 to
7:
For e xa m ple , a n orie nta tion of 0 would c a use the a nim a l to m ove up a nd to the le ft by the ne xt da y.
Fina lly, we ne e d to tra c k the a nim a l’s genes. E a c h a nim a l ha s e xa c tly e ight ge ne s, c onsisting of positive inte ge rs. T he se
inte ge rs re pre se nt e ight “ slots, ” whic h e nc irc le the a nim a l a s follows:

E ve ry da y, a n a nim a l will de c ide whe the r to c ontinue fa c ing the sa m e dire c tion a s the da y be fore or to turn a nd fa c e a ne w
dire c tion. It will do this by c onsulting the se e ight slots a nd ra ndom ly c hoosing a ne w dire c tion. T he c ha nc e of a ge ne be ing c hose n
will be proportiona l to the num be r store d in the ge ne slot.
For e xa m ple , a n a nim a l m ight ha ve the following ge ne s:
(1 1 10 1 1 1 1 1)
L e t’s re pre se nt the se ge ne s a s a ta ble , showing e a c h slot num be r a nd how la rge of a va lue is store d in it:
In this e xa m ple , a n a nim a l ha s a la rge num be r (10) store d in slot 2. L ooking a t our pic ture of the e ight slots a round the a nim a l,
you c a n se e tha t slot 2 points to the right. T he re fore , this a nim a l will m a ke a lot of right-ha nd turns a nd run in a c irc le . Of c ourse ,
sinc e the othe r slots still c onta in va lue s la rge r tha n z e ro, the a nim a l will oc c a siona lly m ove in a nothe r dire c tion.
L e t’s c re a te a n *animals* va ria ble , popula te d with a single sta rting a nim a l. You c a n think of this a nim a l a s “ Ada m ” (or “ E ve ” ,
de pe nding on wha t ge nde r you pre fe r for our a se xua l a nim a ls).
(defparameter *animals*
(list (make-animal :x (ash *width* −1)
:y (ash *height* −1)
:energy 1000
:dir 0
:genes (loop repeat 8
collecting (1+ (random 10))))))
W e m a ke the a nim a l’s sta rting point the c e nte r of the world by se tting the x a nd y positions to ha lf of the m a p’s width a nd
he ight, re spe c tive ly. W e se t its initia l e ne rgy to 1000, sinc e it ha sn’t e volve d m uc h ye t a nd we wa nt it to ha ve a fighting c ha nc e
a t surviva l. It sta rts off fa c ing the uppe r le ft, with its dir fie ld se t to 0. For its ge ne s, we just use ra ndom num be rs.
Note tha t unlike the *plants* struc ture , whic h wa s a ha sh ta ble , the *animals* struc ture is just a pla in list (c urre ntly c onta ining
only a single m e m be r). T his is be c a use , for the c ore of our sim ula tion, we ne ve r ne e d to se a rc h our list of a nim a ls. Inste a d, we ’ll
just be tra ve rsing *animals* onc e e ve ry sim ula te d da y, to le t our c ritte rs do the ir da ily a c tivitie s. L ists a lre a dy support e ffic ie nt
line a r tra ve rsa ls, so using a nothe r, m ore c om ple x da ta struc ture (suc h a s a ta ble ) would ha ve no signific a nt e ffe c t on the
pe rform a nc e of our sim ula tion.

H andling Animal M otion


T he move func tion a c c e pts a n a nim a l a s a n a rgum e nt a nd m ove s it, orthogona lly or dia gona lly, ba se d on the dire c tion grid we
ha ve de sc ribe d:
(defun move (animal)
(let ((dir (animal-dir animal))

(x (animal-x animal))

(y (animaly animal)))

(setf (animal-x animal) (mod (+ x

(cond ((and (>= dir 2) (< dir 5)) 1)

((or (= dir 1) (= dir 5)) 0)

(t −1))
*width*)
*width*))

(setf (animaly animal) (mod (+ y


(cond ((and (>= dir 0) (< dir 3)) −1)
((and (>= dir 4) (< dir 7)) 1)
(t 0))
*height*)
*height*))
(decf (animal-energy animal))))
T h e move func tion m odifie s the x a nd y fie lds, using the animal-x a nd animaly a c c e ssors. As we ’ve disc usse d, the se a re
a utom a tic a lly ge ne ra te d through the defstruct m a c ro, ba se d on the fie ld na m e s. At the top of this func tion, we use the a c c e ssors

to re trie ve the x-a nd y-c oordina te s for the a nim a l . T he n we use the sa m e a c c e ssors to se t the sa m e va lue s, with the a id

of setf .

T o c a lc ula te the ne w x-c oordina te , we use a cond c om m a nd to first c he c k if the dire c tion is 2, 3, or 4 . T he se a re the
dire c tions the a nim a l m a y fa c e tha t point e a st in the world, so we wa nt to a dd one to the x-c oordina te . If the dire c tion inste a d is 1

or 5, it m e a ns the a nim a l is fa c ing dire c tly north or south . In those c a se s, the x-c oordina te shouldn’t be c ha nge d. In a ll othe r
c a se s, the a nim a l is fa c ing we st a nd we ne e d to subtra c t one . T he y-c oordina te is a djuste d in a n a na logous wa y .
Sinc e the world ne e ds to wra p a round a t the e dge s, we do som e e xtra m a th using the mod (re m a inde r) func tion to c a lc ula te the

m odulus of the c oordina te s a nd e na ble wra pping a c ross the m a p . If a n a nim a l would ha ve e nde d up with a n x-
c oordina te of *width*, the mod func tion puts it ba c k to z e ro, a nd it doe s the sa m e for the y-c oordina te a nd *height*. So, for
e xa m ple , if our func tion m a ke s the a nim a l m ove e a st until x e qua ls 100, this will m e a n tha t (mod 100 *width*) e qua ls z e ro, a nd
the a nim a l will ha ve wra ppe d a round ba c k to the fa r we st side of the ga m e world.
T he fina l thing the move func tion ne e ds to do is de c re a se the a m ount of e ne rgy the a nim a l posse sse s by one . Motion, a fte r a ll,
re quire s e ne rgy.

H andling Animal Tur ning

Ne xt, we ’ll write the turn func tion. T his func tion will use the a nim a l’s ge ne s to de c ide if a nd how m uc h it will turn on a give n
da y.
(defun turn (animal)

(let ((x (random (apply #'+ (animal-genes animal)))))

(labels ((angle (genes x)


(let ((xnu (- x (car genes))))

(if (< xnu 0)


0
(1+ (angle (cdr genes) xnu))))))
(setf (animal-dir animal)

(mod (+ (animal-dir animal) (angle (animal-genes animal) x))


8)))))
T his func tion ne e ds to m a ke sure tha t the a m ount the a nim a l turns is proportiona l to the ge ne num be r in the give n slot. It doe s

this by first sum m ing the a m ount of a ll ge ne s, a nd the n pic king a ra ndom num be r within tha t sum . Afte r tha t, it use s a

re c ursive func tion na m e d angle , whic h tra ve rse s the ge ne s a nd finds the ge ne tha t c orre sponds to the c hose n num be r, ba se d on
the re spe c tive c ontributions of e a c h ge ne to the sum . It subtra c ts the running c ount in the a rgum e nt x from the num be r store d a t the

c urre nt ge ne . If the running c ount ha s hit or e xc e e de d z e ro, the func tion ha s re a c he d the c hose n num be r a nd stops re c ursing

. Fina lly, it a dds the a m ount of turning to the c urre nt dire c tion a nd, if ne e de d, wra ps the num be r a round ba c k to z e ro, onc e

a ga in by using mod .

H andling Animal Eating


E a ting is a sim ple proc e ss. W e just ne e d to c he c k if the re ’s a pla nt a t the a nim a l’s c urre nt loc a tion, a nd if the re is, c onsum e it:
(defun eat (animal)
(let ((pos (cons (animal-x animal) (animaly animal))))
(when (gethash pos *plants*)
(incf (animal-energy animal) *plant-energy*)
(remhash pos *plants*))))
T he a nim a l’s e ne rgy is inc re a se d by the a m ount of e ne rgy tha t wa s be ing store d by the pla nt. W e the n re m ove the pla nt from the
world using the remhash func tion.

H andling Animal Re pr oduc tion


Re produc tion is usua lly the m ost inte re sting pa rt in a ny a nim a l sim ula tion. W e ’ll ke e p things sim ple by ha ving our a nim a ls
re produc e a se xua lly, but it should still be inte re sting, be c a use e rrors will c re e p into the ir ge ne s a s the y ge t c opie d, c a using
m uta tions.
(defparameter *reproduction-energy* 200)

(defun reproduce (animal)


(let ((e (animal-energy animal)))

(when (>= e *reproduction-energy*)

(setf (animal-energy animal) (ash e −1))

(let ((animal-nu (copy-structure animal))


(genes (copy-list (animal-genes animal)))
(mutation (random 8)))
(setf (nth mutation genes) (max 1 (+ (nth mutation genes) (random 3) −1)))
(setf (animal-genes animal-nu) genes)
(push animal-nu *animals*)))))
It ta ke s a he a lthy pa re nt to produc e he a lthy offspring, so our a nim a ls will re produc e only if the y ha ve a t le a st 200 da ys’ worth of

e ne rgy . W e use the globa l c onsta nt *reproduction-energy* to de c ide wha t this c utoff num be r should be . If the a nim a l

de c ide s to re produc e , it will lose ha lf its e ne rgy to its c hild .

T o c re a te the ne w a nim a l, we sim ply c opy the struc ture of the pa re nt with the copy-structure func tion . W e ne e d to be
c a re ful though, sinc e copy-structure pe rform s only a shallow c opy of a struc ture . T his m e a ns tha t if the re a re a ny fie lds in the
struc ture tha t c onta in va lue s tha t a re m ore c om plic a te d tha n just num be rs or sym bols, the va lue s in those fie lds will be sha re d with
the pa re nt. An a nim a l’s ge ne s, whic h a re store d in a list, re pre se nt the only suc h c om ple x va lue in our a nim a l struc ture s. If we
a re n’t c a re ful, m uta tions in the ge ne s of a n a nim a l would sim ulta ne ously a ffe c t a ll its pa re nts a nd c hildre n. In orde r to a void this,

we ne e d to c re a te a n e xplic it c opy of our ge ne list using the copy-list func tion .


He re is a n e xa m ple tha t shows wha t horrible things c ould ha ppe n if we just re lie d on the sha llow c opy from the copy-structure
func tion:
> (defparameter *parent* (make-animal :x 0
:y 0
:energy 0
:dir 0

:genes '(1 1 1 1 1 1 1 1)))


*PARENT*

> (defparameter *child* (copy-structure *parent*))


*CHILD*

> (setf (nth 2 (animal-genes *parent*)) 10)


10

> *parent*
#S(ANIMAL :X 0 :Y 0 :ENERGY 0 :DIR 0 :GENES (1 1 10 1 1 1 1 1))

> *child*
#S(ANIMAL :X 0 :Y 0 :ENERGY 0 :DIR 0 :GENES (1 1 10 1 1 1 1 1))

He re , we ’ve c re a te d a pa re nt a nim a l with a ll its ge ne s se t to 1 . Ne xt, we use copy-structure to c re a te a c hild .

T he n we se t the third (se c ond c ounting from z e ro) ge ne e qua l to 10 . Our pa re nt now looks c orre c t . Unfortuna te ly,

sinc e we ne gle c te d to use copy-list to c re a te a se pa ra te list of ge ne s for the c hild, the c hild ge ne s we re a lso c ha nge d whe n
the pa re nt m uta te d. Any tim e you ha ve da ta struc ture s tha t go be yond sim ple a tom ic sym bols or num be rs, you ne e d to be ve ry
c a re ful whe n using setf so tha t the se kinds of bugs don’t c re e p into your c ode . In future c ha pte rs (e spe c ia lly Cha pte r 14), you’ll
le a rn how to a void the se issue s by not using func tions tha t m uta te da ta dire c tly, in the m a nne r tha t setf doe s.
T o m uta te a n a nim a l in our reproduce func tion, we ra ndom ly pic k one of its e ight ge ne s a nd pla c e it in the mutation va ria ble .
T he n we use setf to twiddle tha t va lue a bit, a ga in using a ra ndom num be r. W e did this twiddling on the following line :
(setf (nth mutation genes) (max 1 (+ (nth mutation genes) (random 3) −1)))
In this line , we ’re slightly c ha nging a ra ndom slot in the ge ne list. T he num be r of the slot is store d in the loc a l va ria ble
mutation. W e a dd a ra ndom num be r le ss tha n thre e to the va lue in this slot, a nd the n subtra c t one from the tota l. T his m e a ns the
ge ne va lue will c ha nge plus or m inus one , or sta y the sa m e . Sinc e we don’t wa nt a ge ne va lue to be sm a lle r tha n one , we use the
max func tion to m a ke sure it is a t le a st one .
W e the n use push to inse rt this ne w c ritte r into our globa l *animal* list, whic h a dds it to the sim ula tion.
Simulating a Day in O ur W or ld
Now tha t we ha ve func tions tha t ha ndle e ve ry de ta il of a n a nim a l’s routine , le t’s write one tha t sim ula te s a da y in our world.
(defun update-world ()

(setf *animals* (remove-if (lambda (animal)


(<= (animal-energy animal) 0))
*animals*))

(mapc (lambda (animal)


(turn animal)
(move animal)
(eat animal)
(reproduce animal))
*animals*)

(add-plants))

First, this func tion re m ove s a ll de a d a nim a ls from the world . (An a nim a l is de a d if its e ne rgy is le ss tha n or e qua l to z e ro. )

Ne xt, it m a ps a c ross the list, ha ndling e a c h of the a nim a l’s possible da ily a c tivitie s: turning, m oving, e a ting, a nd re produc ing
. Sinc e a ll the se func tions ha ve side e ffe c ts (the y m odify the individua l a nim a l struc ture s dire c tly, using setf), we use the mapc
func tion, whic h doe s not wa ste tim e ge ne ra ting a re sult list from the m a pping proc e ss.

Fina lly, we c a ll the add-plants func tion , whic h a dds two ne w pla nts to the world e ve ry da y (one in the jungle a nd one in
the ste ppe ). Sinc e the re a re a lwa ys ne w pla nts growing on the la ndsc a pe , our sim ula te d world should e ve ntua lly re a c h a n
e quilibrium , a llowing a re a sona bly la rge popula tion of a nim a ls to survive throughout the spa ns of tim e we sim ula te .
Dr awing O ur W or ld
A sim ula te d world isn’t a ny fun unle ss we c a n a c tua lly se e our c ritte rs running a round, se a rc hing for food, re produc ing, a nd
dying. T he draw-world func tion ha ndle s this by using the *animals* a nd *plants* da ta struc ture s to dra w a sna pshot of the c urre nt
world to the RE PL .
(defun draw-world ()

(loop for y
below *height*
do (progn (fresh-line)
(princ "|")

(loop for x
below *width*

do (princ (cond ((some (lambda (animal)


(and (= (animal-x animal) x)
(= (animaly animal) y)))
*animals*)

#\M)

((gethash (cons x y) *plants*) #\*)

(t #\space))))

(princ "|"))))

First, the func tion use s a loop to ite ra te through e a c h of the world’s rows . E ve ry row sta rts with a ne w line (c re a te d with
fresh-line) followe d by a ve rtic a l ba r, whic h shows us whe re the le ft e dge of the world is. Ne xt, we ite ra te a c ross the c olum ns of

the c urre nt row , c he c king for a n a nim a l a t e ve ry loc a tion. W e pe rform this c he c k using the some func tion , whic h le ts
us de te rm ine if a t le a st one ite m in a list obe ys a c e rta in c ondition. In this c a se , the c ondition we ’re c he c king is whe the r the re ’s a n

a nim a l a t the c urre nt x-a nd y-c oordina te s. If so, we dra w the le tte r M a t tha t spot . (T he c a pita l le tte r M looks a little like a n
a nim a l, if you use your im a gina tion. )

Othe rwise , we c he c k for a pla nt, whic h we ’ll indic a te with a n a ste risk (*) c ha ra c te r . And if the re isn’t a pla nt or a n

a nim a l, we dra w a spa c e c ha ra c te r . L a stly, we dra w a nothe r ve rtic a l ba r to c a p off the e nd of e a c h line .
Notic e tha t in this func tion, we ne e d to se a rc h through our e ntire *animals* list, whic h will c a use a pe rform a nc e pe na lty.
Howe ve r, draw-world is not a c ore routine in our sim ula tion. As you’ll se e shortly, the use r inte rfa c e for our ga m e will a llow us to
run thousa nds of da ys of the sim ula tion a t a tim e , without dra wing the world to the sc re e n until the e nd. Sinc e the re ’s no ne e d to
dra w the sc re e n on e ve ry single da y whe n we do this, the pe rform a nc e of draw-world ha s no im pa c t on the ove ra ll pe rform a nc e of
the sim ula tion.
Cr e ating a Use r Inte r fac e
Fina lly, we ’ll c re a te a use r inte rfa c e func tion for our sim ula tion, c a lle d evolution.
(defun evolution ()

(draw-world)
(fresh-line)

(let ((str (read-line)))

(cond ((equal str "quit") ())

(t (let ((x (parse-integer str :junk-allowed t)))


(if x

(loop for i
below x
do (update-world)
if (zerop (mod i 1000))
do (princ #\.))
(update-world))

(evolution))))))

First, this func tion dra ws the world in the RE PL . T he n it wa its for the use r to e nte r a c om m a nd a t the RE PL using read-

line . If the use r e nte rs quit, the sim ula tion e nds . Othe rwise , it will a tte m pt to pa rse the use r’s c om m a nd using parse-

integer . W e se t :junk-allowed to true for parse-integer, whic h le ts the inte rfa c e a c c e pt a string e ve n if it isn’t a va lid
inte ge r.

If the use r e nte rs a va lid inte ge r n, the progra m will run the sim ula tion for n sim ula te d da ys, using a loop . It will a lso print
a dot to the sc re e n for e ve ry 1000 da ys, so the use r c a n se e tha t the c om pute r ha sn’t froz e n while running the sim ula tion.
If the input isn’t a va lid inte ge r, we run update-world to sim ula te one m ore da y. Sinc e read-line a llows for a n e m pty va lue ,
the use r c a n just ta p the e nte r ke y a nd wa tc h the a nim a ls m ove a round the ir world.

Fina lly, the evolution func tion re c ursive ly c a lls itse lf to re dra w the world a nd a wa it m ore use r input . Our sim ula tion is
now c om ple te .
Le t's W atc h Some Evolution!
T o sta rt the sim ula tion, e xe c ute evolution a s follows:
> (evolution)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| M
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Our world is c urre ntly e m pty, e xc e pt for the Ada m /E ve a nim a l in the c e nte r. Hit e nte r a fe w tim e s to c yc le through a fe w da ys:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| *
|
|
|
| * M
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[enter]
|
|
|
|
|
|
| *
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| *
|
| *
|
| * M
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Our unde r-e volve d a nim a l is stum bling a round ra ndom ly, a nd a fe w pla nts a re sta rting to grow.
Ne xt, e nte r 100 to se e wha t the world looks like a fte r 100 da ys:
100
| *
* |
|
* * |
| * ** *
|
| * * *
* * |
| M *
M* * |
| * M *
|
| * * *
|
| * M M *
|
| M M M
* |
| * M M * *
|
| * M M MM *
* |
| M* *
* |
| M M M
* |
| * M MM
|
| * * * M M * M
M * * * |
| * * * M MM * M
* * |
| M M M* *
* * |
| * * M M M
|
| * * M
* * * |
|
M * |
| * * M M
|
| * * * M M
|
| * * * M M M
M |
| * * M M
* * * |
| *
* |
| * *
* |
| *
* |
| * * M
* |
| *
* |
| * * M *
* * * |
Our a nim a l ha s a lre a dy m ultiplie d quite a bit, a lthough this ha s le ss to do with the a m ount of food it ha s e a te n tha n with the
la rge a m ount of “ sta rte r e ne rgy” we ga ve it.
Now le t’s go a ll out a nd run the sim ula tion for five m illion da ys! Sinc e we ’re using CL ISP, this will be kind of slow, a nd you
m a y wa nt to sta rt it up in the e ve ning a nd le t it run ove rnight. W ith a highe r-pe rform a nc e L isp, suc h a s SBCL , it c ould ta ke only a
c ouple of m inute s.
5000000
| *
M M |
| * * *
M |
| M
* |
| M *
* M M |
| * M *
* |
| **
* |
| M
|
| M * M M
* |
| * * M M M M
M M |
| M
M |
| M* * M M MMM M
M |
| * * MMM * M M
M |
| * M * M M *MM* MMM M
* |
| M MMMMMM M M
M * |
| M MMMM MMM M * M
M |
| M * M MMM
* * |
| M M M M M M
M * |
| M M M MMM M
M M |
| M M M MM
M * * |
| * MMM M
MM M M |
| M MM *
M |
| MMM M M M
|
| * M M
|
| M M M *M *
M M |
| M M
MM M |
| M * M * M
M |
| MM M M M
M |
| M M M
|
| M * M * *
* |
| M M M
|
Our world doe sn’t look m uc h diffe re nt a fte r five m illion da ys tha n it did a fte r a hundre d da ys. Of c ourse , the re a re m ore a nim a ls,
both tra ve ling a c ross the ste ppe s a nd e njoying the lush ve ge ta tion of the jungle .
But a ppe a ra nc e s a re de c e ptive . T he se a nim a ls a re distinc tly diffe re nt from the ir e a rly a nc e stors. If you obse rve the m c lose ly (by
ta pping e nte r), you’ll se e tha t som e of the c re a ture s m ove in stra ight line s a nd othe rs just jitte r a round in a sm a ll a re a , ne ve r
ta king m ore tha n a single ste p in a ny dire c tion. (As a n e xe rc ise , you c ould twe a k the c ode to use diffe re nt le tte rs for e a c h a nim a l,
in orde r to m a ke the ir m otion e ve n e a sie r to obse rve . ) You c a n se e this c ontra st e ve n m ore c le a rly by typing quit to e xit the
sim ula tion, the n c he c king the c onte nts of the *animals* va ria ble a t the RE PL :
>*animals*
#S(ANIMAL :X 6 :Y 24 :ENERGY 65 :DIR 3 :GENES (67 35 13 14 1 3 11 74))
#S(ANIMAL :X 72 :Y 11 :ENERGY 78 :DIR 6 :GENES (68 36 13 12 2 4 11 72))
#S(ANIMAL :X 16 :Y 26 :ENERGY 78 :DIR 0 :GENES (71 36 9 16 1 6 5 77))
#S(ANIMAL :X 50 :Y 25 :ENERGY 76 :DIR 4 :GENES (2 2 7 5 21 208 33 9))
#S(ANIMAL :X 53 :Y 13 :ENERGY 34 :DIR 4 :GENES (1 2 8 5 21 208 33 8))
#S(ANIMAL :X 58 :Y 10 :ENERGY 66 :DIR 6 :GENES (5 2 7 2 22 206 29 3))
#S(ANIMAL :X 74 :Y 3 :ENERGY 77 :DIR 0 :GENES (68 35 11 12 1 3 11 74))
#S(ANIMAL :X 47 :Y 19 :ENERGY 47 :DIR 2 :GENES (5 1 8 4 21 207 30 3))
#S(ANIMAL :X 27 :Y 22 :ENERGY 121 :DIR 1 :GENES (69 36 11 12 1 2 11 74))
#S(ANIMAL :X 96 :Y 14 :ENERGY 78 :DIR 5 :GENES (71 37 9 17 2 5 5 77))
#S(ANIMAL :X 44 :Y 19 :ENERGY 28 :DIR 1 :GENES (1 3 7 5 22 208 34 8))
#S(ANIMAL :X 55 :Y 22 :ENERGY 18 :DIR 7 :GENES (1 3 8 5 22 208 34 7))
#S(ANIMAL :X 52 :Y 10 :ENERGY 63 :DIR 0 :GENES (1 2 7 5 23 208 34 7))
#S(ANIMAL :X 49 :Y 14 :ENERGY 104 :DIR 4 :GENES (4 1 9 2 22 203 28 1))
#S(ANIMAL :X 39 :Y 23 :ENERGY 62 :DIR 7 :GENES (70 37 9 15 2 6 5 77))
#S(ANIMAL :X 97 :Y 11 :ENERGY 48 :DIR 0 :GENES (69 36 13 12 2 5 12 72))
...
If you look c lose ly a t a ll the a nim a ls in the list, you’ll notic e tha t the y ha ve two distinc t type s of ge nom e s. One group of
a nim a ls ha s a high num be r towa rd the front of the list, whic h c a use s the m to m ove m ostly in a stra ight line . T he othe r group ha s a
la rge num be r towa rd the ba c k of the list, whic h c a use s the m to jitte r a bout within a sm a ll a re a . T he re a re no a nim a ls with a
ge nom e be twe e n those two e xtre m e s. Ha ve we e volve d two diffe re nt spe c ie s?
If you we re to c re a te a func tion tha t m e a sure d how fa r the se e volve d a nim a ls tra ve l in a fixe d a m ount of tim e , the histogra m of
the dista nc e would a ppe a r as follows:

T his is a c le a r bim oda l distribution, showing tha t the be ha vior of the se a nim a ls a ppe a rs to fa ll into two popula tions. T hink a bout
the e nvironm e nt the se a nim a ls live in, a nd try to re a son why this bim oda l distribution would e volve . W e will disc uss the solution to
this c onundrum ne xt.
Explaining the Evolution
T he solution to the e volution puz z le is pre tty stra ightforwa rd. T he re a re two possible surviva l stra te gie s a n a nim a l c a n a dopt in
this im a gina ry world:
Foc us on the ric h food supply in the jungle . Any a nim a l a dopting this stra te gy ne e ds to be c onse rva tive in its m otion. It c a n’t
stra y too fa r ove r tim e , or it m ight fa ll out of the jungle . Of c ourse , the se type s of a nim a ls do ne e d to e volve a t le a st a bit of
jitte ry m otion, or the y will ne ve r find a ny food a t a ll. L e t’s c a ll the se c onse rva tive , jitte ry, jungle -dwe lling a nim a ls the e le phant
spe c ie s.
Fora ge the spa rse ve ge ta tion of the ste ppe s. He re , the m ost c ritic a l tra it for surviva l is to c ove r la rge dista nc e s. Suc h a n a nim a l
ne e ds to be ope n-m inde d, a nd m ust c onsta ntly m igra te to ne w a re a s of the m a p to find food. (It c a n’t tra ve l in too stra ight a line
howe ve r, or it m a y e nd up c om pe ting for re sourc e s with its own offspring. ) T his stra te gy re quire s a bit of na ïve optim ism , a nd c a n
a t tim e s le a d to doom . L e t’s c a ll the se libe ra lly m inde d, risk-ta king a nim a ls the donk e y spe c ie s.
E xpa nding the sim ula tion to e volve the thre e bra nc he s of gove rnm e nt is le ft a s a n e xe rc ise to the re a de r.
W hat You've Le ar ne d
In this c ha pte r, we disc usse d the loop c om m a nd in de ta il. Along the wa y, you le a rne d the following:
T he loop c om m a nd is a one -stop looping shop— it c a n do a nything you ne e d a loop to do.
T o c ount through num be rs in a loop, use the for phra se .
T o c ount through ite m s in a list within a loop, use the for in phra se .
You c a n c olle c t ite m s inside a list a nd re turn the m a s a list with the collect phra se .
Use the Pe riodic T a ble of the L oop Ma c ro to find othe r use ful phra se s supporte d by loop.
Chapte r 11. P r inting Te xt with the for mat F unc tion
E ve n in this m ode rn e ra of progra m m ing, it’s e xtre m e ly im porta nt to be a ble to m a nipula te te xt, a nd Com m on L isp ha s som e of
the fa nc ie st te xt-printing func tions a va ila ble . W he the r you ne e d to m a nipula te XML , HT ML , L inux c onfigura tion file s, or a ny
othe r da ta in a te xtua l form a t, L isp will m a ke your work e a sy.
T he m ost im porta nt a dva nc e d te xt printing func tion in Com m on L isp is the format func tion, whic h is the subje c t of this c ha pte r.
Anatomy of the for mat F unc tion
He re is a n e xa m ple of the format func tion in use :
> (format t "Add onion rings for only ˜$ dollars more!" 1.5)
Add onion rings for only 1.50 dollars more!
NIL
L e t’s ta ke a look a t wha t e a c h pa rt of this func tion m e a ns.
The De stination P ar ame te r
T he first pa ra m e te r to the format func tion is the de stination pa ra m e te r, whic h te lls format whe re to se nd the te xt it ge ne ra te s.
He re a re its possible va lue s: nil
Don’t print a nything; just re turn the va lue a s a string.
t
Print the va lue to the c onsole . In this c a se , the func tion just re turns nil a s a va lue (a s in our e xa m ple ).
stream
W rite the da ta to a n output stre a m (c ove re d in Cha pte r 12).
In the following e xa m ple , we se t the first pa ra m e te r to nil so it sim ply re turns the va lue a s a string:
> (princ (reverse
(format nil "Add onion rings for only ˜$ dollars more!" 1.5)))

!erom srallod 05.1 ylno rof sgnir noino ddA

"!erom srallod 05.1 ylno rof sgnir noino ddA"


T he re sulting string va lue ("Add onion rings for only 1.50 dollars more!") is pa sse d to the reverse func tion, a nd the n tha t

re ve rse d string is printe d to the sc re e n with the princ c om m a nd .


In this e xa m ple , the RE PL will a lso print the va lue of the e nte re d e xpre ssion, a long with the inform a tion output by the princ

c om m a nd. T his is why you se e the va lue displa ye d a se c ond tim e . For the re m a inde r of this c ha pte r, the e xa m ple s will om it
the se va lue s printe d by the RE PL , a nd show only the inform a tion e xplic itly printe d by our c ode .
The Contr ol Str ing P ar ame te r
T he se c ond pa ra m e te r to the format func tion is a control string, whic h c ontrols the te xt form a tting. T he format func tion’s
powe r lie s in the c ontrol string. In our c urre nt e xa m ple , the c ontrol string is "Add onion rings for only ˜$ dollars more!".
By de fa ult, the te xt in this string is sim ply printe d a s output. Howe ve r, you c a n pla c e c ontrol se que nc e s into this string to a ffe c t
the form a t of the output, a s de sc ribe d in the re m a inde r of this c ha pte r. Our c urre nt e xa m ple c onta ins the c ontrol se que nc e ˜$,
whic h indic a te s a mone tary floating-point va lue . E ve ry c ontrol se que nc e re c ogniz e d by the format func tion be gins with the tilde
(˜) c ha ra c te r.
Value P ar ame te r s
T he format pa ra m e te rs following the c ontrol string c onta in va lue s, or the a c tua l da ta to be displa ye d a nd form a tte d. As you’ll
se e , the c ontrol string inte ra c ts with the se pa ra m e te rs a nd c ontrols the ir form a tting.
Contr ol Se que nc e s for P r inting Lisp Value s
Any L isp va lue c a n be printe d with the print or prin1 c om m a nd. T o print a va lue for hum a ns, without a ny de lim ite rs, we c a n
use the princ c om m a nd:
> (prin1 "foo")
"foo"
> (princ "foo")
foo
W e c a n use the ˜s a nd ˜a c ontrol se que nc e s with format to produc e the sa m e be ha vior a s prin1 a nd princ. W he n use d with
format, the ˜s c ontrol se que nc e inc lude s a ppropria te de lim ite rs. T he ˜a shows the va lue , without de lim ite rs, for hum a ns to re a d:
> (format t "I am printing ˜s in the middle of this sentence." "foo")
I am printing "foo" in the middle of this sentence.
> (format t "I am printing ˜a in the middle of this sentence." "foo")
I am printing foo in the middle of this sentence.
W e c a n a djust the be ha vior of the se c ontrol se que nc e s e ve n furthe r by e nte ring pa ra m e te rs within the c ontrol se que nc e . For
insta nc e , we c a n pla c e a num be r n in front of the a or s to indic a te tha t the va lue should be padde d with bla nk spa c e s on the right.
T he format c om m a nd will the n a dd spa c e s until the tota l width of the va lue is n.
For e xa m ple , by writing ˜10a in the following e xa m ple , we a dd se ve n spa c e s to the right of foo, m a king the tota l width of the
form a tte d va lue 10 c ha ra c te rs:
> (format t "I am printing ˜10a within ten spaces of room." "foo")
I am printing foo within ten spaces of room.
W e c a n a lso a dd spa c e s on the le ft side of the va lue by a dding the @ sym bol, a s follows:
> (format t "I am printing ˜10@a within ten spaces of room." "foo")
I am printing foo within ten spaces of room.
In this c a se , the tota l width of the a dde d spa c e s a long with the va lue foo e qua ls 10 c ha ra c te rs.
Control se que nc e s c a n a c c e pt m ore tha n just one pa ra m e te r. In the pre c e ding e xa m ple s, we se t only the first pa ra m e te r, whic h
c ontrols the fina l width of the fina l form a tte d string. L e t’s look a t a n e xa m ple tha t se ts the se c ond pa ra m e te r of the ˜a c ontrol
se que nc e a s we ll:
> (format t "I am printing ˜10,3a within ten (or more) spaces of room." "foo")
I am printing foo within ten (or more) spaces of room.
As you c a n se e , a dditiona l pa ra m e te rs to a c ontrol se que nc e a re se pa ra te d with a c om m a . In this c a se , the se c ond pa ra m e te r is
se t to 3, whic h te lls the format c om m a nd to a dd spa c e s in groups of thre e (inste a d of just one a t a tim e ) until the goa l width of 10
is re a c he d. In this e xa m ple , a tota l of nine spa c e s a re a dde d to the form a tte d va lue . T his m e a ns it ove rshot our goa l width of 10
(by de sign), le a ding inste a d to a tota l width of 12 (nine spa c e s plus the le tte rs foo). Pa dding strings in m ultiple s like this is not a
c om m only ne e de d fe a ture , so the se c ond pa ra m e te r to the ˜a c ontrol se que nc e is ra re ly use d.
Som e tim e s we ne e d to c ontrol the e xa c t num be r of spa c e s to a dd to our string, re ga rdle ss of the le ngth of the fina l va lue . W e
c a n do this by se tting the third pa ra m e te r in the ˜a c ontrol se que nc e . For e xa m ple , suppose we wa nt to print e xa c tly four spa c e s
a fte r the fina l form a tte d va lue . T o se t the third c ontrol se que nc e pa ra m e te r e qua l to four, we pla c e two c om m a s in front of the
pa ra m e te r to indic a te tha t the first two pa ra m e te rs a re bla nk, the n follow this with a 4:
> (format t "I am printing ˜,,4a in the middle of this sentence." "foo")
I am printing foo in the middle of this sentence.
Notic e tha t the re a re e xa c tly four e xtra spa c e s inse rte d in the re sults. Sinc e the first a nd se c ond pa ra m e te rs we re not spe c ifie d
be fore the c om m a s, the ir de fa ult va lue s will be use d.
T he fourth c ontrol se que nc e pa ra m e te r spe c ifie s whic h c ha ra c te r will be use d for pa dding. For e xa m ple , in the following listing,
we pa d the printe d va lue with four e xc la m a tion points:
> (format t "The word ˜,,4,'!a feels very important." "foo")
The word foo!!!! feels very important.
T he se c ontrol se que nc e pa ra m e te rs c a n a lso be c om bine d. For e xa m ple , we c a n a dd the @ sym bol to our c ode to indic a te tha t the
e xc la m a tion m a rks should a ppe a r in front of the va lue , like this:
> (format t "The word ˜,,4,'!@a feels very important." "foo")
The word !!!!foo feels very important.
Now tha t you ha ve a n ove rvie w of format c om m a nd c ontrol se que nc e s, le t’s look a t how to use the m for form a tting, be ginning
with num be rs.
Contr ol Se que nc e s for F or matting Numbe r s
T he format c om m a nd ha s m a ny options de signe d spe c ific a lly for c ontrolling the a ppe a ra nc e of num be rs. L e t’s look a t som e of
the m ore use ful one s.
Contr ol Se que nc e s for F or matting Inte ge r s
First, we c a n use format to displa y a num be r using a diffe re nt ba se . For insta nc e , we c a n displa y a num be r in he xa de c im a l (ba se -
16) with the ˜x c ontrol se que nc e :
> (format t "The number 1000 in hexadecimal is ˜x" 1000)
The number 1000 in hexadecimal is 3E8
Sim ila rly, we c a n displa y a num be r in bina ry (ba se -2) using the ˜b c ontrol se que nc e :
> (format t "The number 1000 in binary is ˜b" 1000)
The number 1000 in binary is 1111101000
W e c a n e ve n e xplic itly de c la re tha t a va lue will be displa ye d a s a de c im a l (ba se -10) num be r, using the ˜d c ontrol se que nc e :
> (format t "The number 1000 in decimal is ˜d" 1000)
The number 1000 in decimal is 1000
In this c a se , we would ha ve gotte n the sa m e re sult if we ha d just use d the m ore ge ne ric ˜a c ontrol se que nc e . T he diffe re nc e is
tha t ˜d supports spe c ia l pa ra m e te rs a nd fla gs tha t a re spe c ific to printing de c im a l num be rs. For e xa m ple , we c a n pla c e a c olon
inside the c ontrol se que nc e to e na ble c om m a s a s digit group se pa ra tors:
> (format t "Numbers with commas in them are ˜:d times better." 1000000)
Numbers with commas in them are 1,000,000 times better.
T o c ontrol the width of the num be r, we c a n se t the pa dding pa ra m e te r, just a s we did with the ˜a a nd ˜s c ontrol se que nc e s:
> (format t "I am printing ˜10d within ten spaces of room" 1000000)
I am printing 1000000 within ten spaces of room
T o c ha nge the c ha ra c te r use d for pa dding, pa ss in the de sire d c ha ra c te r (in this c a se , the x c ha ra c te r) a s the se c ond pa ra m e te r:
> (format t "I am printing ˜10,'xd within ten spaces of room" 1000000)
I am printing xxx1000000 within ten spaces of room
Contr ol Se que nc e s for F or matting F loating-P oint Numbe r s
Floa ting-point va lue s a re ha ndle d with the ˜f c ontrol se que nc e . As with a ll of the pre viously disc usse d c ontrol se que nc e s, we c a n
c ha nge the va lue ’s displa y width by c ha nging the first pa ra m e te r. W he n use d with floa ting-point num be rs, the format c om m a nd
will a utom a tic a lly round the va lue to fit within the re que ste d num be r of c ha ra c te rs (inc luding the de c im a l point):
> (format t "PI can be estimated as ˜4f" 3.141593)
PI can be estimated as 3.14
As you c a n se e , the fina l width of 3.14 is four c ha ra c te rs wide , a s spe c ifie d by the c ontrol se que nc e .
T he se c ond pa ra m e te r of the ˜f c ontrol se que nc e c ontrols the num be r of digits displa ye d a fte r the de c im a l point. For e xa m ple , if
we pa ss 4 a s the se c ond pa ra m e te r in the pre c e ding e xa m ple , we ge t the following output:
> (format t "PI can be estimated as ˜,4f" 3.141593)
PI can be estimated as 3.1416
Note tha t Com m on L isp a c tua lly inc lude s the c onsta nt pi a s pa rt of the sta nda rd, so you c ould a lso re write the c om m a nd like
this:
> (format t "PI can be estimated as ˜,4f" pi)
PI can be estimated as 3.1416
T he third pa ra m e te r of the ˜f c ontrol se que nc e c a use s the num be r to be sc a le d by fa c tors of te n. For e xa m ple , we c a n pa ss 2 a s
the third pa ra m e te r, whic h we c a n use to m ultiply a fra c tion by 10 2 to turn it into a pe rc e nta ge :
> (format t "Percentages are ˜,,2f percent better than fractions" 0.77)
Percentages are 77.0 percent better than fractions
In a ddition to ˜f, we c a n use the c ontrol se que nc e ˜$, whic h is use d for form a tting c urre nc ie s:
> (format t "I wish I had ˜$ dollars in my bank account." 1000000.2)
I wish I had 1000000.20 dollars in my bank account.
You sa w a n e xa m ple tha t use d ˜$ a t the be ginning of this c ha pte r.
P r inting M ultiple Line s of O utput
Com m on L isp ha s two diffe re nt c om m a nds for sta rting a ne w line during printing. T he first, terpri, sim ply te lls L isp to
te rm ina te the c urre nt line a nd sta rt a ne w one for printing subse que nt output. For e xa m ple , we c a n print two num be rs on diffe re nt
line s like so:
> (progn (princ 22)
(terpri)
(princ 33))
22
33
W e c a n a lso sta rt a ne w line with fresh-line. T his c om m a nd will sta rt a ne w line , but only if the c ursor position in the RE PL
isn’t a lre a dy a t the ve ry front of a line . L e t’s look a t som e e xa m ple s:
> (progn (princ 22)
(fresh-line)
(princ 33))
22
33
> (progn (princ 22)
(fresh-line)
(fresh-line)
(princ 33))
22
33
As you c a n se e , pla c ing two fresh-line sta te m e nts be twe e n the two princ c a lls re sulte d in L isp printing only one line be twe e n
the outputte d num be rs. T he first fresh-line sta rts a ne w line ; the se c ond fresh-line is sim ply ignore d.
E sse ntia lly, the terpri c om m a nd sa ys “ sta rt a ne w line , ” whe re a s the fresh-line c om m a nd sa ys “ sta rt a ne w line , if ne e de d. ”
Any c ode using the terpri c om m a nd ne e ds to “ know” wha t wa s printe d be fore . Othe rwise , unsightly e m pty line s m a y re sult. Sinc e
it’s a lwa ys be tte r if diffe re nt pa rts of a progra m know a s little a bout e a c h othe r a s possible , m ost L ispe rs pre fe r using fresh-line
ove r terpri, be c a use it a llows the m to de c ouple the printing of one pie c e of da ta from the ne xt.
T he format c om m a nd ha s two c ontrol se que nc e s tha t a re a na logous to terpri a nd fresh-line:
˜%
c a use s a ne w line to be c re a te d in a ll c a se s (like terpri)
˜&
c re a te s ne w line s only a s ne e de d (like fresh-line).
T he se e xa m ple s illustra te this diffe re nc e :
> (progn (format t "this is on one line ˜%")
(format t "˜%this is on another line"))
this is on one line

this is on another line


> (progn (format t "this is on one line ˜&")
(format t "˜&this is on another line"))
this is on one line
this is on another line

As you c a n se e , using a n e xtra ˜% prints a n unsightly e m pty line , a nd using ˜& in the sa m e pla c e s doe s not.
T he se two line -te rm ina tion se que nc e s c a n a lso ha ve a n a dditiona l pa ra m e te r in front of the m to indic a te the num be r of ne w line s
to be c re a te d. T his is use ful in c a se s whe re we wa nt to use e m pty line s to spa c e out our output. For e xa m ple , the a ddition of 5 in
the following e xa m ple a dds five e m pty line s to our output:
> (format t "this will print ˜5%on two lines spread far apart")
this will print

on two lines spread far apart


Justifying O utput
T he format c om m a nd a lso give s us a lot of c ontrol ove r te xt justific a tion. Control se que nc e s a llow us to form a t ta ble s, c e nte r
te xt, a nd pe rform othe r use ful justific a tion fe a ts.
T o he lp you unde rsta nd the va rious justific a tion rule s, we ’ll c re a te a sim ple func tion tha t re turns diffe re nt a nim a l na m e s with
va rying c ha ra c te r le ngths:
> (defun random-animal ()
(nth (random 5) '("dog" "tick" "tiger" "walrus" "kangaroo")))
RANDOM-ANIMAL
> (random-animal)
"walrus"
Now suppose we wa nt to displa y a bunc h of ra ndom a nim a ls in a ta ble . W e c a n do this by using the ˜t c ontrol se que nc e . ˜t c a n
ta ke a pa ra m e te r tha t spe c ifie s the c olum n position a t whic h the form a tte d va lue should a ppe a r. For e xa m ple , to ha ve our ta ble of
a nim a ls a ppe a r in thre e c olum ns a t the fifth, fifte e nth, a nd twe nty-fifth c ha ra c te r positions, we c ould c re a te this ta ble :
> (loop repeat 10
do (format t "˜5t˜a ˜15t˜a ˜25t˜a˜%"
(random-animal)
(random-animal)
(random-animal)))
kangaroo tick dog
dog walrus walrus
walrus tiger tiger
walrus kangaroo dog
kangaroo tiger dog
tiger walrus kangaroo
tick dog tiger
kangaroo tick kangaroo
tiger dog walrus
kangaroo kangaroo tick
Re m e m be r tha t a loop c om m a nd with a repeat 10 c la use e xe c ute s the body of the loop 10 tim e s. As you c a n se e , use of the ˜t
c ontrol se que nc e c a use d the a nim a ls to be la id out in a ne a tly form a tte d ta ble .

Now suppose we wa nt a ll the a nim a ls be spa c e d e qua lly a pa rt on a single line . T o do so, we c a n use the ˜< a nd ˜> c ontrol
se que nc e s, a s follows:
> (loop repeat 10
do (format t "˜30<˜a˜;˜a˜;˜a˜>˜%"
(random-animal)
(random-animal)
(random-animal)))
tick tiger tick
tick tiger dog
tick dog dog
kangaroo kangaroo tiger
tiger tiger kangaroo
walrus kangaroo dog
dog dog walrus
kangaroo dog walrus
walrus dog walrus
kangaroo tiger tick
L e t’s de c onstruc t this c ontrol string to unde rsta nd how it works:
First, the ˜30< te lls the func tion tha t we ’re initia ting a bloc k of justifie d te xt. T he pa ra m e te r 30 indic a te s tha t the bloc k should
be 30 c ha ra c te rs wide . Ne xt, we ha ve thre e ˜a c ontrol se que nc e s in a row, one for e a c h a nim a l. E a c h ˜a is se pa ra te d by ;, whic h
te lls format tha t we ’re sta rting a ne w va lue to be justifie d by ˜<. (T he ˜; se que nc e s indic a te whe re e xtra spa c e s should be inse rte d
to justify the va lue s. ) W e the n e nd the justifie d se c tion with the ˜> c om m a nd se que nc e .
Be c a use the e qua l spa c ing of the a nim a ls in e a c h line doe sn’t gua ra nte e tha t the c olum ns c re a te d by printing m ultiple line s will
be prope rly a ligne d, we a dd the :@ fla g to our justific a tion ˜< c om m a nd se que nc e . For e xa m ple , we c a n c re a te a single , ne a tly
c e nte re d c olum n a s follows:
> (loop repeat 10 do (format t "˜30:@<˜a˜>˜%" (random-animal)))
dog
walrus
kangaroo
tick
tick
tiger
dog
kangaroo
kangaroo
dog
In the sa m e wa y, we c a n use :@ with m ultiple justifie d va lue s, c e nte ring the m on the line with a dditiona l spa c e a t the ir le ft a nd
right e nds:
> (loop repeat 10
do (format t "˜30:@<˜a˜;˜a˜;˜a˜>˜%"
(random-animal)
(random-animal)
(random-animal)))
walrus tick tick
walrus tiger tick
tick dog tick
walrus tiger tiger
kangaroo dog kangaroo
tiger kangaroo walrus
tiger kangaroo kangaroo
kangaroo tiger tick
tick tiger walrus
walrus tiger tick
T his ste p brings us c lose r to ha ving thre e ne a tly c e nte re d c olum ns, but our c olum ns a re still a bit wa vy be c a use we ’re a ligning
the va lue s within a single line , without te lling format to a rra nge the va lue s using thre e c e nte re d c olum ns.
T o produc e ne a t c olum ns, we ’ll still use the :@ fla g, but we ’ll de sc ribe our rows using thre e se pa ra te 10-c ha ra c te r justific a tion
se c tions:
> (loop repeat 10
do (format t "˜10:@<˜a˜>˜10:@<˜a˜>˜10:@<˜a˜>˜%"
(random-animal)
(random-animal)
(random-animal)))
tiger kangaroo kangaroo
kangaroo kangaroo walrus
tick tick tick
dog dog dog
tiger dog walrus
dog tiger kangaroo
walrus dog tick
tick walrus kangaroo
dog tick walrus
tiger tiger tiger
At la st, we ha ve the nic e ly c e nte re d ra ndom a nim a l c olum ns of our dre a m s!
As you c a n se e , the la yout options for format a re quite fle xible . Sinc e we ofte n ne e d to c re a te c om ple x lists a nd ta ble s of da ta
whe n de bugging a pplic a tions, the se tric ks a re ve ry he lpful whe n you ne e d to ge t a ha ndle on your da ta , e ve n with m ore c om ple x
progra m s.
Ite r ating Thr ough Lists Using Contr ol Se que nc e s
T he format func tion with its m a ny c ontrol se que nc e s is pra c tic a lly a progra m m ing la ngua ge in its own right. (In fa c t, m a ny
L ispe rs would c a ll it a domain-spe c ific language , a c onc e pt we will re visit in Cha pte r 17. ) And, like m ost progra m m ing la ngua ge s,
format c a n loop through da ta . It doe s this using the ˜{ a nd ˜} c ontrol se que nc e s.
T o a c hie ve this looping, pa ss the format func tion a c ontrol string c onta ining ˜{ a nd ˜}, a nd a list to ite ra te through. T he pa rt of
the c ontrol string be twe e n the ˜{ a nd ˜} se que nc e s is tre a te d a lm ost like the body of a loop. It will be e xe c ute d a num be r of tim e s,
de pe nding on the le ngth of the list tha t follows it. T he format func tion will ite ra te through this list, a pplying e a c h of its ite m s to
the spe c ifie d se c tion of the c ontrol string.
For e xa m ple , le t’s c re a te a list of a nim a ls tha t we c a n use for te sting:
> (defparameter *animals* (loop repeat 10 collect (random-animal)))
*ANIMALS*
> *animals*
("dog" "kangaroo" "walrus" "kangaroo" "kangaroo" "walrus" "kangaroo"
"dog" "tick" "tick")
Now we use the ˜{ ˜} c ontrol se que nc e s to to loop through this list:
> (format t "˜{I see a ˜a! ˜}" *animals*)
I see a dog! I see a kangaroo! I see a walrus! I see a kangaroo! I see
a kangaroo! I see a walrus! I see a kangaroo!
I see a dog! I see a tick! I see a tick!
T o produc e this loop, we sim ply pa ss the single va ria ble *animals*, a list of ite m s, to the format func tion. T he c ontrol string
ite ra te s through the list, c onstruc ting the se nte nc e "I see a ˜a" for e a c h m e m be r of *animals*.
A single ite ra tion c onstruc t c a n a lso gra b m ore tha n one ite m from the list, a s in this e xa m ple :
> (format t "˜{I see a ˜a... or was it a ˜a?˜%˜}" *animals*)
I see a dog... or was it a kangaroo?
I see a walrus... or was it a kangaroo?
I see a kangaroo... or was it a walrus?
I see a kangaroo... or was it a dog?
I see a tick... or was it a tick?
He re , we ha ve two ˜a c ontrol se que nc e s within a single looping c onstruc t. E a c h ˜a pulls a single a nim a l from the list, so two
a nim a ls print for e ve ry ite ra tion of the loop.
A Cr az y F or matting Tr ic k for Cr e ating P r e tty Table s of Data
L e t’s look a t one la st format e xa m ple tha t use s som e of the c ontrol se que nc e s you’ve a lre a dy se e n, a s we ll a s som e ne w one s.
T his e xa m ple will illustra te how the va rie d c ontrol se que nc e s c a n be c om bine d for c om ple x be ha vior.
> (format t "|˜{˜<|˜%|˜,33:;˜2d ˜>˜}|" (loop for x below 100 collect x))
| 0 1 2 3 4 5 6 7 8 9 |
|10 11 12 13 14 15 16 17 18 19 |
|20 21 22 23 24 25 26 27 28 29 |
|30 31 32 33 34 35 36 37 38 39 |
|40 41 42 43 44 45 46 47 48 49 |
|50 51 52 53 54 55 56 57 58 59 |
|60 61 62 63 64 65 66 67 68 69 |
|70 71 72 73 74 75 76 77 78 79 |
|80 81 82 83 84 85 86 87 88 89 |
|90 91 92 93 94 95 96 97 98 99 |
T o c re a te this nic e ly form a tte d ta ble of num be rs, we first use the looping c ontrol se que nc e s ˜{ ˜} to ite ra te through a list of
num be rs c re a te d by the loop c om m a nd. W ithin the ite ra tion, we pla c e justific a tion c ontrol se que nc e s ˜< ˜>, whic h we ’ve use d
e a rlie r. In this c a se , we don’t use the m to justify our te xt, but inste a d use the m to divide the re sulting te xt into pie c e s. T his is how
we bre a k our 100 num be rs into nic e c le a n rows of 10. W e pla c e the ˜:; c ontrol se que nc e inside our justific a tion c ontrol se que nc e s
˜< ˜>, whic h c a use s te xt to be broke n into pie c e s of e qua l le ngth.
W he n use d inside a justific a tion, the c ontrol string pre c e ding this se que nc e ˜:; (whic h in this c a se ha ppe ns to be |˜%|) will be
trigge re d only if the c urre nt c ursor position is be yond a c e rta in point, a s spe c ifie d by the se c ond pa ra m e te r, 33. In othe r words,
we ’re te lling the form a t func tion “ He y, onc e you ha ve 33 c ha ra c te rs’ worth of te xt, sta rt a fre sh line . ”
T he |˜%| c ontrol string c a use s the line bre a k a nd ve rtic a l ba rs to be printe d. T he num be r to be displa ye d is form a tte d using ˜2d,
whic h prints a le ft-justifie d num be r, two c ha ra c te rs wide .

Note

For full de ta ils on e ve ry single c ontrol se que nc e , se e the Common Lisp Hy pe rSpe c at
http://www. lispworks. c om /doc um e nta tion/Hype rSpe c /Front/inde x. htm .
Attac k of the Robots!

He re , we look a t a ga m e so horrifying tha t it’s sure to give you nightm a re s: Atta c k of the Robots! In this ga m e , robots ha ve ta ke n
ove r the world, a nd it’s your job to de stroy the m . T hough the plot m a y sound sc a ry, the pa rt of this ga m e tha t will re ally give a
L isp progra m m e r nightm a re s is the wa y it a buse s the loop a nd format c om m a nds in orde r to sque e z e a fully func tiona l robot-
fighting ga m e into a single page of c ode ! (T his progra m use s the “ c ra z y form a tting tric k” disc usse d in the pre vious se c tion. )
I ha ve a nnota te d the c ode with som e ba sic e xpla na tions. If you wa nt to unde rsta nd how the ga m e works in de ta il, you’ll ne e d to
re vie w m ost of the inform a tion from the pre vious c ouple of c ha pte rs. Also, you c a n visit http://la ndoflisp. c om / to downloa d the
sourc e c ode for the ga m e a nd re a d a m ore thorough e xpla na tion of the c ode .
T o win the ga m e , you ne e d to stra te gic a lly wa lk a round the fie ld to c a use a ll robots to c ollide with e a c h othe r. T he m ove m e nt
ke ys a re QW E /ASD/Z XC. T he se c ha ra c te rs form a grid on the le ft side of your ke yboa rd, le tting you m ove up, down, le ft, right, a s
we ll a s dia gona lly. You c a n a lso te le port with the T ke y.
E njoy!
W hat You've Le ar ne d
T his c ha pte r didn’t re a lly e ve n c om e c lose to c ove ring a ll of the fe a ture s of the format func tion. Howe ve r, it did provide a n
introduc tion, in whic h you le a rne d the following:
T he first pa ra m e te r of the format c om m a nd de te rm ine s whe the r the output is se nt to the RE PL , a stre a m , or re turne d a s a
string.
T he se c ond pa ra m e te r of the format c om m a nd is a c ontrol string tha t le ts you c ha nge the wa y your da ta is printe d. T he c ontrol
string ha s a sophistic a te d synta x, a c ting a lm ost like a progra m m ing la ngua ge in its own right.
T he re m a ining format pa ra m e te rs a re va lue s tha t c a n be re fe re nc e d from the c ontrol string to e m be d va lue s into the form a tte d
output.
T o e m be d a L isp va lue into a form a tte d string, use the ˜s or ˜a c ontrol se que nc e s.
Ma ny c ontrol se que nc e s a re a va ila ble for printing a nd c ustom iz ing the a ppe a ra nc e of num be rs.
T he format c om m a nd a lso ha s c om ple x looping a bilitie s tha t c a n be use d, for e xa m ple , to form a t ta ble s la id out in m a ny
diffe re nt style s.
Chapte r 12. W or king with Str e ams
Ne a rly e ve ry c om pute r progra m you write will ne e d to inte ra c t with the outside world a t som e point. Pe rha ps your progra m just
ne e ds to c om m unic a te with the use r through the RE PL , printing out inform a tion a nd c a pturing the use r’s input from the ke yboa rd.
Othe r progra m s you write m a y ne e d to re a d or write file s on a ha rd drive . Additiona lly, you m a y wa nt to write progra m s tha t
inte ra c t with othe r c om pute rs, e ithe r ove r a loc a l ne twork or the Inte rne t. In Com m on L isp, the se kinds of inte ra c tions ha ppe n
through stre a m s.
Stre ams a re da ta type s in Com m on L isp tha t a llow you to ta ke som e e xte rna l re sourc e a nd m a ke it look like just a nothe r sim ple
pie c e of da ta you c a n m a nipula te with your c ode . T he e xte rna l re sourc e c ould be a va rie ty of things: a file on a disk, a nothe r
c om pute r on a ne twork, or te xt in a c onsole window on the sc re e n. As you’ll le a rn in this c ha pte r, through the use of a stre a m , a
L isp progra m c a n inte ra c t with this outside re sourc e just a s e a sily a s it m ight inte ra c t with a list or a ha sh ta ble .
Type s of Str e ams
W he n we c om m unic a te with a n e xte rna l re sourc e from a Com m on L isp progra m , we do so by using a stre a m . Diffe re nt type s of
stre a m s a re a va ila ble for diffe re nt type s of re sourc e s. Anothe r fa c tor is the dire c tion of the stre a m — som e tim e s you will wa nt to
write da ta to a re sourc e , a nd som e tim e s you will wa nt to re a d da ta from a re sourc e .
Str e ams by Type of Re sour c e

W he n orga niz e d by the type of re sourc e on whic h the y ope ra te , the following a re the m ost c om m only use d stre a m type s: Console
str e ams
W ha t we ’ve be e n using so fa r whe n c om m unic a ting with the RE PL .
F ile str e ams
L e t us re a d a nd write to file s on our ha rd drive .
Soc ke t str e ams
L e t us c om m unic a te with othe r c om pute rs on a ne twork.
Str ing str e ams
L e t us se nd a nd re c e ive te xt from a L isp string.
Of the se stre a m type s, string stre a m s a re the bla c k she e p of the fa m ily. Ra the r tha n le tting you c om m unic a te with the outside
world, string stre a m s a llow you to m a nipula te strings in ne w a nd inte re sting wa ys.
Str e ams by Dir e c tion
W he n you write da ta to a re sourc e , you use output stre ams. For re a ding da ta from a re sourc e , you use input stre ams.

O utput Str e ams

Output stre a m s a re use d for ta sks suc h a s writing to the RE PL , writing to a file , or se nding inform a tion ove r a soc ke t. At the m ost
prim itive le ve l, you c a n do two things with a n output stre a m :

Che c k whe the r the stre a m is va lid.


Push a ne w ite m onto the stre a m .
As you c a n se e , a stre a m is m ore re stric tive tha n a true da ta struc ture in L isp. For insta nc e , a list supports a ll of the sa m e
fe a ture s a s a stre a m (we c a n push a ne w ite m onto a list with push a nd c he c k if a list is va lid with listp), a nd we a lso c a n do
c e rta in ta sks with a list tha t we c a n’t do with a n output stre a m (suc h a s c ha nging ite m s in the list with setf). But this lim ite d
func tiona lity of stre a m s a c tua lly m a ke s the m use ful in m a ny c a se s.
T o se e if we ha ve a va lid output stre a m , we c a n use the output-stream-p func tion. For e xa m ple , the RE PL ha s a n output stre a m
a ssoc ia te d with it c a lle d *standard-output*. W e c a n se e if this is a va lid output stre a m with the following c ode :
> (output-stream-p *standard-output*)
T
A L isp c ha ra c te r is one ite m tha t c a n be pushe d onto a n output stre a m using the ba sic c om m a nd write-char. For e xa m ple , to
write the c ha ra c te r #\x to the *standard-output* stre a m , we c a n run the following c om m a nd:
> (write-char #\x *standard-output*)
xNIL
T his c ode prints a n x to the sta nda rd output (whic h, in this c a se , is the sa m e a s the RE PL ). Note tha t this func tion a lso re turns
nil, c a using the x a nd the re turn va lue to be printe d on the sa m e line . As you sa w in Cha pte r 6, this e xtra nil is just a side e ffe c t
of running the c ode in the RE PL . If we ra n this c om m a nd a s pa rt of a la rge r progra m , only the x would ha ve printe d out.

Note

In this c ha pte r, we ’ll disc uss only stre a m s ba se d on te xt c ha ra c te rs. In Com m on L isp, you c a n a lso c re a te stre a m s ba se d on othe r
da ta type s. For insta nc e , if you’re working with bina ry da ta , you m a y wa nt to se nd or re c e ive ra w byte s inste a d of c ha ra c te rs. But
for our purpose s, m a nipula ting te xtua l da ta (a nd he nc e using stre a m s tha t work with te xt c ha ra c te rs) is the m ost c onve nie nt.

Input Str e ams

Input stre a m s a re use d for re a ding da ta . As with output stre a m s, the a c tions tha t you c a n pe rform with a n input stre a m a re
lim ite d. At the m ost prim itive le ve l, you c a n do two things with a n input stre a m :

Che c k whe the r the stre a m is va lid.


Pop a n ite m off of the stre a m .

W e c a n se e if we ha ve a va lid stre a m with the input-stream-p c om m a nd. For insta nc e , a s with sta nda rd output, the RE PL ha s
a n a ssoc ia te d input stre a m c a lle d *standard-input*, whic h we c a n va lida te a s follows:
> (input-stream-p *standard-input*)
T
W e c a n pop a n ite m off the stre a m with the read-char c om m a nd. Sinc e we ’re re a ding from the RE PL , we ne e d to type som e
c ha ra c te rs a nd pre ss e nte r to se nd the da ta into the sta nda rd input stre a m :
> (read-char *standard-input*)
123
#\1
As you c a n se e , the 1 a t the front of the stre a m wa s poppe d off a nd re turne d by read-char.

USING O TH ER CO M M ANDS TO INTERACT W ITH STREAM S

In a ddition to write-char a nd read-char, Com m on L isp ha s m a ny othe r c om m a nds for inte ra c ting with stre a m s. In fa c t, a ll the
c om m a nds for printing a nd re a ding introduc e d in Cha pte r 6 c a n a c c e pt a stre a m a s a n e xtra pa ra m e te r, whic h le ts us use L isp's
powe rful input/output a bilitie s with a ny stre a m . For insta nc e , we c a n e xplic itly te ll the print c om m a nd to print to *standard-
output*, a s follows:
> (print 'foo *standard-output*)
FOO
T his c a n be use ful whe n working with stre a m s othe r tha n *standard-output*, a s you'll se e shortly.
W or king with F ile s
In a ddition to using stre a m s to write to a nd re a d from the RE PL , we c a n a lso use stre a m s to write to a nd re a d from file s.
You c a n c re a te a file stre a m in Com m on L isp in se ve ra l wa ys. T he be st wa y is to use the with-open-file c om m a nd. As you’ll
se e shortly, this c om m a nd c onta ins spe c ia l bug-pre ve ntion fe a ture s tha t m a ke it sa fe r to use tha n othe r a va ila ble file c om m a nds.
T he following e xa m ple use s with-open-file to write the string "my data" to a file na m e d data.txt:

> (with-open-file ( my-stream "data.txt" :direction :output)

(print "my data" my-stream))

In this e xa m ple , the with-open-file c om m a nd binds the output stre a m to the na m e my-stream . T his c a use s a file output
stre a m to be c re a te d with the na m e my-stream. T his stre a m will be a va ila ble within the body of the with-open-file c om m a nd

(until the fina l c losing bra c ke t ), a nd a ny da ta we se nd to this stre a m will e nd up in the file na m e d data.txt on the disk. T he

print c om m a nd re fe re nc e s my-stream a s the de stina tion for its output . T he re fore , a fte r running this e xa m ple , you should find
a ne w file na m e d data.txt in the folde r from whic h you la unc he d CL ISP. T his file ha s the te xt "my data" a s its c onte nt.
Spe c ifying :output a s the dire c tion for with-open-file c re a te s a n output stre a m . T o m a ke this a n input stre a m inste a d, we
c ould c ha nge the dire c tion to :input, a s follows:
> (with-open-file (my-stream "data.txt" :direction :input)
(read my-stream))
"my data"
As you c a n se e , this c a use s the da ta — the sa m e da ta writte n to the file in the pre vious e xa m ple — to be re a d in from the file .
As you le a rne d in Cha pte r 6, the print a nd read c om m a nds c a n print a nd re a d a ny of the ba sic Com m on L isp da ta type s. T his
func tiona lity m a ke s it e a sy to use stre a m s to store da ta from your progra m s to the ha rd drive . He re is a m ore c om plic a te d e xa m ple
tha t write s a n a ssoc ia tion list (a list) to a file :

> (let ((animal-noises '((dog . woof)


(cat . meow))))

(with-open-file (my-stream "animal-noises.txt" :direction :output)


(print animal-noises my-stream)))
((DOG . WOOF) (CAT . MEOW))

> (with-open-file (my-stream "animal-noises.txt" :direction :input)


(read my-stream))
((DOG . WOOF) (CAT . MEOW))
In this e xa m ple , we ’re c re a ting a n a ssoc ia tion ta ble of a nim a ls a nd the sounds the y m a ke . W e c re a te a ne w a list na m e d animal-

noises . W e put ke ys for dog a nd cat into this list. Now we c a n write this a list to a ne w file c a lle d animal-noises.txt

. L a te r, we c a n e a sily re c onstitute this a list from the file .


T he with-open-file c om m a nd c a n ta ke ke yword pa ra m e te rs tha t m odify its be ha vior. For insta nc e , you c a n te ll the c om m a nd
wha t to do if a file with the give n na m e a lre a dy e xists. In the following e xa m ple , we ’ll displa y a n e rror m e ssa ge using the :if-
exists ke yword pa ra m e te r:
> (with-open-file (my-stream "data.txt" :direction :output :if-exists :error)
(print "my data" my-stream))
*** - OPEN: file #P"/home/user/data.txt" already exists
Alte rna tive ly, you m a y sim ply wa nt the e xisting file to be ove rwritte n. In tha t c a se , se t the :if-exists ke yword pa ra m e te r to
:supersede, a s follows:
> (with-open-file (my-stream "data.txt" :direction :output
:if-exists :supersede)
(print "my data" my-stream))
"my data"
T he with-open-file c om m a nd give s you a ve ry suc c inc t wa y to work with file s. Unlike m ost progra m m ing la ngua ge s, whe n
using this c om m a nd, you don’t ne e d to ope n a nd c lose file s m a nua lly, a nd you don’t ne e d to worry a bout pote ntia lly m e ssing up
your file s by fa iling to prope rly c lose the m . (Ac tua lly, Com m on L isp ha s lowe r-le ve l c om m a nds for ope ning a nd c losing file s a s
we ll, but with-open-file pa c ka ge s the m in a c le a n wa y tha t hide s a ll the ugly de ta ils. )
T he m a in purpose of with-open-file is to a c quire a file re sourc e . It ta ke s c om m a nd of the file a nd a ssum e s the re sponsibility of
c losing it. In fa c t, e ve n if the c ode inside the with-open-file throws a n ugly e rror tha t stops the progra m de a d, with-open-file
will still c lose the file prope rly to m a ke sure this re sourc e sta ys inta c t.

Note

Com m on L isp ha s m a ny c om m a nds tha t be gin with with- tha t will sa fe ly a lloc a te re sourc e s in this wa y. T he se with- c om m a nds,
a va ila ble in the c ore L isp libra rie s, a re built with L isp’s a we som e m a c ro syste m . You’ll le a rn m ore a bout L isp m a c ros, a nd how to
c re a te your own with- c om m a nds, in Cha pte r 16.
W or king with Soc ke ts

Now tha t we ’ve use d stre a m s to c om m unic a te with the RE PL a nd with file s, le t’s se e how we c a n use the m to c om m unic a te with
a nothe r c om pute r.
If you wa nt to write a progra m tha t c a n c om m unic a te with a nothe r c om pute r e lse whe re on a sta nda rd ne twork (a lm ost a ll
ne tworks nowa da ys use the T CP/IP protoc ol), you’ll first ne e d to c re a te a soc ke t. A soc k e t is a m e c ha nism for routing da ta ove r a
c om pute r ne twork be twe e n progra m s running on diffe re nt c om pute rs on tha t ne twork.
Unfortuna te ly, soc ke ts didn’t m a ke it into the ANSI Com m on L isp sta nda rd, whic h m e a ns the re ’s no sta nda rd wa y of inte ra c ting
with soc ke ts a t this tim e . Howe ve r, e ve ry ve rsion of Com m on L isp supports soc ke ts, e ve n if it doe sn’t follow a ny sta nda rd. Sinc e
we ’ve be e n using CL ISP a s our L isp of c hoic e in this book, we ’ll c onside r only CL ISP’s soc ke t c om m a nds.

Note

c l-soc ke ts (http://c om m on-lisp. ne t/proje c t/c l-soc ke ts/) a nd usoc ke t (http://c om m on-lisp. ne t/proje c t/usoc ke t/) a re two a tte m pts a t
a dding a sta nda rd soc ke t libra ry to Com m on L isp.
Soc ke t Addr e sse s
E ve ry soc ke t within a ne twork m ust ha ve a soc k e t addre ss. T his soc ke t a ddre ss ha s two c om pone nts: IP addr e ss
A num be r tha t unique ly ide ntifie s a c om pute r on the ne twork (typic a lly shown a s 4 byte s de lim ite d by pe riods, suc h a s
192. 168. 33. 22).
P or t numbe r
Any progra m s tha t wa nt to use the ne twork m ust c hoose a unique port num be r tha t no othe r progra m on the sa m e c om pute r is
a lre a dy using.
T he IP a ddre ss a nd the port num be r c om bine to m a ke up the soc ke t a ddre ss. Sinc e the IP a ddre ss is unique on a ne twork a nd the
port num be r is unique for a give n c om pute r, e ve ry soc ke t a ddre ss on a ne twork is unique to a spe c ific progra m running on a spe c ific
c om pute r. Any m e ssa ge s running ove r the ne twork (through c hunks of da ta c a lle d TCP pac k e ts) will be la be le d with a soc ke t
a ddre ss to indic a te the ir de stina tion.
Onc e a c om pute r re c e ive s a pa c ke t la be le d with its IP a ddre ss, the ope ra ting syste m will look a t the port num be r in the soc ke t
a ddre ss of the m e ssa ge to figure out whic h progra m should re c e ive the m e ssa ge .
And how doe s the ope ra ting syste m know whic h progra m re c e ive s m e ssa ge s for a give n port? It knows this be c a use a progra m first
m ust c re a te a soc ke t for tha t port in orde r to use it. In othe r words, a soc ke t is sim ply a wa y for a c om pute r progra m to te ll the
ope ra ting syste m , “ He y, if you ge t a ny m e ssa ge s on port 251, se nd the m m y wa y!”
Soc ke t Conne c tions
In orde r to a c tua lly se nd a m e ssa ge ove r a soc ke t be twe e n two progra m s, we first ne e d to follow som e ste ps to initia liz e a soc k e t
c onne c tion. T he first ste p in c re a ting suc h a c onne c tion is to ha ve one of the progra m s c re a te a soc ke t tha t sta rts in a liste ning
sta te , wa iting to se e if othe r progra m s on the ne twork wa nt to sta rt a c om m unic a tion. T he c om pute r with the soc ke t in a liste ning
sta te is c a lle d the se rv e r. T he n the othe r progra m , c a lle d a c lie nt, c re a te s a soc ke t on its e nd a nd use s it to e sta blish a c onne c tion
with the se rve r. If a ll goe s we ll, the se two progra m s c a n now tra nsm it m e ssa ge s a c ross the soc ke t c onne c tion running be twe e n the m .
But e nough ta lk. L e t’s try c onne c ting two progra m s right now to se e the m a gic ha ppe n for ourse lve s!
Se nding a M e ssage ove r a Soc ke t
First, ope n two c opie s of CL ISP in two diffe re nt c onsole windows on your c om pute r. W e ’ll c a ll one the c lie nt a nd one the se rve r.
(Or, if you ha ve two c om pute rs on a ne twork a nd know the ir IP a ddre sse s, you c a n c re a te the two c onsole s on two se pa ra te
m a c hine s, for the full ne twork e xpe rie nc e . )

Note

You must use CL ISP to ge t the soc ke t c ode shown in this c ha pte r to run.

On the se rve r, ta ke c ontrol of a port by c a lling socket-server:


> (defparameter my-socket (socket-server 4321)) ;ON THE SERVER
MY-SOCKET
T his c om m a nd a c quire s port 4321 a nd binds a soc ke t to it using the ope ra ting syste m . T he soc ke t is bound to the my-socket
va ria ble so tha t we c a n inte ra c t with it.
T his c om m a nd is som e wha t da nge rous, be c a use the ope ra ting syste m is e xpe c ting us to give up the soc ke t onc e we ’re finishe d
with it. If we don’t, no one will be a ble to use this soc ke t a nym ore . In fa c t, if you m a ke a ny m ista ke s during this soc ke t e xe rc ise ,
you c ould m e ss up the soc ke t a t port 4321, a nd the n you would ne e d to switc h to a nothe r port num be r until you re sta rt your
c om pute r. (In the ne xt c ha pte r, you’ll le a rn how to use the e xc e ption ha ndling syste m in Com m on L isp to work a round the se ugly
proble m s. )
Ne xt, le t’s m a ke a stre a m from this soc ke t (still on the se rve r) tha t ha ndle s a c onne c tion from a single c lie nt:
> (defparameter my-stream (socket-accept my-socket)) ;ON THE SERVER
Afte r running this c om m a nd, the se rve r will se e m to loc k up, a nd you won’t be re turne d to the RE PL prom pt. Don’t be a la rm e d
— the socket-accept c om m a nd is a bloc k ing ope ration, whic h m e a ns the func tion won’t e xit until a c lie nt ha s c onne c te d.
Now switc h ove r to your c lie nt CL ISP a nd use the socket-connect c om m a nd to c onne c t to tha t soc ke t on the se rve r:
> (defparameter my-stream (socket-connect 4321 "127.0.0.1")) ;ON THE CLIENT
MY-STREAM
T he IP a ddre ss 127. 0. 0. 1 is a spe c ia l a ddre ss tha t a lwa ys points to the c om pute r from whic h it’s c a lle d. If you a re using two
diffe re nt c om pute rs for this e xe rc ise , you should e nte r the a c tua l IP a ddre ss of your se rve r.
Afte r running this c om m a nd, the se rve r will unloc k, a nd the va lue of the my-stream va ria ble will be se t. W e now ha ve a stre a m
ope n in both c opie s of CL ISP, a nd we c a n use it to c om m unic a te be twe e n the m !
T he stre a m CL ISP ha s c re a te d he re is c a lle d a bidire c tional stre a m . T his m e a ns it c a n a c t both a s a n input stre a m a nd a n output
stre a m , a nd we c a n use e ithe r se t of c om m a nds on it to c om m unic a te in both dire c tions. L e t’s se nd a c ordia l gre e ting be twe e n the
c lie nt a nd the se rve r.
E nte r the following on the c lie nt:
> (print "Yo Server!" my-stream)
"Yo Server!"
And e nte r the following on the se rve r:
> (read my-stream)
"Yo Server!"
T he n, still on the se rve r, e nte r this:
> (print "What up, Client!" my-stream)
"What up, Client!"
Ba c k on the c lie nt, run this c om m a nd:
> (read my-stream)
"What up, Client!"
He re ’s wha t your two CL ISP windows should look like whe n you’re finishe d:
T he m e ssa ge we se nt a c ross the soc ke t wa s a L isp string, but be c a use of L isp’s e le ga nt stre a m -ha ndling c a pa bilitie s, we c ould
se nd a lm ost a ny sta nda rd L isp da ta struc ture in the sa m e wa y, without a ny e xtra e ffort!
Tidying Up Afte r O ur se lve s
It’s c ruc ia l tha t we fre e up the re sourc e s we ’ve c re a te d during this e xe rc ise . First, run the following c om m a nd on both the c lie nt
a nd the se rve r to c lose the stre a m on both e nds:
> (close my-stream)
T
Ne xt, run socket-server-close on the se rve r to fre e up the port a nd disc onne c t the soc ke t from it. If you don’t, port 4321 will
be unusa ble until you re boot.
> (socket-server-close my-socket)
NIL
Str ing Str e ams: The O ddball Type
Stre a m s a re usua lly use d for c om m unic a ting with the outside world from within a L isp progra m . One e xc e ption to this is the
string stre a m , whic h sim ply m a ke s a string look like a stre a m . In the sa m e wa y you c a n re a d or write to e xte rna l re sourc e s with
othe r type s of stre a m s, a string stre a m will le t you re a d or write to a string.
You c a n c re a te string stre a m s with the make-string-output-stream a nd make-string-input-stream c om m a nds. Following is a n
e xa m ple tha t use s make-string-output-stream:
> (defparameter foo (make-string-output-stream))
> (princ "This will go into foo. " foo)
> (princ "This will also go into foo. " foo)
> (get-output-stream-string foo)
"This will go into foo. This will also go into foo. "
You m a y be wonde ring why a nyone would wa nt to do this, sinc e we c a n a lre a dy dire c tly m a nipula te strings in L isp, without using
stre a m s. Ac tua lly, the re a re se ve ra l good re a sons for using string stre a m s in this wa y. T he y a re use ful for de bugging, a s we ll a s for
c re a ting c om ple x strings e ffic ie ntly.
Se nding Str e ams to F unc tions
Using string stre a m s a llows us to use func tions tha t re quire stre a m s a s pa ra m e te rs. T his is gre a t for de bugging c ode tha t works
with file s or soc ke ts, using only strings for the input a nd output of da ta .
For e xa m ple , suppose we ha ve a func tion write-to-log tha t write s log inform a tion to a stre a m . Usua lly, we would wa nt to se nd
the log inform a tion to a file stre a m , so it c a n be writte n to a file for sa fe ke e ping. Howe ve r, if we wa nt to de bug the func tion, we
m a y wa nt to se nd it a string stre a m inste a d, so we c a n ta ke a look a t the da ta it write s a nd m a ke sure it’s c orre c t. If we ha d ha rd-
c ode d the write-to-log func tion to only write to a file , we wouldn’t ha ve this fle xibility. T his is why it m a ke s se nse to write
func tions to use the a bstra c t c onc e pt of a stre a m whe ne ve r possible , inste a d of using othe r m e thods to a c c e ss e xte rna l re sourc e s.
W or king with Long Str ings
String stre a m s c a n le a d to be tte r-pe rform ing c ode whe n de a ling with ve ry long strings. For insta nc e , c onc a te na ting two strings
toge the r c a n be a c ostly ope ra tion— first, it re quire s a ne w bloc k of m e m ory to be a lloc a te d to hold both strings, a nd the n the
strings ne e d to be c opie d into this ne w loc a tion. Be c a use of this bottle ne c k, m a ny progra m m ing la ngua ge s use de vic e s c a lle d string
builde rs to a void this ove rhe a d. In L isp, we c a n ge t sim ila r pe rform a nc e be ne fits by using string stre a m s.
Re ading and De bugging
Anothe r re a son for using string stre a m s is tha t the y c a n m a ke our c ode e a sie r to re a d a nd de bug, e spe c ia lly whe n we use the
with-output-to-string m a c ro.

He re ’s a n e xa m ple of this c om m a nd be ing use d:

> (with-output-to-string (*standard-output*)

(princ "the sum of ")


(princ 5)
(princ " and ")
(princ 2)
(princ " is ")
(princ (+ 2 5)))

"the sum of 5 and 2 is 7"

T he with-output-to-string m a c ro will inte rc e pt a ny te xt tha t would othe rwise be output to the c onsole , RE PL , or othe r

output stre a m , a nd c a pture it a s a string. In the pre c e ding e xa m ple , the output c re a te d by the princ func tions within the body
of the with-output-to-string c a ll is re dire c te d a utom a tic a lly into a string stre a m . Onc e the body of the with-output-to-string

c om m a nd ha s c om ple te d, the e ntire printe d output tha t wa s put into the stre a m is re turne d a s a re sult .
You c a n a lso use the with-output-to-string m a c ro to e a sily c onstruc t c om ple x strings by “ printing” e a c h pa rt, a nd the n
c a pturing the re sult a s a string. T his te nds to be m uc h m ore e le ga nt a nd e ffic ie nt tha n using the concatenate c om m a nd.

Note

Using with-output-to-string runs c ounte r to the te ne ts of func tiona l progra m m ing (disc usse d in Cha pte r 14). Som e L ispe rs
c onside r this func tion (a nd sim ila r func tions tha t inte rc e pt input or output inte nde d for othe r de stina tions) to be a n ugly ha c k.
You’ll se e som e disa gre e m e nt in the L isp c om m unity a bout whe the r the use of with-output-to-string is e le ga nt or ugly.
W hat You've Le ar ne d
T his c ha pte r de sc ribe d how to use stre a m s to le t your L isp progra m s inte ra c t with outside re sourc e s. You le a rne d the following:
Diffe re nt type s of stre a m s inte ra c t with diffe re nt type s of re sourc e s. T he se inc lude c onsole stre ams, file stre ams, soc k e t stre ams,
a nd string stre ams.
Stre a m s c a n be c a te goriz e d ba se d on the ir dire c tion. Output stre ams le t us write to a re sourc e . Input stre ams le t us re a d from a
re sourc e .
Soc ke t stre a m s a llow c om pute r progra m s to c om m unic a te ove r a ne twork. T o e sta blish a soc ke t stre a m , we m ust first ope n
soc ke ts on both e nds a nd ope n a soc ke t c onne c tion be twe e n the progra m s.
String stre a m s a llow us to use func tions tha t re quire stre a m s without linking to a n outside re sourc e , for de bugging purpose s.
T he y a lso a re use ful for c onstruc ting c om ple x strings e ffic ie ntly a nd e le ga ntly through the use of with-output-to-string.
Chapte r 13. Le t's Cr e ate a W e b Se r ve r !
In Cha pte r 6, you le a rne d how to inte ra c t with a use r by se nding te xt to a nd from the RE PL . Howe ve r, whe n pe ople ta lk a bout
“ inte ra c ting with a use r” the se da ys, the y’re usua lly re fe rring to a use r on the W e b. In this c ha pte r, you’re going to le a rn how to
inte ra c t with use rs on the W e b by building a we b se rve r from sc ra tc h. Sinc e c om m unic a tions ove r a ne twork a re e rror prone by
the ir na ture , you’ll first le a rn how e rrors a re ha ndle d in L isp.
Er r or H andling in Common Lisp
Any tim e you’re inte ra c ting with the outside world, a s our we b se rve r will, une xpe c te d things c a n ha ppe n. No m a tte r how sm a rt a
m ode rn c om pute r ne twork m a y be , it c a n ne ve r a ntic ipa te e ve ry possible e xc e ptiona l situa tion. Afte r a ll, e ve n the sm a rte st
ne twork c a n’t re c ove r from som e fool tripping ove r the wrong c a ble .
Com m on L isp ha s a ve ry e xte nsive se t of fe a ture s for de a ling with une xpe c te d e xc e ptiona l situa tions in your c ode . T his
e xc e ption ha ndling syste m is ve ry fle xible , a nd it c a n be use d to do things tha t a re im possible with e xc e ption syste m s in m ost othe r
la ngua ge s.
Signaling a Condition
If you’re writing a func tion a nd som e thing goe s horribly wrong, a L isp func tion c a n notify the L isp e nvironm e nt tha t a proble m
ha s be e n e nc ounte re d. T his is done by signaling a c ondition. W ha t sort of things c ould go wrong? Ma ybe a func tion trie d to divide
by z e ro. Or m a ybe a libra ry func tion re c e ive d a pa ra m e te r of the wrong type . Or m a ybe a soc ke t c om m unic a tion wa s inte rrupte d
be c a use you trippe d ove r your ne twork c a ble .
If you wa nt to signa l a c ondition dire c tly, you c a n do so with the error c om m a nd. You would do this if a func tion you wrote
de te c te d a proble m on its own— a proble m so se rious the progra m just c ould not c ontinue norm a lly. Using the error c om m a nd will
inte rrupt your running L isp progra m , unle ss you inte rc e pt the e rror e lse whe re to pre ve nt a n inte rruption. L e t’s signa l a c ondition
a nd print the m e ssa ge “ foo” to de sc ribe the e rror:
> (error "foo")

*** - foo
The following restarts are available:
ABORT :R1 Abort main loop
>
As you c a n se e , signa ling this c ondition c a use s L isp to inte rrupt our progra m , print the m e ssa ge “ foo, ” a nd show a n e rror prom pt
a t the RE PL . (In CL ISP, you c a n type :a a t this point to a bort the progra m a nd re turn to the norm a l RE PL . ) Most of the tim e your
progra m signa ls a c ondition, it will proba bly not be be c a use you c a lle d error yourse lf. Inste a d, it will be be c a use your progra m ha s
a bug, or be c a use you c a lle d a libra ry func tion, a nd tha t func tion signa ls a c ondition. Howe ve r, a ny tim e som e thing pre ve nts
norm a l e xe c ution in your progra m , le a ding to a c ondition, your progra m will stop a nd show a n e rror prom pt suc h a s in the
pre c e ding e xa m ple .
Cr e ating Custom Conditions
In our first e xa m ple , we pa sse d a string de sc ribing the c ondition to the error c om m a nd. Howe ve r, this te xt string just c ustom iz e s
the e rror m e ssa ge a nd doe sn’t le a d to a diffe re nt “ type ” of c ondition. Com m on L isp a lso a llows you to ha ve va rious type s of
c onditions tha t c a n be ha ndle d in diffe re nt wa ys.
A m ore sophistic a te d wa y to signa l c onditions is to first de fine a c ustom c ondition using define-condition, a s in the following
e xa m ple :

(define-condition foo () ()

(:report (lambda (condition stream)

(princ "Stop FOOing around, numbskull!" stream))))

T his is a typic a l e xa m ple of c re a ting a ne w type of c ondition, whic h we ’ve na m e d foo . W he n this c ondition is signa le d,

we c a n supply a c ustom func tion tha t will be c a lle d to re port the e rror. He re , we de c la re a la m bda func tion for this purpose .

W ithin the la m bda func tion, we print a c ustom m e ssa ge to re port the e rror .
L e t’s se e wha t ha ppe ns whe n we trigge r this ne w c ondition:
> (error 'foo)
*** - Stop FOOing around, numbskull!
The following restarts are available:
ABORT :R1 Abort main loop
>
As you c a n se e , our c ustom m e ssa ge wa s printe d. T his te c hnique a llows the progra m m e r to ge t a m ore m e a ningful e rror re port,
c ustom iz e d for the spe c ific c ondition tha t wa s trigge re d.
Inte r c e pting Conditions
W he n we c re a te a c ondition with define-condition, it’s give n a na m e (suc h a s foo). T his na m e c a n be use d by the highe r-le ve l
pa rts of our progra m to inte rc e pt a nd ha ndle tha t c ondition, so it won’t stop the progra m ’s e xe c ution. W e c a n do this with the
handler-case c om m a nd, a s follows:
> (defun bad-function ()
(error 'foo))
BAD-FUNCTION

> (handler-case (bad-function)

(foo () "somebody signaled foo!")


(bar () "somebody signaled bar!"))

"somebody signaled foo!"


T he first thing we put inside a handler-case c om m a nd is the pie c e of c ode tha t m a y signa l c onditions tha t we wa nt to ha ndle

.
In this e xa m ple , the c ode we ’re wa tc hing is a c a ll to bad-function. T he re st of handler-case le ts us spe c ify a c tions to pe rform

if a pa rtic ula r c ondition oc c urs . W he n this c ode is run, bad-function signa ls the foo c ondition by c a lling (error 'foo).
Usua lly, this would c a use our progra m to be inte rrupte d a nd le a d to a e rror prom pt a t the RE PL . Howe ve r, our handler-case

c om m a nd inte rc e pts the foo c ondition . T his m e a ns tha t the progra m c a n ke e p running without inte rruption, with the

handler-case e va lua ting a s “ som e body signa le d foo!” .


P r ote c ting Re sour c e s Against Une xpe c te d Conditions
W he n a n une xpe c te d e xc e ption ha ppe ns in a progra m , the re is a lwa ys a risk tha t it c ould bring down your progra m , or e ve n c a use
da m a ge to re sourc e s outside your progra m . E xc e ptions inte rrupt the re gula r flow of your c ode , a nd the y m a y stop your c ode de a d in
its tra c ks, e ve n while it’s in the m iddle of a se nsitive ope ra tion.
For insta nc e , your progra m m a y be writing to a file or to a soc ke t stre a m whe n a n une xpe c te d e xc e ption ha ppe ns. In this c a se , it
is c ritic a lly im porta nt tha t your progra m ha s a n opportunity to c lose the file /soc ke t stre a m a nd fre e the file ha ndle or soc ke t;
othe rwise , tha t re sourc e m a y be c om e loc ke d inde finite ly. If suc h re sourc e s a re n’t c le a ne d up prope rly, the use rs m a y ne e d to re boot
the ir c om pute r first be fore the re sourc e be c om e s a va ila ble a ga in.
T he unwind-protect c om m a nd c a n he lp us to a void the se proble m s. W ith this c om m a nd, we c a n te ll the L isp c om pile r, “ T his
pie c e of c ode m ust run no m a tte r wha t ha ppe ns. ” Conside r the following e xa m ple :

> (unwind-protect (/ 1 0)

(princ "I need to say 'flubyduby' matter what"))


*** - /: division by zero
The following restarts are available:
ABORT :R1 Abort main loop
> :r1
I need to say 'flubyduby' matter what
>

W ithin the unwind-protect, we divide by 0, whic h signa ls a c ondition . But e ve n a fte r we te ll CL ISP to a bort, the

progra m still prints its c ruc ia l m e ssa ge .


W e c a n usua lly a void c a lling unwind-protect dire c tly by re lying on Com m on L isp’s “ with-” m a c ros; m a ny of the se c a ll
unwind-protect the m se lve s, unde r the hood. In Cha pte r 16, we ’ll c re a te our own m a c ros to se e how this is possible .

Note

In the c om ic book e pilogue a t the e nd of the book, you’ll le a rn a bout a n a dditiona l fe a ture of the Com m on L isp signa ling syste m
c a lle d re starts.
W r iting a W e b Se r ve r fr om Sc r atc h
Now tha t you ha ve a ba sic unde rsta nding of soc ke ts (c ove re d in Cha pte r 12) a nd e rror ha ndling, you know e nough to m a ke a we b
se rve r tha t c a n se rve dyna m ic we b pa ge s writte n in L isp. Afte r a ll, why should Apa c he (the world’s m ost popula r we b se rve r) ha ve
a ll the fun?
H ow a W e b Se r ve r W or ks
Hype rte xt T ra nsfe r Protoc ol, or HT T P, is the Inte rne t protoc ol use d for tra nsfe rring we b pa ge s. It a dds a la ye r on top of T CP/IP
for re que sting pa ge s onc e a soc ke t c onne c tion ha s be e n e sta blishe d. W he n a progra m running on a c lie nt c om pute r (usua lly a we b
browse r) se nds a prope rly e nc ode d re que st, the se rve r will re trie ve the re que ste d pa ge a nd se nd it ove r the soc ke t stre a m in
re sponse .

Note

T his we b se rve r is a da pte d from http. lisp, c re a te d by Ron Ga rre t.

For e xa m ple , suppose the c lie nt is the Fire fox we b browse r, a nd it a sks for the pa ge lolc ats. html. T he c lie nt’s re que st m ight look
like this:
GET /lolcats.html HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
For our we b se rve r, the m ost im porta nt pa rt of this re que st is the first line . T he re we c a n se e the type of re que st m a de (a GET
re que st, whic h m e a ns we just wa nt to look a t a pa ge without m odifying it), a nd the na m e of the pa ge re que ste d (lolc ats. html). T his
da ta se nt to the se rve r is c a lle d the re que st he ade r. You’ll se e la te r tha t a dditiona l inform a tion c a n be se nt to the se rve r be low the
re que st he a de r, in a re que st body .

Note

T o re a de rs from the dista nt future , lolc ats wa s a vira l Inte rne t phe nom e non from e a rly in the third m ille nnium . It involve d
pic ture s of c a ts with funny c a ptions. If pe ople of your tim e a re no longe r fa m ilia r with lolc a ts, it is of no gre a t loss.

In re sponse , the se rve r will se nd a n HT ML doc um e nt tha t re pre se nts the we b pa ge ove r the soc ke t stre a m . T his is c a lle d the
re sponse body . He re is wha t a re sponse body m ight look like :
<html>

<body>

Sorry dudez, I don't have any L0LZ for you today :-(
</body>

</html>

An HT ML doc um e nt is wra ppe d in html ope ning a nd c losing ta gs . W ithin the se ta gs, you c a n de c la re a body se c tion

. In the body se c tion, you c a n write a te xt m e ssa ge tha t will be displa ye d in the we b browse r a s the body of the we b pa ge

.
For a fully HT ML -c om plia nt we b pa ge , othe r ite m s m ust e xist in the doc um e nt, suc h a s a DOCT YPE de c la ra tion. Howe ve r, our
e xa m ple will work just fine , a nd we c a n ignore the se te c hnic a l de ta ils for our sim ple de m onstra tion.
A we b se rve r will typic a lly a lso ge ne ra te a re sponse he ade r. T his he a de r c a n give a we b browse r a dditiona l inform a tion a bout the
doc um e nt it ha s just re c e ive d, suc h a s whe the r it is in HT ML or a nothe r form a t. Howe ve r, the sim plifie d we b se rve r we ’re going to
c re a te doe s not ge ne ra te suc h a he a de r a nd inste a d sim ply re turns a body.

Note

Sinc e we ’re using CL ISP-spe c ific soc ke t c om m a nds, you m ust be running CL ISP for the sa m ple we b se rve r pre se nte d in this
c ha pte r to work.
Re que st P ar ame te r s
W e b form s a re a n e sse ntia l e le m e nt in powe ring we bsite s. For insta nc e , suppose we c re a te a sim ple login form for a we bsite .

Afte r the visitor to our we bsite hits the Subm it button on this pa ge , it will se nd a POST re que st ba c k to the we bsite . A POST
re que st looks ve ry sim ila r to the GET re que st in the pre c e ding e xa m ple . Howe ve r, a POST re que st usua lly c a rrie s the e xpe c ta tion
tha t it m a y a lte r da ta on the se rve r.
In our sa m ple login form , we ne e d to te ll the se rve r the use r ID a nd pa ssword tha t the visitor to our site ha d e nte re d into the te xt
fie lds on this form . T he va lue s of the se fie lds tha t a re se nt to the se rve r a s pa rt of the POST re que st a re c a lle d re que st parame te rs.
T he y a re se nt within the POST re que st by a ppe nding the m be low the re que st he a de r, in the a re a tha t m a ke s up the re que st body.
T his is wha t the POST re que st m ight look like for our login e xa m ple :
POST /login.html HTTP/1.1
Host: www.mywebsite.com
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Length: 39

userid=foo&password=supersecretpassword
T he e xtra pa ra m e te r in the he a de r of this POST re que st, Content-Length, indic a te s the le ngth of the pa ra m e te r da ta a t the

bottom of the re que st. Spe c ific a lly, Content-Length: 39 te lls the se rve r tha t the te xt c onta ining the re que st pa ra m e te rs is
39 c ha ra c te rs long.

Re que st P ar ame te r s for G ET Re que sts

As we ’ve disc usse d, the typic a l purpose of re que st pa ra m e te rs is to se nd we b form da ta ba c k to the se rve r during a POST re que st.
Howe ve r, GET re que sts m a y a lso c onta in re que st pa ra m e te rs. Usua lly, with a GET re que st, we wa nt to se e wha t the pa ra m e te rs a re in
the URL of the re que st, whe re a s with a POST re que st, the pa ra m e te rs a re hidde n in the body of the re que st.
For insta nc e , suppose you go to Google a nd se a rc h for “ dogs. ” In this c a se , the follow-up pa ge will ha ve a URL tha t re a ds
som e thing like http://www. google . c om /se a rc h? q=dogs& hl=e n& sa fe =off& . . . . T he se va lue s in the URL (suc h a s the one sta ting tha t
the [q]ue ry=“ dogs” ) a re a lso re que st pa ra m e te rs.
T he we b se rve r we ’re c re a ting will ne e d to give the se rve r c ode a c c e ss to both type s of re que st pa ra m e te rs: the one s in the body
of the re que st (a s is c om m on with POST re que sts) a s we ll a s the one s tha t a ppe a r in the URL (a s is c om m on with GET re que sts. )

De c oding the Value s of Re que st P ar ame te r s

HT T P ha s a spe c ia l wa y to re pre se nt the nona lpha num e ric c ha ra c te rs tha t a use r m ight e nte r into a form , using HTTP e sc ape
c ode s. T he se e sc a pe c ode s le t you ha ve c ha ra c te rs in the va lue s of a re que st pa ra m e te r tha t would not othe rwise be a va ila ble in the
HT T P form a t. For insta nc e , if a use r e nte rs "foo?", it will a ppe a r in the re que st a s "foo%3F", sinc e the que stion m a rk is
re pre se nte d with a n e sc a pe c ode . Our we b se rve r will ne e d to de c ode the se e sc a pe c ha ra c te rs, so the first func tion we ’ll write is
decode-param:

(defun http-char (c1 c2 &optional (default #\Space))

(let ((code (parse-integer


(coerce (list c1 c2) 'string)
:radix 16
:junk-allowed t)))
(if code
(code-char code)
default)))
(defun decode-param (s)

(labels ((f (lst)


(when lst
(case (car lst)
(#\% (cons (http-char (cadr lst) (caddr lst))
(f (cdddr lst))))

(#\+ (cons #\space (f (cdr lst))))

(otherwise (cons (car lst) (f (cdr lst))))))))

(coerce (f (coerce s 'list)) 'string)))

Note

T he HT T P e sc a pe c ode s we a re disc ussing he re a re unre la te d to the e sc a pe c ha ra c te rs in L isp strings we ’ve disc usse d in othe r
pa rts of this book.

First, this func tion de fine s a loc a l func tion na m e d f , whic h we ’ll use to re c ursive ly proc e ss the c ha ra c te rs. T o m a ke this

re c ursion work, we ne e d to use coerce to turn the string into a list of c ha ra c te rs , a nd the n pa ss this list to f.
T he f func tion c he c ks the first c ha ra c te r in the list to se e if it’s a pe rc e nt sign (%) or a plus sign (+). If it’s a pe rc e nt sign, we
know tha t the ne xt va lue in the list is a n ASCII c ode , re pre se nte d a s a he xa de c im a l num be r. (ASCII c ode s a re a sta nda rd se t of
num be rs tha t c orre spond to te xt c ha ra c te rs, sha re d a m ong m a ny c om pute r syste m s a nd a pplic a tions. )

T o de c ode this ASCII c ode , we ’ve c re a te d a func tion na m e d http-char . In this func tion, we use the parse-integer

func tion to c onve rt this string to a n inte ge r . In this c a se , we ’re using som e ke yword pa ra m e te rs on parse-integer: the
:radix pa ra m e te r, whic h te lls the func tion to pa rse a he xa de c im a l num be r, a nd the :junk-allowed pa ra m e te r, whic h te lls it to
just re turn nil whe n a n inva lid num be r is give n, ra the r tha n signa ling a n e rror.
W e the n use the code-char func tion to c onve rt this inte ge r (whic h holds a n ASCII c ode ) into the a c tua l c ha ra c te r tha t the use r
e nte re d.
As pe r the rule s of HT T P e nc oding, if a va lue in a re que st pa ra m e te r c onta ins a plus sign, it should be tra nsla te d into a spa c e

c ha ra c te r. W e m a ke this c onve rsion he re .


Any othe r c ha ra c te r pa sse s through the f func tion unc ha nge d. Howe ve r, we still ne e d to c a ll f on the re m a inde r of the list until

a ll the c ha ra c te rs ha ve be e n proc e sse d .


He re a re som e e xa m ple s of decode-param in a c tion:
> (decode-param "foo")
"foo"
> (decode-param "foo%3F")
"foo?"
> (decode-param "foo+bar")
"foo bar"

De c oding Lists of Re que st P ar ame te r s

T he ne xt thing our se rve r ne e ds to do is to de c ode a list of pa ra m e te rs, whic h will be give n a s na m e /va lue pa irs in a string suc h
a s "name=bob&age=25&gender=male". As we ’ve disc usse d, URL s for we b pa ge s ofte n c onta in suc h na m e /va lue pa irs a t the e nd. As
you c a n se e , this string sa ys tha t the pe rson we ’re looking for on the we b pa ge ha s a na m e of bob, a n a ge of 25, a nd a ge nde r of
m a le . T he se na m e /va lue pa irs a re se pa ra te d by a n a m pe rsa nd (&). T he struc ture of the se strings is e quiva le nt to tha t of a n
a ssoc ia tion list (a list), so we ’ll store the se pa ra m e te rs a s a n a list using the following func tion:
(defun parse-params (s)

(let* ((i1 (position #\= s))


(i2 (position #\& s)))

(cond (i1 (cons (cons (intern (string-upcase (subseq s 0 i1)))

(decode-param (subseq s (1+ i1) i2)))

(and i2 (parse-params (subseq s (1+ i2))))))


((equal s "") nil)
(t s))))
T he parse-params func tion finds the first oc c urre nc e of a n a m pe rsa nd (&) a nd e qua l sign (=) in the string, using the position

func tion . If a na m e /va lue pa ir is found (we know this is true if a n e qua l sign wa s found in the string a nd is store d in i1), we

use the intern func tion to c onve rt the na m e into a L isp sym bol . W e cons this na m e to the va lue of the pa ra m e te r, whic h we

de c ode with our decode-param func tion . Fina lly, we re c ursive ly c a ll parse-params on the re m a inde r of the string .
L e t’s give our ne w parse-params func tion a try:
> (parse-params "name=bob&age=25&gender=male")
((NAME . "bob") (AGE . "25") (GENDER . "male"))
Putting this da ta into a n a list will a llow our c ode to e a sily re fe re nc e a spe c ific va ria ble whe ne ve r tha t’s ne c e ssa ry.

Note

Both decode-param a nd parse-params c ould a c hie ve highe r pe rform a nc e if the y we re writte n using a ta il c a ll, a s we ’ll disc uss in
Cha pte r 14.
P ar sing the Re que st H e ade r
Ne xt, we ’ll write a func tion to proc e ss the first line of the re que st he a de r. (T his is the line tha t will look som e thing like GET
/lolcats.html HTTP/1.1).
T he following parse-url func tion will proc e ss the se strings:
(defun parse-url (s)
(let* ((url (subseq s
(+ 2 (position #\space s))

(position #\space s :from-end t)))

(x (position #\? url)))


(if x

(cons (subseq url 0 x) (parse-params (subseq url (1+ x))))

(cons url '()))))

T his func tion first use s the string’s de lim iting spa c e s to find a nd e xtra c t the URL . It the n c he c ks this URL for a que stion

m a rk, whic h m a y indic a te tha t the re a re re que st pa ra m e te rs tha t ne e d to be ha ndle d . For insta nc e , if the URL is lolc ats. html?
e x tra-funny =y e s, the n the que stion m a rk le ts us know tha t the re is a pa ra m e te r na m e d e x tra-funny in the URL . If suc h pa ra m e te rs

e xist, we ’ll ne e d to e xtra c t the m , a nd the n pa rse the m using our parse-params func tion . If the re a re n’t a ny re que st

pa ra m e te rs, we just re turn the URL . Note tha t this func tion skips ove r the re que st m e thod (m ost ofte n GET or POST). A fa nc ie r
we b se rve r would e xtra c t this da ta point a s we ll.
L e t’s try out our ne w URL e xtra c tor:
> (parse-url "GET /lolcats.html HTTP/1.1")
("lolcats.html")
> (parse-url "GET /lolcats.html?extra-funny=yes HTTP/1.1")
("lolcats.html" (EXTRA-FUNNY . "yes"))
Now tha t we c a n re a d the first line , we ’ll proc e ss the re st of the re que st. T he following get-header func tion will c onve rt the
re m a ining line s of the re que st into a nic e a list:
(defun get-header (stream)

(let* ((s (read-line stream))


(h (let ((i (position #\: s)))
(when i
(cons (intern (string-upcase (subseq s 0 i)))

(subseq s (+ i 2)))))))
(when h

(cons h (get-header stream)))))

T his func tion re a ds in a line from the stre a m , c onve rts it to a ke y/va lue pa ir ba se d on the loc a tion of a c olon , a nd

the n re c urse s to c onve rt a dditiona l line s in the he a de r . If it e nc ounte rs a line tha t doe sn’t c onform to a he a de r line , it m e a ns
we ’ve re a c he d the bla nk line a t the e nd of the he a de r a nd a re finishe d. In this c a se , both i a nd h will be nil, a nd the func tion
te rm ina te s.
T he intern c om m a nd use d whe n ge ne ra ting the ke y a bove is a sim ple func tion tha t c onve rts a string into a sym bol. W e c ould,
inste a d, ha ve use d the read c om m a nd for this purpose , a s we ha ve pre viously in this book. But re m e m be r tha t the fle xibility of the
read c om m a nd a lso m a ke s it a gre a t ta rge t for ha c ke rs, who m ight try c re a ting m a lform e d he a de rs to c ra c k your we b se rve r. T ha t’s
why it’s wise to use the m ore lim ite d, spe c ific intern func tion to proc e ss this da ta se nt ove r the Inte rne t to our we b se rve r.
Te sting ge t-he ade r with a Str ing Str e am
Sinc e the get-header func tion pulls its da ta dire c tly from a soc ke t stre a m , you m ight think we c a n’t te st it dire c tly through the
RE PL . Howe ve r, a s you sa w in the pre vious c ha pte r, the re a re a c tua lly se ve ra l diffe re nt type s of re sourc e s be side s soc ke ts tha t c a n
be a c c e sse d through the stre a m inte rfa c e in Com m on L isp. Be c a use of the c om m on inte rfa c e a m ong stre a m s, we c a n te st our get-
header func tion by pa ssing it a string stre a m inste a d of a soc ke t stre a m :

> (get-header (make-string-input-stream "foo: 1


bar: abc, 123

"))
((FOO . "1") (BAR . "abc, 123"))
Using the make-string-input-stream func tion, we c a n c re a te a n input stre a m from a lite ra l string. In this e xa m ple , we ’re
ta king a string de fining two ke ys (foo a nd bar) a nd e nding it with a n e m pty line , just like a typic a l HT T P he a de r. Note tha t we

ha ve a single lite ra l string from to . Suc h strings a re pe rm itte d in Com m on L isp. As you c a n se e , the get-header
func tion a ppropria te ly pulle d the two ke ys a nd the ir va lue s out of this stre a m , in the sa m e wa y it would pull the se va lue s out of a
soc ke t stre a m .
Using this tric k, you c a n te st func tions tha t m a nipula te stre a m s dire c tly from the RE PL . T o do this, sim ply substitute string
stre a m s for othe r, m ore c om plic a te d stre a m type s.
P ar sing the Re que st Body
In a POST re que st, the re will usua lly be pa ra m e te rs store d be ne a th the he a de r, in a n a re a known a s the re que st body or re que st
c onte nt. T he following get-content-params func tion e xtra c ts the se pa ra m e te rs:
(defun get-content-params (stream header)

(let ((length (cdr (assoc 'content-length header))))

(when length

(let ((content (make-string (parse-integer length))))

(read-sequence content stream)

(parse-params content)))))

First, this func tion se a rc he s the he a de r for a va lue c a lle d content-length , whic h te lls us the le ngth of the string tha t

c onta ins the se c onte nt pa ra m e te rs. If content-length e xists, the n we know the re a re pa ra m e te rs to pa rse . T he func tion will

the n c re a te a string with the give n le ngth using make-string , a nd use read-sequence to fill tha t string with c ha ra c te rs from

the stre a m . It the n runs the re sult through our parse-params func tion to tra nsla te the pa ra m e te rs into our c le a ne d-up a list

form a t .
O ur G r and F inale : The se r ve F unc tion!
Now a ll the pie c e s a re in pla c e to write the he a rt of our we b se rve r: the serve func tion. He re it is in a ll its glory:

(defun serve (request-handler)

(let ((socket (socket-server 8080)))

(unwind-protect

(loop (with-open-stream (stream (socket-accept socket))

(let* ((url (parse-url (read-line stream)))


(path (car url))
(header (get-header stream))
(params (append (cdr url)
(get-content-params stream header)))
(*standard-output* stream))

(funcall request-handler path header params))))


(socket-server-close socket))))

T he serve func tion ta ke s a single pa ra m e te r: request-handler , whic h is supplie d by the c re a tor of a we bsite tha t wa nts to
use this we b se rve r. W he n the se rve r re c e ive s a re que st ove r the ne twork, it pa rse s the re que st into c le a n L isp da ta struc ture s (using
the func tions we ’ve disc usse d throughout this c ha pte r), a nd the n pa sse s this re que st inform a tion to request-handler. T he request-
handler the n displa ys the c orre c t HT ML .

L e t’s look a t our serve func tion in de ta il to se e how it a c c om plishe s this.

First, serve c re a te s a soc ke t bound to port 8080 . T his is one of se ve ra l ports tha t is c om m only use d for se rving we b pa ge s,
e spe c ia lly whe n a site is still unde r de ve lopm e nt. (Port 80 is usua lly use d for a produc tion we bsite /we b se rve r. ) W e the n c a ll

unwind-protect , whic h e nsure s tha t no m a tte r wha t ha ppe ns a s the se rve r runs, socket-server-close will be c a lle d a t som e
point to fre e the soc ke t.

Ne xt, we sta rt the m a in we b-se rving loop. W ithin this loop, we ope n a stre a m for a ny c lie nt tha t a c c e sse s our se rve r . We
the n use the with-open-stream m a c ro to gua ra nte e tha t, no m a tte r wha t, tha t stre a m will be prope rly c lose d. Now we ’re re a dy to
re a d a nd pa rse the we bsite re que st tha t the c lie nt ha s m a de to our se rve r, using a ll of the re a ding a nd pa rsing func tions we c re a te d
.

Fina lly, we c a ll the request-handler func tion, pa ssing in the re que st de ta ils . Note how we re de fine the *standard-
output* dyna m ic va ria ble be fore ha nd. T his m e a ns tha t the re que st ha ndle r c a n just write to sta nda rd output, a nd a ll the printe d
da ta will be re dire c te d to the c lie nt stre a m a utom a tic a lly. As you le a rne d in Cha pte r 12, c a pturing da ta from sta nda rd output
a llows us to m inim iz e string c onc a te na tion. Also, it will m a ke our request-handler func tion e a sie r to de bug, a s you’ll se e shortly.

Note

One thing we did not do with our we b se rve r is pre ve nt the we b se rve r from c ra shing if the request-handler trigge rs a n
e xc e ption. Inste a d, we sim ply gua ra nte e tha t no re sourc e s a re m a ngle d in the c a se of a n e xc e ption. W e c ould e a sily a dd e xtra
e xc e ption ha ndling to ke e p the se rve r tic king e ve n if horrible e xc e ptions oc c ur. Howe ve r, sinc e our goa l is to le a rn L isp a nd
de ve lop ga m e s in a browse r, it’s be tte r for us to know right a wa y a bout a ny e xc e ptions, e ve n if tha t brings down our se rve r.
Building a Dynamic W e bsite
T o try out our shiny ne w we b se rve r, le t’s build a sim ple site tha t gre e ts a visitor, using the dirt-sim ple func tion hello-request-
handler:
(defun hello-request-handler (path header params)

(if (equal path "greeting")

(let ((name (assoc 'name params)))


(if (not name)
(princ "<html><form>What is your name?<input name='name' />

</form></html>")

(format t "<html>Nice to meet you, ˜a!</html>" (cdr name))))

(princ "Sorry... I don't know that page.")))


T h i s hello-request-handler func tion supports only a single we b pa ge , c a lle d greeting. T he first ste p in se rving up this

greeting pa ge is to se e if this pa ge is inde e d wha t the c lie nt re que ste d . If not, we print a n a pology to the use r for not

finding the spe c ifie d pa ge . Othe rwise , we c he c k the re que st pa ra m e te rs to se e if we know the use r’s na m e . If not, we a sk

the use r to e nte r a use rna m e using a we b form . If we do know the use r’s na m e , we gre e t the visitor e nthusia stic a lly .

Note

W e ’re ta king a ton of shortc uts with our we b se rve r a nd this prim itive we bsite . For insta nc e , a ny HT ML se nt to a c lie nt should be
wra ppe d in a prope r HT ML ske le ton, suc h a s <html><body>...</body></html>. Howe ve r, e ve n the n our pa ge wouldn’t be fully
c om plia nt with m ode rn HT ML sta nda rds. In a ddition, whe n a c lie nt re que sts a none xiste nt pa ge , the a ppropria te re sponse is to
displa y a 404 e rror pa ge , not just print a polite a pology. L uc kily, we b browse rs a re ve ry forgiving a bout suc h shortc uts, a nd the y
will displa y our sim plifie d re sponse s a nywa y.
Te sting the Re que st H andle r
Be fore we la unc h our ne w we bsite , le t’s te st our hello-request-handler in the RE PL by first vie wing a pa ge a bout lolc a ts:
> (hello-request-handler "lolcats" '() '())
Sorry... I don't know that page.
Pe rfe c t. As you c a n se e , whe n we a sk our re que st ha ndle r for a pa ge othe r tha n the greeting pa ge , it just prints a n a pology. Now
le t’s try vie wing the c orre c t greeting pa ge :
> (hello-request-handler "greeting" '() '())
<html><form>What is your name?<input name='name' /></form></html>
E xc e lle nt! Our re que st ha ndle r ha s ge ne ra te d a n HT ML form a sking the use r for a use rna m e . Now le t’s pa ss in a pa ra m e te r for
the use r’s na m e , a s if the form ha d be e n proc e sse d a nd se nt to the se rve r:
> (hello-request-handler "greeting" '() '((name . "Bob")))
<html>Nice to meet you, Bob!</html>
Be c a use of the wa y we de signe d our we b se rve r, it’s ve ry sim ple to de bug a re que st ha ndle r inde pe nde ntly in the RE PL . W e we re
a ble to se e tha t hello-request-handler ge ne ra te s the c orre c t re sponse s without a c tua lly firing up a we b browse r.
Launc hing the W e bsite
Now tha t we know tha t our ne w we bsite is func tioning, le t’s la unc h it! But first, we ne e d to m a ke sure tha t a ll of the func tions
disc usse d in this c ha pte r ha ve be e n de fine d in a n insta nc e of CL ISP. If you ha ve n’t be e n e nte ring the se func tions into the RE PL a s
you’ve be e n re a ding, you c a n just sa ve the m a ll into a file c a lle d we bse rv e r. lisp, a nd the n loa d the m with (load "webserve'").
Onc e you’ve de fine d your func tions in the CL ISP, sta rt the se rve r by e nte ring the following into the RE PL :
> (serve #'hello-request-handler)
T ha t’s it! Now you should be a ble to visit the site in a we b browse r:

As you c a n se e , whe n you visit our greeting pa ge from a browse r (using 127. 0. 0. 1:8080, whic h will point to port 8080 on the
sa m e m a c hine the we b browse r is running on), you a re a ske d for your na m e . T he se rve r the n shows a follow-up pa ge , whic h gre e ts
you by na m e . T his shows tha t our we b se rve r wa s a ble to pa rse out the na m e from the re que st pa ra m e te rs, a nd wa s a ble to pa ss the
na m e to our hello-request-handler func tion.
W e now ha ve a fully func tioning we b se rve r a nd re que st ha ndling infra struc ture . In future c ha pte rs, we ’ll use the se tools to c re a te
a n a we som e , gra phic a l, we b-ba se d ga m e .
W hat You've Le ar ne d
In this c ha pte r, you c re a te d a we b se rve r using Com m on L isp, a nd le a rne d the following a long the wa y:
You c a n signa l c onditions in Com m on L isp with the error func tion. You c a n c a tc h suc h e rrors with the handle-case
c om m a nd. If som e c ode a bsolute ly, positive ly ne e ds to be c a lle d no m a tte r wha t e rrors oc c ur, you c a n pla c e this c ode inside the
unwind-protect c om m a nd.
A we b se rve r proc e sse s HT T P re que sts. T he m ost c om m on type of re que st is the GET re que st, use d for vie wing inform a tion.
Anothe r c om m on type is a POST re que st, whic h is use d whe n subm itting we b form s, for insta nc e . You c a n te ll the type of re que st,
whic h pa ge wa s re que ste d, a s we ll a s othe r inform a tion, by looking a t the re que st he ade r. Both GET a nd POST re que sts m a y ha ve
re que st pa ra m e te rs, whic h c a n a ppe a r e ithe r a t the e nd of the re que ste d URL or a t the bottom of the re que st in the re que st body .
Chapte r 13. 5. F unc tional P r ogr amming Is Be autiful
P ar t IV. Lisp is Sc ie nc e
Chapte r 14. Ramping Lisp Up a Notc h with F unc tional P r ogr amming
As you’ve se e n in the pre c e ding c ha pte rs, L isp m a ke s it pre tty e a sy to throw toge the r som e quic k c ode a nd build som e sim ple
ga m e s in no tim e . Howe ve r, L isp’s m a in c la im to fa m e is a s a n a c a de m ic tool, a ppropria te for ta c kling the m ost c om plic a te d
sc ie ntific proble m s. T he fa c t tha t it’s a lso gre a t for ha c king is a rgua bly just a side be ne fit.
In the re st of this book, we ’re going to foc us on the sc ie ntific side of the la ngua ge , e xploring som e a dva nc e d te c hnique s to build
a m ore sophistic a te d ga m e tha t I hope will re a lly blow your m ind. It will do things you m a y ne ve r ha ve thought would be possible
in a c om pute r progra m .
In this c ha pte r, you’re going to le a rn a bout the first a dva nc e d L isp c onc e pt, c a lle d the func tional programming te c hnique . In the
ne xt c ha pte r, we ’ll use this te c hnique to build a sim ple dic e wa rs ga m e , a s we ll a s a c rude a rtific ia lly inte llige nt oppone nt to pla y
a ga inst!
W hat Is F unc tional P r ogr amming?
W e ’ve a lre a dy disc usse d the c onc e pt of func tiona l progra m m ing a bit in e a rlie r c ha pte rs. T he glib a nswe r is tha t func tiona l
progra m m ing is “ a style of progra m m ing whe re we write a ll of our c ode using func tions. ”
Howe ve r, we m e a n som e thing ve ry spe c ific whe n using the te rm func tion in this c onte xt— e xa c tly the sa m e thing tha t
m a the m a tic ia ns m e a n whe n the y use the word func tion. So, wha t do m a the m a tic ia ns m e a n whe n the y use this word?
You proba bly a lre a dy know the a nswe r. T ry to re m e m be r wa y, wa y ba c k whe n you took pre -a lge bra . If you didn’t fa ll a sle e p
during tha t pa rtic ula r le sson, you m ight re m e m be r your te a c he r dra wing som e thing like this on the c ha lkboa rd:

T his pic ture shows tha t a func tion ha s a rgum e nts tha t m a y go into it, c a lle d the domain of the func tion. T he func tion the n ta ke s
the se a rgum e nts a nd re turns a va lue . T his va lue is sa id to fa ll within the range of the func tion.

Note

Som e a dva nc e d L ispe rs will c ringe whe n som e one sa ys tha t a func tion “ re turns a va lue . ” T his is be c a use L isp de rive s from a
som e thing c a lle d the lambda c alc ulus, whic h is a funda m e nta l progra m m ing-like a lge bra de ve lope d ba c k in the 1930s by Alonz o
Churc h. In the la m bda c a lc ulus, you “ run” a progra m by pe rform ing substitution rule s on the sta rting progra m to de te rm ine the
re sult of a func tion. He nc e , the re sult of a se t of func tions just sort of m a gic a lly a ppe a rs by pe rform ing substitutions; ne ve r doe s a
func tion c onsc iously “ de c ide ” to re turn a va lue .
Be c a use of this, L isp purists pre fe r to sa y tha t a func tion “ e va lua te s to a re sult. ” Howe ve r, a lm ost e ve ryone e lse in the
progra m m ing world like s to sa y tha t func tions re turn a va lue . It’s up to you to de c ide whic h wa y of thinking a bout func tions fe e ls
the m ost na tura l.

He re a re som e im porta nt prope rtie s of m a the m a tic a l func tions tha t we ’ll wa nt our L isp func tions to obe y a s we ll:

T he func tion a lwa ys re turns the sa m e re sult, a s long a s the sa m e a rgum e nts a re pa sse d into it. (T his is ofte n
re fe rre d to a s re fe re ntial transpare nc y . )
T he func tion ne ve r re fe re nc e s va ria ble s tha t a re de fine d outside the func tion, unle ss we a re c e rta in tha t the se
va ria ble s will re m a in c onsta nt.
No va ria ble s a re m odifie d (or mutate d, a s func tiona l progra m m e rs like to sa y) by the func tion.
T he purpose of the func tion is to do nothing othe r tha n to re turn a re sult.
T he func tion doe sn’t do a nything tha t is visible to the outside world, suc h a s pop up a dia log box on the sc re e n or
m a ke your c om pute r go “ Bing!”
T he func tion doe sn’t ta ke inform a tion from a n outside sourc e , suc h a s the ke yboa rd or the ha rd drive .

If we obe y the se rule s whe ne ve r possible , we c a n sa y tha t our c ode is writte n in the func tional sty le .
A gre a t e xa m ple of a true m a the m a tic a l func tion is the sine func tion. Sim ila rly, the sin func tion in L isp (whic h c a lc ula te s the
m a the m a tic a l sine ) is a gre a t e xa m ple of a L isp func tion tha t obe ys the rule s of the func tiona l style :
> (sin 0.5)
0.47942555
T he sin func tion a lwa ys re turns the sa m e re sult, a s long a s you a lwa ys pa ss the sa m e a rgum e nt (in this c a se , 0.5) into it. It
doe sn’t do a nything to inte ra c t with the outside world. Its e ntire purpose in life is to re turn the sine a s a va lue . It obe ys a ll the rule s
in the pre c e ding list.
Cle a rly, it would be im possible to write all the c ode in a c om pute r progra m in the func tiona l style . For insta nc e , one of the rule s
stipula te s tha t the c om pute r isn’t a llowe d to go “ Bing!” — who would wa nt to use a c om pute r if it didn’t go “ Bing!” onc e in a
while ?

W he ne ve r a pie c e of c ode doe s som e thing tha t is visible to the outside world, suc h a s go “ Bing!” or displa y a dia log box on the
sc re e n, we sa y tha t the c ode c ause s a side e ffe c t. Func tiona l progra m m e rs think of suc h side e ffe c ts a s m a king your c ode “ dirty. ”
T he te c hnic a l te rm for suc h dirty c ode tha t c onta ins side e ffe c ts is impe rativ e c ode . T he te rm impe rativ e im plie s tha t the c ode is
writte n in a “ c ookbook” style , whe re you ba sic a lly sa y things like “ first do this, a nd the n do tha t. ” L ike a c ookbook, m ost line s in
im pe ra tive c ode pe rform side e ffe c ts, suc h a s writing to the sc re e n or m odifying a globa l va ria ble . Im pe ra tive c ode is the opposite
of func tiona l c ode .
T his le a ds us to the c e ntra l philosophy of func tiona l progra m m ing. It sta te s tha t you should bre a k your progra m into two pa rts:

T he first, a nd bigge st pa rt, should be c om ple te ly func tiona l a nd fre e of side e ffe c ts. T his is the c le a n pa rt of your
progra m .
T he se c ond, sm a lle r pa rt of your progra m is the pa rt tha t ha s a ll the side e ffe c ts, inte ra c ting with the use r a nd the
re st of the outside world. T his c ode is dirty a nd should be ke pt a s sm a ll a s possible .

If a pie c e of c ode pops up a dia log box, for e xa m ple , we de e m it dirty a nd ba nish it to the im pe ra tive se c tion of our c ode .
T hings like dia log boxe s a re not re a lly m a th, a nd we shouldn’t le t the m pla y with our m a th func tions a nd othe r c le a n, func tiona l
c ode .
Anatomy of a P r ogr am W r itte n in the F unc tional Style
Now tha t we ’ve disc usse d how func tiona l progra m m ing is done , le t’s write a sim ple progra m tha t follows this style . Sinc e we
wa nt this progra m to be a typic a l e xa m ple of m ost softwa re , we should figure out wha t m ost softwa re in the world a c tua lly doe s. So
wha t do m ost progra m s in the world a c tua lly do? T he y ke e p tra c k of widge ts!

He re ’s our e ntire e xa m ple progra m , writte n in the func tiona l style :


;the clean, functional part

(defun add-widget (database widget)

(cons widget database))

;the dirty, nonfunctional part

(defparameter *database* nil)

(defun main-loop ()

(loop (princ "Please enter the name of a new widget:")

(setf *database* (add-widget *database* (read)))


(format t "The database contains the following: ˜a˜%" *database*)))
As prom ise d, it is split into two pa rts: the c le an part a nd the dirty part. I did sa y tha t the c le a n pa rt of the progra m should be
m uc h bigge r tha n the dirty pa rt. Howe ve r, sinc e this e xa m ple is so short, the dirty pa rt e nde d up a bit bigge r. Usua lly, you c a n
e xpe c t the c le a n pa rt to be a round 80 pe rc e nt of the a c tua l c ode .

Note

Som e progra m m ing la ngua ge s a re e ve n m ore foc use d on func tiona l progra m m ing tha n L isp is. Ha ske ll, for insta nc e , ha s powe rful
fe a ture s tha t le t you write 99. 9 pe rc e nt of your c ode in a func tiona l style . In the e nd, howe ve r, your progra m will still ne e d to ha ve
som e kind of side e ffe c t; othe rwise , your c ode c ouldn’t a c c om plish a nything use ful.

So wha t doe s our e xa m ple progra m do? W e ll, it ba sic a lly doe s wha t m ost c om pute r progra m s in the world a re de signe d to do: It
ke e ps tra c k of widge ts in a da ta ba se !
T he da ta ba se in this e xa m ple is ve ry prim itive . It’s just a L isp list, store d in the globa l va ria ble *database*. Sinc e the da ta ba se

is going to sta rt off e m pty, we initia liz e this va ria ble a nd se t it to be e m pty .

W e c a n c a ll the func tion main-loop to sta rt tra c king som e widge ts . T his func tion just sta rts a n infinite loop, a sking the

use r for a widge t na m e . T he n, a fte r it re a ds in the widge t, it c a lls the add-widget func tion to a dd the ne w widge t to the

da ta ba se .

Howe ve r, the add-widget func tion is in the c le a n pa rt of the c ode . T ha t m e a ns it’s func tiona l a nd isn’t a llowe d to m odify
the *database* va ria ble dire c tly. L ike a ll func tiona l c ode , the add-widget func tion is a llowe d to do nothing m ore tha n re turn a
ne w va lue . T his m e a ns tha t the only wa y it c a n “ a dd” a widge t to a da ta ba se is to re turn a bra nd-ne w da ta ba se ! It doe s this by
sim ply ta king the da ta ba se pa sse d to it a nd the n c onsing the widge t to the da ta ba se to c re a te a ne w da ta ba se . T he ne w
da ta ba se is ide ntic a l to the pre vious one , e xc e pt tha t it now c onta ins a ne w widge t a t the front of the list.
T hink of how c ra z y this sounds on the fa c e of it. Im a gine tha t we ’re running a n Ora c le da ta ba se se rve r, c onta ining m illions of
widge ts:

T he n, whe n we a dd a ne w widge t, the da ta ba se se rve r a c c om plishe s this by c re a ting a bra nd-ne w re plic a of the pre vious
da ta ba se , whic h diffe rs only in tha t a single ne w ite m ha s be e n a dde d:

T his would be horribly ine ffic ie nt. Howe ve r, in our widge ts e xa m ple , things a re not a s ba d a s the y m a y first a ppe a r. It is true
tha t the add-widgets func tion c re a te s a ne w list of widge ts e ve ry tim e it is c a lle d, a nd tha t re pe a te d c a lls to this func tion would
m a ke the list longe r a nd longe r. Howe ve r, sinc e e ve ry ne w widge t is sim ply a dde d to the front of the list, it turns out tha t the ta il
e nd of the widge t list is ide ntic a l to the pre vious ve rsion of the list. He nc e , the add-widget func tion c a n “ c he a t” whe ne ve r it
c re a te s a ne w list, by sim ply c onsing a single ne w widge t to the front of the list, a nd the n re purposing the old list a s a ta il to hold

the re st of the ite m s . T his a llows the ne w list to be c re a te d in a wa y tha t is fa st a nd a lso re quire s ve ry little ne w m e m ory to
be a lloc a te d. In fa c t, the only ne w m e m ory a lloc a te d by add-widget is a single ne w c ons c e ll to link the ne w widge t to the
pre vious list.
T his type of c he a ting whe n c re a ting ne w da ta struc ture s is a ke y te c hnique tha t m a ke s e ffic ie nt func tiona l progra m m ing possible .
Furthe rm ore , sha ring of struc ture s c a n be done sa fe ly, sinc e one of the te ne ts of func tiona l progra m m ing is to ne ve r m odify old
pie c e s of da ta .
So our add-widget func tion c re a te s a ne w da ta ba se for us with the a dditiona l ite m a dde d to it. T he main-loop func tion, in the
dirty pa rt of the c ode , se ts the globa l *database* va ria ble e qua l to this ne w da ta ba se . In this wa y, we ha ve indire c tly m odifie d the
da ta ba se in two ste ps:

1. T he add-widget func tion, whic h is ba sic a lly the bra ins of this progra m , ge ne ra te d a n upda te d da ta ba se for us.
2. T he main-loop func tion, whic h wa s in c ha rge of the dirty work, m odifie d the globa l *database* va ria ble to
c om ple te the ope ra tion.

T his e xa m ple progra m illustra te s the ba sic la yout of a L isp progra m writte n in the func tiona l style . L e t’s try out our ne w
progra m to se e it in a c tion:
> (main-loop)
Please enter the name of a new widget: Frombulator
The database contains the following: (FROMBULATOR)
Please enter the name of a new widget: Double-Zingomat
The database contains the following: (DOUBLE-ZINGOMAT FROMBULATOR)
...
Re m e m be r tha t you c a n hit c trl-C to e xit the infinite loop in this e xa m ple .
H ighe r -O r de r P r ogr amming
One c om m on stum bling bloc k for progra m m e rs le a rning to write progra m s in the func tiona l style is tha t the y find it ha rd to
c om bine diffe re nt c hunks of c ode to pe rform a single a c tion. T his is c a lle d c ode c omposition. A progra m m ing la ngua ge should
m a ke c ode c om position e a sy. In othe r words, it should m a ke it e a sy for you to ta ke diffe re nt pie c e s of c ode a nd use the m toge the r
to solve a ta sk. T he m ost powe rful tool for c ode c om position whe n writing func tiona l c ode is highe r-orde r programming, whic h le ts
you use func tions tha t a c c e pt othe r func tions a s pa ra m e te rs.
L e t’s look a t a n e xa m ple to unde rsta nd why c ode c om position c a n be a c ha lle nge to a be ginning func tiona l progra m m e r. Suppose
we wa nt to a dd two to e ve ry num be r in the following list:
> (defparameter *my-list* '(4 7 2 3))
*MY-LIST*
T o do this, we will ne e d to write c ode to tra ve rse the list, a s we ll a s write c ode to a dd two to a num be r. T he se a re the two ta sks
we ne e d to c om pose .
Code Composition with Impe r ative Code
One possible na ïve (a nd im pe ra tive ) wa y to pe rform this ta sk is to use a loop:
;For demonstration purposes only. A Lisper would not write code like this.

> (loop for n below (length *my-list*)

do (setf (nth n *my-list*) (+ (nth n *my-list*) 2)))


NIL
> *my-list*
(6 9 4 5)

He re , we ’re c re a ting a va ria ble n tha t c ounts through a ll the ite m s in the list in a loop . W e the n use setf to a dd two to

the num be r a t the loc a tion n in the list . T his is sim ila r to the sort of c ode you m ight write if you we re a C progra m m e r.
Although it’s pre tty ugly, the re a re positive things tha t c a n be sa id a bout it:

Code struc ture d like this is pote ntia lly ve ry e ffic ie nt. It’s spa c e -e ffic ie nt, sinc e we don’t ne e d to a lloc a te a ny
m e m ory for storing a ne w list (we ’re just m unging the old list to inc re a se a ll the num be rs in it by two). And it c ould
a lso be ve ry tim e -e ffic ie nt, if we re wrote this loop to work on a n a rra y inste a d of a list. (Re m e m be r tha t finding the
nth ite m in a list is slow. )

Code writte n like this c le a rly c om pose s the ta sk of looping a nd the ta sk of a dding two to a num be r .
By putting our c ode for the a ddition inside the loop, we a re c om posing the se two a c tivitie s to c om ple te a m ore
c om plic a te d goa l: a dding two to a n e ntire list of num be rs.

Howe ve r, the re a re obvious downside s to the im pe ra tive a pproa c h:


It de stroys the origina l list. T his is a proble m if we use the *my-list* va ria ble la te r, a nd m iss the fa c t tha t this c ode ha s
m e sse d up the origina l va lue s in this list. A L ispe r would sa y tha t a llowing the *my-list* va ria ble to be m odifie d willy-nilly
m a ke s this va ria ble a pie c e of hidde n state in the progra m . Bugs re la te d to hidde n sta te a re c om m on in progra m m ing la ngua ge s
tha t e nc oura ge im pe ra tive -style progra m m ing.

W e ne e de d to c re a te a va ria ble n to ke e p tra c k of our position in the list. T his m a ke s the c ode m ore bulky a nd a lso a dds
m ore pla c e s whe re bugs c ould lurk. T he re ’s a lwa ys a risk tha t we give n a wrong va lue or use it inc orre c tly to a c c e ss ite m s from
the list.
Using the F unc tional Style
Now le t’s se e wha t ha ppe ns if we re write this c ode in a func tiona l style . L e t’s first write it a s a be ginning func tiona l progra m m e r
m ight, without using highe r-orde r progra m m ing:

> (defun add-two (list)


(when list
(cons (+ 2 (car list)) (add-two (cdr list)))))
ADD-TWO
> (add-two '(4 7 2 3))
(6 9 4 5)

He re , we ’re c re a ting a func tion add-two , whic h a dds two to the num be r a t the front of the list a nd the n c a lls itse lf
re c ursive ly to build the ta il of the list.
T his c ode a voids m a ny of the downside s from the im pe ra tive solution. It doe s not de stroy the origina l list, a nd it doe s not re quire
us to use a num e ric inde x. Unfortuna te ly, it a lso la c ks one of the c ritic a l be ne fits of the im pe ra tive ve rsion: T he re is no longe r a
c le a r de line a tion be twe e n the c ode tha t a dds two to ite m s in the list a nd the c ode tha t tra ve rse s the list. T he se two a c tivitie s a re
now de e ply inte rtwine d, whic h is the re a son we ne e de d to c re a te a spe c ia l func tion, add-two, to m a ke this solution work. W e ha ve
lost our a bility to c om pose the se two ta sks in a c le a n wa y.
H ighe r -O r de r P r ogr amming to the Re sc ue
If we wa nt to write c ode for this ta sk in a func tiona l style , but still a llow our c ode to be c om posa ble , we ’ll ne e d to m a ke use of
highe r-orde r func tions. He re is how a n e xpe rie nc e d L ispe r would a dd two to e ve ry num be r in a list:
> (mapcar (lambda (x)
(+ x 2))
'(4 7 2 3))
(6 9 4 5)
Now we fina lly ha ve a ve rsion of the c ode tha t is func tiona l and a llows us to c om pose the tra ve rsa l c ode a nd the a ddition c ode .
He re , the tra ve rsa l is pe rform e d by the mapcar func tion, whic h is a highe r-orde r func tion sinc e it a pplie s a supplie d func tion to
e ve ry m e m be r in a list. T he a ddition is pe rform e d by a la m bda func tion, whic h is re sponsible only for a dding two to a num be r, a nd
is oblivious to the fa c t tha t the num be rs a re in a list. T his e xa m ple shows tha t highe r-orde r progra m m ing c a n le t us write c le a rly
de line a te d c hunks of c ode a nd the n c om pose the m , without ne e ding to bre a k from the func tiona l style .
W hy F unc tional P r ogr amming Is Cr az y
W e a lre a dy know one re a son why func tiona l progra m m ing is c ra z y: Func tiona l progra m s c a n’t re a lly do a nything, sinc e the y
c a n’t ha ve side e ffe c ts. As Sim on Pe yton Jone s, a we ll-known func tiona l progra m m e r, like s to sa y, “ All you c a n do without side
e ffe c ts is push a button a nd wa tc h the box ge t hot for a while . ” (W hic h isn’t te c hnic a lly true , sinc e e ve n the box ge tting hot is a
side e ffe c t. )
W e ’ve se e n tha t we c a n work a round this lim ita tion of func tiona l progra m m ing by a dding a dirty se c tion to our progra m s, whic h
is ke pt se pa ra te from the re st of the c ode a nd c onta ins a ll our c ode tha t is im pe ra tive a nd not in the func tiona l style . Howe ve r,
re c a ll the proble m with the func tiona l style : It c a n c a use c ode to be e xtre m e ly ine ffic ie nt.
Pe rform a nc e ha s a lwa ys be e n a huge c onc e rn with func tiona l progra m s. Ha ving to write c ode tha t isn’t a llowe d to m uta te the
va lue of e xisting va ria ble s, but only c re a te ne w va ria ble s, c a n le a d to a huge a m ount of m e m ory c opying a nd m e m ory a lloc a tion,
whic h c a n slow progra m s down to a c ra wl. One wa y to m itiga te this c opying a nd a lloc a tion is by using sha re d struc ture s be twe e n
diffe re nt pie c e s of da ta in our progra m s.
None the le ss, c ode writte n in the func tiona l style ha s othe r prope rtie s tha t a ffe c t pe rform a nc e . For insta nc e , func tiona l c ode use s
a lot of re c ursion, inste a d of looping. Using re c ursion c a use s the L isp c om pile r/inte rpre te r to put a lot of ite m s on the progra m
sta c k, whic h c a n be ve ry slow.
Fortuna te ly, func tiona l progra m m e rs ha ve de ve lope d optim iz a tion te c hnique s tha t c a n solve the va st m a jority of pe rform a nc e
proble m s. T he se inc lude m e m oiz a tion, ta il c a ll optim iz a tion, la z y e va lua tion, a nd highe r-orde r progra m m ing, whic h we ’ll c ove r
in the ne xt fe w c ha pte rs. Using the se te c hnique s a nd othe rs, a n e xpe rie nc e d func tiona l progra m m e r c a n write c ode tha t is usua lly
c om pa ra ble in pe rform a nc e to c ode writte n in a ny othe r style .
Howe ve r, som e type s of progra m s just c a n’t be writte n in a pure ly func tiona l wa y. For insta nc e , you proba bly wouldn’t write
som e thing like a full-on Ora c le -style re la tiona l da ta ba se syste m in a func tiona l style . Ye t, sm a lle r, m e m ory-re side nt da ta ba se
syste m s m a y be a ble to use pure ly func tiona l te c hnique s (a n e xa m ple is the HAppS-IxSe t a va ila ble to Ha ske ll progra m m e rs a t
http://ha pps. org/). So the re is re a lly no ha rd lim it a s to whe n func tiona l progra m m ing c a n be use d.
W hy F unc tional P r ogr amming Is F antastic
Now tha t I’ve told you a bout a ll the he a da c he s a func tiona l progra m m e r m ust e ndure , you m a y be wonde ring, “ W hy would
a nyone bothe r to progra m this wa y? ” T he a nswe r is tha t func tiona l progra m m ing ha s m a ny e ntic ing be ne fits tha t m a ke up for the se
he a da c he s.
F unc tional P r ogr amming Re duc e s Bugs
Bugs in c om pute r progra m s usua lly ha ppe n be c a use , unde r c e rta in c irc um sta nc e s, the c ode be ha ve s in wa ys the progra m m e r
didn’t e xpe c t whe n the c ode wa s writte n. In func tiona l progra m m ing, the be ha vior of your func tions de pe nds on one a nd only one
thing: the a rgum e nts e xplic itly pa sse d into the func tion. T his m a ke s it m uc h e a sie r for a progra m m e r to a ppre c ia te a ll the
c irc um sta nc e s a progra m c ould possibly e nc ounte r, inc luding c irc um sta nc e s tha t c ould le a d to e rrors.
W riting func tions tha t de pe nd on only the ir a rgum e nts for the ir be ha vior a lso m a ke s bugs e a sy to duplic a te . If you c a ll a func tion
with the sa m e da ta pa sse d in through its a rgum e nts, it should do the sa m e e xa c t thing e ve ry tim e . T his is the prope rty we c a lle d
re fe re ntial transpare nc y .
F unc tional P r ogr ams Ar e M or e Compac t
It turns out a lot of the work in run-of-the -m ill c om pute r progra m s involve s c re a ting, initia liz ing, a nd upda ting va ria ble s.
Func tiona l progra m s don’t do a ny of this. As we disc usse d e a rlie r, func tiona l progra m s m a ke use of highe r-orde r func tions, whic h
don’t re quire us to c re a te tons of te m pora ry va ria ble s in our c ode , a nd tha t m a ke s our c ode m ore c om pa c t.
F unc tional Code Is M or e Ele gant
T he bigge st a dva nta ge of func tiona l progra m m ing is tha t it brings a ll of c om pute r progra m m ing ba c k to the dom a in of
m a the m a tic s. It wouldn’t m a ke se nse for a m a th e qua tion to pop up a dia log box or write to the ha rd drive . It c a n be a rgue d tha t if
we ge t our c om pute r c ode ba c k to this sa m e le ve l of purity, it will be fa r m ore e le ga nt. Additiona lly, if our c ode is c lose r to the
world of m a the m a tic s, we m a y be a ble to use tools in m a the m a tic s to write be tte r c om pute r c ode .
In fa c t, a lot of re se a rc h c ontinue s to be done in using m a the m a tic a l proofs to c he c k for the c orre c tne ss of func tiona l c om pute r
progra m s. Although this re se a rc h still isn’t to the point whe re a pra c tic a l progra m m e r would use suc h te c hnique s, the y m a y be m ore
c om m on in the future . And, a lm ost c e rta inly, a func tiona l progra m m ing style will be e sse ntia l in m a king c orre c tne ss proofs on your
c ode possible .
W hat You've Le ar ne d
In this c ha pte r, we disc usse d func tiona l progra m m ing. Along the wa y, you le a rne d the following:
Progra m s writte n in the func tional sty le a lwa ys give the sa m e re sult whe n the y a re give n the sa m e va lue s in the ir a rgum e nts.
Func tiona l progra m s do not c onta in side e ffe c ts. T he ir whole purpose in life is to just c a lc ula te a va lue to re turn.
Progra m s tha t a re not func tiona l usua lly re a d like a c ookbook, with sta te m e nts like , “ First do this, a nd the n do tha t. ” T his style
of progra m m ing is c a lle d impe rativ e programming.
A good stra te gy for writing L isp progra m s is to bre a k the m into a c le a n, func tiona l pa rt a nd a dirty, im pe ra tive pa rt.
Func tiona l progra m s c a n be writte n quic kly, a re m ore c om pa c t, a nd te nd to ha ve fe we r bugs, pa rtic ula rly in the ha nds of a n
e xpe rie nc e d func tiona l progra m m e r.
Chapte r 15. Dic e of Doom, a G ame W r itte n in the F unc tional Style
Now we ’re fina lly re a dy to c re a te a m ore sophistic a te d (a nd fun) c om pute r progra m in the func tiona l style . As we e xpa nd this
progra m throughout the re st of this book, you’ll le a rn a bout te c hnique s for writing e le ga nt func tiona l c ode , while a t the sa m e tim e
m a inta ining strong pe rform a nc e in your progra m s.
The Rule s of Dic e of Doom
Dic e of Doom is a ga m e in the sa m e fa m ily a s Risk, Dic e W a rs (http://www. ga m e de sign. jp/fla sh/dic e /dic e . htm l), a nd KDic e
(http://kdic e . c om /). In the be ginning, we ’re going to ke e p the rule s of Dic e of Doom m ind-num bingly sim ple . In la te r c ha pte rs,
we ’ll e xpa nd the rule s, until e ve ntua lly we ’ll ha ve a ga m e ve ry sim ila r to Dic e W a rs.
He re a re the sim plifie d rule s we ’ll sta rt with:

T wo pla ye rs (na m e d A a nd B) oc c upy spa c e s on a he xa gona l grid. E a c h he xa gon in the grid will ha ve som e six-
side d dic e on it, owne d by the oc c upa nt.
During a turn, a pla ye r c a n pe rform a ny num be r of m ove s, but m ust pe rform a t le a st one m ove . If the pla ye r
c a nnot m ove , the ga m e e nds.
A m ove c onsists of a tta c king a ne ighboring he xa gon owne d by the oppone nt. T he pla ye r m ust ha ve m ore dic e in
he r he xa gon tha n the ne ighboring he xa gon in orde r to a tta c k. For now, a ll a tta c ks will a utom a tic a lly le a d to a win.
In future va ria nts, we ’ll a c tua lly roll the dic e for a ba ttle . But for now, the pla ye r with m ore dic e just wins
a utom a tic a lly.
Afte r winning a ba ttle , the losing pla ye r’s dic e a re re m ove d from the boa rd, a nd a ll but one of the winning
pla ye r’s dic e a re m ove d onto the ne wly won he xa gon.
Afte r a pla ye r is finishe d m a king he r m ove s, re inforc e m e nts a re a dde d to tha t pla ye r’s dic e a rm ie s.
Re inforc e m e nts to the pla ye r’s oc c upie d he xa gons a re a dde d one die a t a tim e , sta rting from the uppe r-le ft c orne r,
m oving a c ross a nd down. T he m a xim um num be r of dic e a dde d a s re inforc e m e nts is one le ss tha n the pla ye r took
from the oppone nt in he r c om ple te d turn.
W he n a pla ye r c a n no longe r ta ke he r turn, the ga m e ha s e nde d. T he pla ye r who oc c upie s the m ost he xa gons a t
this point is the winne r. (A tie is a lso possible . )
A Sample G ame of Dic e of Doom
Sinc e our im ple m e nta tion of Dic e of Doom will inc lude a n AI pla ye r, we ’re going to sta rt with a n e xtre m e ly hum ble siz e for our
ga m e boa rd. As you proba bly know, AI c ode c a n be ve ry c om puta tiona lly inte nsive . In our e a rly, ve ry na ive ve rsion of this ga m e ,
a ny boa rd la rge r tha n a 2-by-2 grid of he xa gons would bring CL ISP to its kne e s!
He re is a c om ple te ga m e , pla ye d on a puny 2-by-2 boa rd:

At the be ginning of the ga m e , pla ye r A (indic a te d with bla c k he xa gons) posse sse s the top two he xa gons, with thre e dic e on e a c h.
Pla ye r B oc c upie s the bottom row (indic a te d by the white he xa gons), with thre e dic e a nd one die , re spe c tive ly. Pla ye r A a tta c ks
the lone die with one of his pile s. Afte r the a tta c k, one of pla ye r A’s dic e re m a ins be hind, while the othe rs m ove to the c onque re d
spot. T he n pla ye r A pa sse s the turn.

Pla ye r B now a tta c ks pla ye r A’s two dic e with a pile of thre e . Pla ye r B the n pa sse s. At this point, pla ye r B re c e ive s a single
re inforc e m e nt die on he r le ft he xa gon. T his is be c a use she kille d two of pla ye r A’s dic e . T he re inforc e m e nts, a s pe r the rule s,
c onsist of the num be r of dic e kille d, m inus one .
Pla ye r A now a tta c ks with thre e of his dic e a nd pa sse s. Also, he ge ts a re inforc e m e nt die .

Pla ye r B now ha s only one le ga l m ove , a tta c king two a ga inst one .

Pla ye r A now ha s the uppe r ha nd, killing a ll of pla ye r B’s re m a ining dic e . As you c a n se e , pla ye r A is pe rm itte d to pe rform
m ultiple a tta c ks on his turn be fore pa ssing. T he ga m e ha s e nde d with pla ye r A a s the winne r.
Imple me nting Dic e of Doom, Ve r sion 1
L e t’s sta rt c oding this ga m e in L isp. As we disc usse d in the pre vious c ha pte r, this ga m e will c onta in both c le a n, func tiona l c ode
a nd dirty, im pe ra tive c ode . You’ll be a ble to te ll in whic h c a te gory a bloc k of c ode fits by the “ c le a n/func tiona l” or
“ dirty/im pe ra tive ” ic on ne xt to it.
De fining Some G lobal Var iable s
First, we ’ll c re a te som e globa l va ria ble s tha t de fine the ba sic pa ra m e te rs for our ga m e :

(defparameter *num-players* 2)

(defparameter *max-dice* 3)

(defparameter *board-size* 2)

(defparameter *board-hexnum* (* *board-size* *board-size*))

W e ’re sta ting tha t the re will be two pla ye rs , tha t the m a xim um num be r of dic e on a squa re is thre e , a nd tha t the

boa rd will be 2-by-2 . In la te r ve rsions of Dic e of Doom , we ’ll inc re a se a ll of the se pa ra m e te rs, to a llow for a m ore
c ha lle nging ga m e .

Sinc e it’s use ful to know the tota l num be r or he xa gons the re a re a t the c urre nt boa rd siz e , we a lso de fine *board-hexnum* .
Note tha t e ve n though the grid is m a de of he xa gons, it is still ba sic a lly a squa re grid, sinc e the num be r of he xa gons just e qua ls the
squa re of the side of the grid.

Note

In this c ha pte r, e ve ry c ode sa m ple ha s a n a ssoc ia te d ic on to indic a te whe the r it is m a de of dirty, im pe ra tive or c le a n, func tiona l
c ode . By the e nd of this c ha pte r, you should be a ble to e a sily te ll the diffe re nc e a nd ha ve som e a ppre c ia tion for the be ne fits of
e a c h style .
Re pr e se nting the G ame Boar d
W e ’re going to re pre se nt the ga m e boa rd using a sim ple list. T he he xa gons will be store d in this list, sta rting a t the top le ft, a nd
the n m oving a c ross a nd down. For e a c h he xa gon, we ’ll store a list of two ite m s: a num be r indic a ting the c urre nt oc c upa nt of the
he xa gon a nd a nothe r num be r indic a ting the num be r of dic e a t tha t loc a tion.
For insta nc e , he re is a n e xa m ple of a ga m e boa rd a nd the list tha t e nc ode s it:

((0 3) (0 3) (1 3) (1 1))
Note tha t m ost L isp progra m m e rs like to c ount sta rting a t z e ro. T he re fore , pla ye rs A a nd B a re re pre se nte d with the num be rs 0
a nd 1. T his list indic a te s tha t pla ye r A ha s thre e dic e on the first he xa gon a nd thre e on the se c ond. Pla ye r B ha s thre e dic e on the
third he xa gon a nd one on the fourth.
W he n we c re a te our AI pla ye r, it will ne e d to be a ble to look a t m a ny he xa gons on the boa rd ve ry quic kly. Be c a use of this,
we ’re going to c re a te a se c ond re pre se nta tion of our boa rd in the form of a n a rra y. Re m e m be r tha t c he c king a num e ric loc a tion
(for insta nc e , he xa gon 2) in a list re quire s the nth func tion, whic h is pote ntia lly slow. Arra ys, on the othe r ha nd, will a llow for ve ry
fa st lookup a t a spe c ific loc a tion, e ve n with ve ry la rge boa rd siz e s.
T he board-array func tion c onve rts a boa rd re pre se nte d with a list to a n a rra y for us:
(defun board-array (lst)
(make-array *board-hexnum* :initial-contents lst))
W he n the ga m e be gins, we ’ll sta rt with a ra ndom iz e d boa rd. He re ’s the func tion tha t c re a te s a ra ndom boa rd:

(defun gen-board ()

(board-array (loop for n below *board-hexnum*

collect (list (random *num-players*)


(1+ (random *max-dice*))))))
T his func tion is not in the func tiona l style (a s the ic on indic a te s), sinc e it will c re a te a diffe re nt, ra ndom re sult e ve ry tim e it is
c a lle d. It ge ne ra te s the boa rd a s a list, but the n c onve rts the list to our spe e die r a rra y form a t whe n it’s done , using board-array

.
It ge ne ra te s ra ndom va lue s using the L isp func tion random. T his func tion produc e s a diffe re nt ra ndom inte ge r e ve ry tim e , gre a te r
tha n or e qua l to z e ro, but sm a lle r tha n the num be r pa sse d to it. W e use our *num-players* a nd *max-dice* globa l va ria ble s to

ge ne ra te ra ndom va lue s for e a c h he xa gon .


L e t’s try out the gen-board func tion:
> (gen-board)
#((0 3) (1 2) (1 3) (0 1))
Re m e m be r tha t the ha sh m a rk (#) indic a te s tha t we ’ve c re a te d a n a rra y, not a list.
W e ’ll na m e our pla ye rs using le tte rs (just A a nd B, until we sta rt introduc ing m ore pla ye rs). He re ’s a func tion tha t c onve rts a
pla ye r num be r into a le tte r:
(defun player-letter (n)
(code-char (+ 97 n)))
T he code-char func tion c onve rts a n ASCII c ode into the a ppropria te c ha ra c te r. L e t’s c a ll it for pla ye r 1 to se e the re sult:
> (player-letter 1)
#\b
Fina lly, le t’s c re a te a func tion tha t will ta ke a n e nc ode d boa rd a nd dra w it in a pre tty wa y on the sc re e n. It will tilt the boa rd in
the sa m e wa y a s our dra wings, so it’s obvious whic h six he xa gons ne ighbor a ny give n he xa gon.
(defun draw-board (board)

(loop for y below *board-size*


do (progn (fresh-line)

(loop repeat (- *board-size* y)


do (princ " "))

(loop for x below *board-size*

for hex = (aref board (+ x (* *board-size* y)))


do (format t "˜a-˜a " (player-letter (first hex))

(second hex))))))
Sinc e the whole purpose of this draw-board func tion is to write stuff to the c onsole , it’s de finite ly not func tiona l. L e t’s look a t
this func tion m ore c lose ly.

T he oute r loop runs through a ll the rows of the boa rd, store d in the va ria ble y . T he re a re two inne r loops. T he first inne r

loop a dds the inde nta tion to the le ft side to give the boa rd tha t tilte d look . T he se c ond inne r loop loops through the c olum ns,

store d in the va ria ble x . It the n use s x a nd y to c a lc ula te the a ppropria te he x num be r, a nd re trie ve s tha t he x from the boa rd

a rra y using aref . Fina lly, it prints the da ta in the he x .


He re ’s the output of the draw-board func tion, a s we ll a s a dra wing to c om pa re it with:
> (draw-board #((0 3) (0 3) (1 3) (1 1)))
a-3 a-3
b-3 b-1
De c oupling Dic e of Doom's Rule s fr om the Re st of the G ame
Now we ’re re a dy to write the c ode tha t ta ke s c a re of the guts of our first Dic e of Doom im ple m e nta tion. In writing this c ode ,
we ’re going to e m ploy a powe rful func tiona l progra m m ing te c hnique : a func tion pipe line . T his m e a ns tha t our ga m e is going to
c onsist of a suc c e ssion of func tions tha t ope ra te , one a fte r a nothe r, on a big c hunk of da ta , whic h will hold a re pre se nta tion of our
ga m e boa rd, m a king m odific a tions to the struc ture a long the wa y. A func tion pipe line will a llow us to build a ga m e rule e ngine
tha t’s 100% de c ouple d from the re st of the game c ode . T o unde rsta nd why this is so c ool, le t’s first c onside r som e of wha t’s involve d
in writing a boa rd ga m e with a sm a rt AI pla ye r.
For one thing, a ny c om pute r im ple m e nta tion of a boa rd ga m e will ne e d c ode tha t ha ndle s the hum a n pla ye r’s m ove s. T his pa rt
of the c ode will ne e d to know the rule s of the boa rd ga m e a nd m a ke sure the hum a n pla ye r’s m ove is le ga l be fore le tting it
ha ppe n.
W e ’ll a lso ne e d to write the AI c ode . And in orde r for the AI pla ye r to pic k a m ove , it ne e ds to know a ll the rule s of the boa rd
ga m e .
Notic e som e thing? Both of the se se pa ra te pa rts of our ga m e e ngine ne e d to unde rsta nd the rule s of the ga m e ! Cle a rly, wha t we
wa nt to do is bre a k our ga m e c ode into thre e big pie c e s:

T he ha ndling of the hum a n’s m ove s


T he AI pla ye r
T he rule e ngine

One pie c e ha ndle s the pla ye r’s m ove s. Anothe r is the c ode for the AI pla ye r. Both of the se the n ta lk to som e c ode tha t
unde rsta nd the rule s, sort of a “ rule e ngine . ” Is this kind of de sign possible ?
In a tra ditiona l, im pe ra tive progra m m ing style , it would be ve ry diffic ult to write a progra m like this. Most im pe ra tive ga m e
e ngine s duplic a te the c ode tha t “ unde rsta nds the rule s, ” be c a use of the c om ple xity of writing fully de c ouple d c om pone nts in a n
im pe ra tive la ngua ge . T he re a son for this is tha t a boa rd ga m e re quire s a lot of c onte xt— e ve ry m ove is de pe nde nt on wha t m ove s
pre c e de d it. T his m e a ns tha t e ve ry tim e the AI m odule or pla ye r-ha ndling m odule ne e ds to c he c k the rule s, it m ust te ll the “ rule
c ode ” the c urre nt c onte xt in de ta il. Both would ne e d to te ll the rule c ode tha t “ It’s pla ye r so-a nd-so’s turn a nd the ga m e boa rd
looks like suc h-a nd-suc h. ” W ithout this inform a tion, the rule c ode c a n’t te ll whe the r or not a m ove is le ga l.
Pa ssing a round this c onte xt re quire s tons of te dious bookke e ping c ode e ve rywhe re , is e rror-prone , a nd is ine ffic ie nt. It’s
ine ffic ie nt be c a use , with a na ive de sign, the pla ye r-ha ndling c ode m a y c he c k the le ga lity of m ove s the AI c ode ha d a lre a dy
e xplore d a nd found le ga l.
Using func tiona l progra m m ing, howe ve r, we c a n de c ouple the se thre e c onc e rns e ntire ly in our progra m . W e will be a ble to do
this without bookke e ping c ode a nd in a wa y tha t a voids duplic a tion a ny le ga lity c a lc ula tions. W e will a c c om plish this by e nc oding
our rule c ode in a la z y ga m e tre e !

Note

T he ba sic a pproa c h we ’re using— progra m m ing a ga m e in the func tiona l style using a la z y ga m e tre e a nd a func tion pipe line — is
de sc ribe d in the c la ssic pa pe r “ W hy Func tiona l Progra m m ing Ma tte rs” by John Hughe s (http://www. sc ribd. c om /doc /26902/whyfp/).

In this c ha pte r, we ’ll be c re a ting a ga m e tre e tha t is not ye t la z y. You’ll ne e d to wa it until Cha pte r 18 to unde rsta nd la z y
progra m m ing a nd wha t a la z y ga m e tre e will look like . T ha t’s a lso whe n you’ll be a ble to fully a ppre c ia te how c ool this
a rc hite c tura l de sign re a lly is.
G e ne r ating a G ame Tr e e
T he e ntire rule se t for our ga m e is e nc ode d in the following m a ste r func tion:

(defun game-tree (board player spare-dice first-move)

(list player
board

(add-passing-move board
player
spare-dice
first-move

(attacking-moves board player spare-dice))))


T he game-tree func tion builds a tre e of a ll possible m ove s, give n a c e rta in sta rting c onfigura tion. T his func tion will be c a lle d
only a single tim e a t the be ginning of the ga m e . It will the n re c ursive ly build a tre e of a ll possible m ove s for the ga m e , down to
the fina l winning positions. T he othe r pa rts of our ga m e will the n e le ga ntly tra ve rse this tre e in orde r to c onform to the rule s of the
ga m e .
In orde r to c a lc ula te the le ga l possible m ove s of the ga m e tre e from a give n c onte xt, the func tion ne e ds four pie c e s of da ta

pa sse d to it a s a rgum e nts :

W ha t the boa rd looks like


T he c urre nt pla ye r
How m a ny dic e ha ve be e n c a pture d by the pla ye r in the pla ye r’s c urre nt turn, whic h is ne e de d to c a lc ula te a ny
future re inforc e m e nts, a s pe r our rule s
W he the r the c urre nt m ove is the first m ove for the c urre nt pla ye r, be c a use a pla ye r c a n’t pa ss a turn without first
m a king a t le a st one m ove

As the game-tree func tion c re a te s the tre e , it will put inform a tion a bout the c urre nt boa rd a nd c urre nt pla ye r a t e ve ry bra nc h
. T he subbra nc he s will the n hold a ll the le ga l follow-up m ove s from the c urre nt bra nc h:

T he re a re two type s of le ga l m ove s possible for pla ye rs: a tta c k a he xa gon or pa ss the ir turn to the ne xt pla ye r (a ssum ing the y’ve
a lre a dy a tta c ke d a t le a st onc e a lre a dy). T he pa ssing m ove is a dde d to the list of le ga l m ove s through the add-passing-move

func t i on . T he a tta c king m ove s a re a dde d to the list through the attacking-moves func tion . L e t’s look a t the se
func tions ne xt.
Calc ulating P assing M ove s
He re is the func tion tha t a dds the pa ssing m ove s to the ga m e tre e :

(defun add-passing-move (board player spare-dice first-move moves)

(if first-move

moves

(cons (list nil

(game-tree (add-new-dice board player (1-spare-dice))

(mod (1+ player) *num-players*)


0
t))
moves)))
T he job of this func tion is to a dd a pa ssing m ove to the ta lly of m ove s, if pa ssing is pe rm itte d. T he c urre nt list of m ove s is

pa sse d in to this func tion , a nd the n the func tion will re turn the e xpa nde d list of m ove s. If the m ove is the first m ove in a

pla ye r’s turn , no pa ssing is a llowe d, a nd we just re turn the una lte re d list . Othe rwise , we a dd a ne w m ove to the list.
E ve ry m ove in our ga m e tre e c onsists of two pa rts:

T he first pa rt is a de sc ription of the m ove . Sinc e we ’re just pa ssing in this m ove , we ’ll se t the de sc ription to nil

.
T he se c ond pa rt of the m ove is a n e ntire ly ne w ga m e tre e , whic h holds the e ntire unive rse of m ove s tha t e xists

a fte r this m ove ha s be e n pe rform e d. W e c re a te this by re c ursive ly c a lling game-tree a ga in . Sinc e this is the
e nd of the pla ye r’s turn, the pla ye r m a y re c e ive dic e a s re inforc e m e nts. So, we upda te the boa rd se nt to this ne w

game-tree c a ll with the add-new-dice func tion .

Of c ourse , we a lso will ne e d to c ha nge the c urre nt pla ye r, sinc e a ne w pe rson’s turn is now sta rting. W e do this by a dding one to

the c urre nt pla ye r num be r a nd ta king the m odulus of the re sult, with the tota l num be r of pla ye rs a s the de nom ina tor .
Cha nging a pla ye r in this fa nc y wa y will a llow the c ode to work, e ve n whe n we inc re a se the num be r of pla ye rs in the ga m e in
future ve rsions.
Calc ulating Attac king M ove s
He re is the func tion tha t a dds the possible a tta c king m ove s to the ga m e tre e :

(defun attacking-moves (board cur-player spare-dice)

(labels ((player (pos)


(car (aref board pos)))

(dice (pos)
(cadr (aref board pos))))

(mapcan (lambda (src)

(when (eq (player src) cur-player)

(mapcan (lambda (dst)


(when (and (not (eq (player dst) cur-player))

(> (dice src) (dice dst)))


(list

(list (list src dst)

(game-tree (board-attack board cur-player src dst (dice src))


cur-player
(+ spare-dice (dice dst))
nil)))))

(neighbors src))))
(loop for n below *board-hexnum*
collect n))))
T he attacking-moves func tion is a bit m ore c om plic a te d tha n the add-passing-move func tion. It’s re sponsible for sc a nning the
c urre nt ga m e boa rd a nd figuring out wha t m ove s the c urre nt pla ye r is le ga lly a llowe d to pe rform .
Sinc e it m ust spe nd a lot of tim e figuring out who the pla ye r is on a give n he xa gon, we first write a c onve nie nc e func tion c a lle d

player tha t re turns the pla ye r for a give n boa rd position . W e write a sim ila r func tion to ge t the num be r of dic e on a give n

he xa gon .
Ne xt, we ne e d to sc a n the boa rd top to bottom a nd find out whic h squa re s the c urre nt pla ye r oc c upie s. For e a c h oc c upie d squa re ,
the re m a y be one or m ore le ga l a tta c ks sta rting a t tha t position. Sinc e the num be r of a tta c ks from a ny he xa gon m a y va ry, we use

mapcan to sc a n the boa rd . Re m e m be r tha t mapcan le ts e a c h he xa gon we sc a n re turn its re sults a s a list. T he n mapcan
c onc a te na te s the se lists toge the r. T his wa y, a ny sc a nne d he xa gon c a n c ontribute z e ro to n m ove s to the list.
W ithin the lambda func tion use d by the mapcan, whic h ge ts c a lle d for e ve ry he xa gon, we first wa nt to c he c k whe the r the c urre nt

pla ye r oc c upie s this he xa gon . T he n we wa nt to c he c k a ll of its ne ighbors to se e if a ny of the m pre se nt a via ble a tta c k. W e

do this with a nothe r mapcan . W e ’ll figure out the ne ighbors to this he xa gon by using the neighbors func tion, whic h we ’ll

write shortly .
How do we de c ide if a he xa gon c a n be a n a tta c k de stina tion? W e ll, it m ust be a he xa gon we don’t a lre a dy own, plus (a s pe r the

rule s) the sourc e he xa gon ne e ds to ha ve m ore dic e tha n the de stina tion he xa gon . If we ha ve found a le ga l a tta c k m ove , we

the n de sc ribe the m ove . T he de sc ription is sim ply a list of the sourc e position a nd the de stina tion position. W e the n (a s with

pa ssing m ove s) re c ursive ly ge ne ra te a nothe r ga m e tre e tha t de sc ribe s wha t ha ppe ns if the m ove is e xe c ute d .
F inding the Ne ighbor s
Ne xt, le t’s c re a te the func tion tha t c a lc ula te s the ne ighboring he xa gons to a give n he xa gon:

(defun neighbors (pos)


(let ((up (- pos *board-size*))
(down (+ pos *board-size*)))

(loop for p in (append (list up down)

(unless (zerop (mod pos *board-size*))


(list (1-up) (1-pos)))

(unless (zerop (mod (1+ pos) *board-size*))


(list (1+ pos) (1+ down))))

when (and (>= p 0) (< p *board-hexnum*))


collect p)))
E ve ry he xa gon on the boa rd m a y ha ve up to six ne ighbors, or fe we r, if the he xa gon is on a n e dge of the boa rd. W e build up a list

of possible ne ighbors in a loop , a nd the n c olle c t the one s with position num be rs tha t a re n’t off the e dge of the boa rd .
Also, sinc e our position num be rs wra p from row to row, we ne e d to m a ke sure we don’t look to the le ft if we ’re on the le ft e dge of

the boa rd or look to the right if we ’re on the right e dge of the boa rd .
T his func tion is m a rke d c le a n (it is in the func tiona l style ), but none the le ss c onta ins a loop. Usua lly, looping goe s a ga inst the
te ne ts of func tiona l progra m m ing. Howe ve r, m a ny L ispe rs c onside r it koshe r to use a loop in func tiona l c ode if a ll it doe s is
c olle c t som e va lue s, sinc e it re a lly isn’t m uta ting a ny va lue s or produc ing a ny othe r side e ffe c ts. So, we will a llow ourse lve s to use
suc h loops in the func tiona l-style pa rt of this ga m e .
L e t’s try out our neighbors func tion:
> (neighbors 2)
(0 3)
As you c a n se e , it c orre c tly te lls us tha t he xa gon 2 ne ighbors he xa gons 0 a nd 3.
Attac king
Now le t’s write our board-attack func tion:

(defun board-attack (board player src dst dice)

(board-array (loop for pos

for hex across board

collect (cond ((eq pos src) (list player 1))

((eq pos dst) (list player (1-dice)))

(t hex)))))
T his is a func tion tha t figure s out wha t ha ppe ns if the he xa gon src a tta c ks the he xa gon dst. It works by looping a c ross the boa rd,

ke e ping tra c k of the c urre nt position a nd the c onte nts in the he xa gon a t tha t position . If the c urre nt he xa gon is the

sourc e he xa gon, we just pla c e a single die in tha t pla c e ; a s pe r our rule s, a single die is le ft be hind a fte r a n a tta c k . If the

c urre nt he xa gon is the de stina tion position, we pla c e the re m a ining dic e the re , subtra c ting the one le ft be hind . In othe r

c a se s, we just c olle c t the ve ry sa m e he x .


L e t’s try out our board-attack func tion:
> (board-attack #((0 3) (0 3) (1 3) (1 1)) 0 1 3 3)
#((0 3) (0 1) (1 3) (0 2))
As you c a n se e , a tta c king from he xa gon 1 to 3 c a use s board-attack to prope rly upda te the ga m e boa rd, so tha t one die re m a ins
on the old squa re a nd two a re on the ne w, c onque re d squa re .

Note

Ma ny of the func tions in this c ha pte r ha ve ine ffic ie nc ie s to ke e p things sim ple . W e ’ll fix m a ny of the se in future ve rsions of the
ga m e .
Re infor c e me nts
T o a dd the re inforc e m e nts to the boa rd, we ne e d to sc a n a c ross the ga m e boa rd, find oc c upie d spots tha t c a n a c c om m oda te
a nothe r die , a nd a dd the die the re . Of c ourse , the num be r of re inforc e m e nts is lim ite d ba se d on how m a ny oppone nt dic e the pla ye r
c a pture d in the la st turn. Be c a use of this, we ’ll ne e d to ke e p a running ta lly of how m a ny re inforc e m e nt dic e re m a in.
T he m ost obvious wa y to tra c k the re m a ining dic e would be to ha ve a remaining-dice va ria ble , a nd de c re m e nt this e ve ry tim e
a die is pla c e d. Howe ve r, ha ving a die tha t is de c re m e nte d (m uta te d) would not be in line with the func tiona l style .
T he re fore , inste a d, we ’re going to write our add-new-dice func tion using a loc a l re c ursive func tion, whic h will a lso m a inta in
this running c ount of dic e .
He re is this add-new-dice func tion:

(defun add-new-dice (board player spare-dice)

(labels ((f (lst n)

(cond ((null lst) nil)

((zerop n) lst)
(t (let ((cur-player (caar lst))
(cur-dice (cadar lst)))

(if (and (eq cur-player player) (< cur-dice *max-dice*))


(cons (list cur-player (1+ cur-dice))

(f (cdr lst) (1-n)))

(cons (car lst) (f (cdr lst) n))))))))

(board-array (f (coerce board 'list) spare-dice))))

T he first thing add-new-dice doe s is de fine a loc a l func tion na m e d f . T his func tion will be our list-e a te r tha t goe s through
the he xa gons of the boa rd a nd spits out a ne w list tha t inc lude s the re inforc e m e nts. Sinc e our boa rd is a c tua lly store d in a n a rra y for
e ffic ie nc y re a sons, we c onve rt our a rra y into a list with the coerce func tion be fore c a lling f .
Inside the func tion f, we m ust c onside r thre e situa tions:

T ha t we ’re a t the e nd of the boa rd. In this c a se , the re inforc e d boa rd will a lso be c om ple te d, so we just re turn

nil .
T ha t we ’re out of spare-dice to a dd to a dd a s re inforc e m e nts. In this c a se , the re st of the boa rd will just be the

sa m e a s be fore , so we c a n just re turn the re m a inde r of the list a s the ne w boa rd .


Ne ithe r of the pre c e ding situa tions. In a ll othe r c a se s, we ne e d to a na lyz e the c urre nt he xa gon a nd de c ide
whe the r a re inforc e m e nt should be a dde d in it. W e c he c k whe the r the c urre nt pla ye r oc c upie s tha t he xa gon a nd

whe the r we ha ve le ss tha n the m a xim um num be r of dic e on tha t squa re . If this is the c a se , we a dd a ne w die

on the he xa gon a nd c a ll f a ga inst the re st of the boa rd, re c ursive ly . Othe rwise , we le a ve the c urre nt he xa gon

unc ha nge d a nd proc e e d by re c ursive ly c a lling f a ga inst the re st of the boa rd .

L e t try a dding re inforc e m e nts to a boa rd:


> (add-new-dice #((0 1) (1 3) (0 2) (1 1)) 0 2)
#((0 2) (1 3) (0 3) (1 1))
As you c a n se e , add-new-dice prope rly pla c e d two re inforc e m e nt dic e for pla ye r A (pla ye r 0).
Tr ying O ut O ur Ne w game -tr e e F unc tion
W e ha ve now writte n a ll the c ode ne e de d to c re a te a c om pre he nsive ga m e tre e of our sim plifie d ve rsion of Dic e of Doom . But
be c a re ful! A ga m e tre e of m ost boa rd ga m e s is e xc ruc ia tingly la rge . E ve n on a 2-by-2 boa rd, our ga m e m a y c onsist of hundre ds of
possible m ove s. You’ll wa nt to c a ll the game-tree func tion only on a ga m e boa rd tha t is ne a r the e nd of pla y, or you’ll be
wa tc hing he lple ssly a s the CL ISP RE PL prints out a hum ongous tre e showing a ll the possible wa ys in whic h a ga m e m a y progre ss.
He re is a sa fe boa rd position for you to try out:

> (game-tree #((0 1) (1 1) (0 2) (1 1)) 0 0 t)

(0

#((0 1)(1 1) (0 2) (1 1))

(((2 3)(0
#((0 1) (1 1) (0 1) (0 1))

((NIL(1
#((0 1) (1 1) (0 1) (0 1))
NIL)))))))

T he ga m e tre e first lists the c urre nt pla ye r num be r , the la yout of the boa rd , a nd the n the le ga l m ove s for tha t
c onte xt. For the initia l boa rd position, a t the be ginning of pla ye r A’s turn, the re is only one possible m ove : T he pla ye r c a n m ove

from he xa gon 2 to he xa gon 3, c a pturing pla ye r B’s die in tha t spot . Afte r tha t, the pla ye r c a n pa ss. Pla ye r B now ha s no

m ove a va ila ble . Sinc e this pla ye r’s ga m e tre e ha s no a va ila ble m ove s liste d , the ga m e ha s e nde d, with a win for pla ye r A.
P laying Dic e of Doom Against Anothe r H uman
Now tha t we ’ve c om ple te ly c a pture d the unive rse of Dic e of Doom in our c om pre he nsive game-tree func tion, it’s sim ple to
c re a te a hum a n ve rsus hum a n ve rsion of this ga m e . All we ne e d to do is c re a te som e func tions tha t tra ve l down the ga m e tre e a s
pla ye rs c hoose the ir m ove s.

The M ain Loop

He re is the func tion tha t tra ve ls down the ga m e tre e , a llowing two hum a ns to pla y Dic e of Doom :

(defun play-vs-human (tree)

(print-info tree)

(if (caddr tree)

(play-vs-human (handle-human tree))

(announce-winner (cadr tree))))


T his func tion, play-vs-human, is the m a in loop of our ga m e . It a c c e pts a tre e de sc ribing the sta rting position of the boa rd.
First, it c a lls a func tion na m e d print-info, whic h will dra w the boa rd on the sc re e n, a long with othe r he lpful inform a tion a bout

the c urre nt sta te of the ga m e . Ne xt, we ne e d to c he c k if a ny follow-up m ove s e xist. T he se follow-up m ove s would be liste d

sta rting a t the caddr position of the ga m e tre e .


If follow-up m ove s a re a va ila ble , we c a ll the func tion handle-human, whic h will inte ra c t with the c urre nt pla ye r to he lp him
pic k his ne w m ove . T his handle-human func tion will the n re turn the subbra nc h of the tre e tha t re pre se nts the pla ye r’s c hoic e . W e

c a n the n re c ursive ly pa ss this subbra nc h into play-vs-human to proc e e d with the ga m e .


If no follow-up m ove s a re a va ila ble , the ga m e ha s offic ia lly e nde d. W e the n c a ll the announce-winner func tion, whic h,
a ppropria te ly, will a nnounc e the winne r .

G iving Infor mation About the State of the G ame

He re is the print-info func tion, whic h de sc ribe s the sta tus of the c urre nt node in the ga m e tre e :

(defun print-info (tree)


(fresh-line)

(format t "current player = ˜a" (player-letter (car tree)))

(draw-board (cadr tree)))

T his func tion displa ys two im porta nt pie c e s of inform a tion on the RE PL . First, it shows who the c urre nt pla ye r is . T he n it

prints out a pre tty ve rsion of the ga m e boa rd with the draw-board func tion .

H andling Input fr om H uman P laye r s

Ne xt is the func tion tha t le ts hum a ns c hoose the ir ne xt m ove . It displa ys a ve ry he lpful, num be re d m e nu of a ll c urre ntly
a va ila ble m ove s for the pla ye r to c hoose from .
(defun handle-human (tree)
(fresh-line)
(princ "choose your move:")
(let ((moves (caddr tree)))

(loop for move in moves

for n from 1
do (let ((action (car move)))
(fresh-line)

(format t "˜a. " n)

(if action

(format t "˜a -> ˜a" (car action) (cadr action))

(princ "end turn"))))


(fresh-line)

(cadr (nth (1- (read)) moves))))


T o displa y the list of a va ila ble m ove s, we use a loop tha t tra ve rse s a ll the a va ila ble m ove s a nd prints a de sc ription a bout e a c h

one . T his loop is not func tiona l, sinc e it prints stuff on the sc re e n for the pla ye r to re a d. W e print a c ounting num be r in

front of e a c h m ove using the va ria ble n, whic h c ounts from 1 inside our loop .

E a c h m ove ha s a n a c tion va lue a ssoc ia te d with it. If the a c tion is non-nil , the n the a c tion is a n a tta c k, whe re the a c tion
va lue de sc ribe s the sourc e a nd de stina tion he xa gons of the a tta c k. W e print suc h a tta c king a c tion using the format c om m a nd
.

W e use a n e m pty a c tion va lue to re pre se nt the pa ssing m ove . In tha t c a se , we just princ “ e nd turn” to de sc ribe this m ove .
Afte r the a va ila ble m ove s ha ve be e n displa ye d, we use read to re a d in the pla ye r’s c hoic e . W ith the nth func tion, we c a n the n

se le c t tha t bra nc h of the ga m e tre e a nd re turn it from our handle-human func tion .

De te r mining the W inne r

T he ta sk of a nnounc ing the winne r c a n be nic e ly broke n into a c le an/func tional a nd a dirty /impe rativ e pa rt.
T he c le a n pa rt c onc e rns the ta sk of c a lc ula ting the winning pla ye r. W e wa nt to c a lc ula te this in a wa y tha t c a n ha ndle m ore
tha n just two pla ye rs, sinc e our ga m e will a llow for m ore in the future . Also, the func tion m ust be c ogniz a nt of possible tie s.
T o a c c om plish this, we ’ll write a func tion c a lle d winners tha t re turns a list of one or m ore pla ye rs who c a pture d the m a xim um
num be r of he xa gons a t the e nd of the ga m e . If the re is a tie , it will sim ply re turn a ll the pla ye rs who sha re first pla c e , in te rm s of
the tota l c ount of oc c upie d spa c e s for a ll pla ye rs. W ith this de sign, the func tion will work for any numbe r of play e rs a nd will
e le ga ntly ha ndle tie s. T his is wha t the winners func tion looks like :

(defun winners (board)

(let* ((tally (loop for hex across board


collect (car hex)))
(totals (mapcar (lambda (player)

(cons player (count player tally)))

(remove-duplicates tally)))

(best (apply #'max (mapcar #'cdr totals))))

(mapcar #'car
(remove-if (lambda (x)
(not (eq (cdr x) best)))
totals))))
W e c a lc ula te the winne r for a give n e nding boa rd position in four ste ps.

First, we build up a ta lly of who oc c upie s e a c h he xa gon on the boa rd . W ith the across loop c onstruc t, we
c a n tra ve rse the a rra y of the e nding boa rd dire c tly a nd c olle c t the oc c upie r of e a c h he xa gon.
Se c ond, we ne e d to c ount the tota l num be r of squa re s e a c h pla ye r ha s c a pture d, using this ta lly. T he tota ls
va ria ble will be a n a list of pla ye r->spa c e s pa irs. W e build this a list by finding a ll pla ye rs who ha ve a t le a st one

e ntry in the ta lly with remove-duplicates . W e c a n m a p a c ross this a nd the n c re a te a c ount for e a c h

oc c upie r .
T hird, we wa nt to find wha t the m a xim um num be r of oc c upie d he xa gons for a single pla ye r is. W e do this by

stripping the c ounts from our a list by m a pping cdr a c ross the list . W e the n a pply max to this list to find the
la rge st num be r of oc c upie d spa c e s for a single pla ye r.
Fina lly, we ne e d c re a te a list of a ll the “ be st” pla ye rs. W e do this by stripping out a ll but the be st from our tota ls

using the remove-if func tion . W e the n just pull out the pla ye r num be rs for the be st pla ye rs by m a pping car

a c ross the list of be sts .

Ne xt, le t’s write the dirty announce-winner func tion:

(defun announce-winner (board)


(fresh-line)

(let ((w (winners board)))


(if (> (length w) 1)

(format t "The game is a tie between ˜a" (mapcar #'player-letter w))

(format t "The winner is ˜a" (player-letter (car w))))))

T his func tion is ra the r sim ple . First, we c a lc ula te the winne rs by c a lling our e a rlie r func tion . T he n we c he c k if the re is

m ore tha n one winne r (a tie ). For tie s, we print a spe c ia l m e ssa ge . Othe rwise , we just a nnounc e a single winne r .

Tr ying O ut the H uman vs. H uman Ve r sion of Dic e of Doom

W e now ha ve a c om ple te ly pla ya ble ga m e of dic e of doom . He re is a n e xa m ple ga m e from sta rt to finish:
> (play-vs-human (game-tree (gen-board) 0 0 t))
current player = a
b-2 b-2
a-2 b-1
choose your move:
1. 2 -> 3
1
current player = a
b-2 b-2
a-1 a-1
choose your move:
1. end turn
1
current player = b
b-2 b-2
a-1 a-1
choose your move:
1. 0 -> 2
2. 0 -> 3
3. 1 -> 3
1
current player = b
b-1 b-2
b-1 a-1
choose your move:
1. end turn
2. 1 -> 3
1
current player = a
b-1 b-2
b-1 a-1
The winner is b
Cr e ating an Inte llige nt Compute r O ppone nt
As we disc usse d whe n we we re de signing the ga m e tre e c ode for Dic e of Doom , ha ving a se pa ra te ga m e tre e ge ne ra tor m a ke s it
e a sy to a dd a n AI pla ye r to a ga m e e ngine . In fa c t, we ’re now going to a dd a c om pute r pla ye r tha t c a n pla y a n a bsolute ly pe rfe c t
ga m e with only 23 a dditiona l line s of c ode !
So how doe s a n AI pla ye r de c ide to m a ke a m ove ? W e ’ll use the following stra te gy:

1. L ook a t e a c h a va ila ble m ove .


2. Give a point ra ting to the boa rd position re sulting from e a c h m ove .
3. Pic k the m ove with the m a xim um point ra ting.

T his sounds like a sim ple pla n, but the re is one ste p in this a lgorithm tha t’s pre tty tric ky: c a lc ula ting the be st point ra ting for a
give n boa rd position.
If a m ove le a ds im m e dia te ly to a win, it’s e a sy to give a point ra ting to tha t m ove — a ny winning m ove c le a rly de se rve s a ve ry
high point ra ting. Howe ve r, m ost m ove s in a ga m e c a nnot le a d to a n im m e dia te win. In those c a se s, in orde r to de te rm ine if the
re sult of a se t of m ove s de se rve s a good point ra ting, we ne e d to figure out wha t the oppone nt pla ye r will do in re sponse .
But how will we know wha t the oppone nt pla ye r will de c ide to do? If we ’re not c a re ful, we ’ll e nd up in a n ugly im pa sse whe re
we sa y, “ He thinks tha t I think tha t he thinks tha t I think . . . ” in orde r to c a lc ula te a m e a ningful point va lue for a give n boa rd
position. How do we a c c ount for the oppone nt’s be ha vior without giving ourse lve s a he a da c he ?
The M inimax Algor ithm
It turns out tha t for a two-pla ye r boa rd ga m e , a sim ple m e thod e xists to m ode l wha t a n oppone nt will do. W e sim ply a c c e pt the
truism “ W ha t is good for m y oppone nt is ba d for m e . ” T his m e a ns we c a n use the following a pproa c h to m ode l a m ove for the
oppone nt:

1. L ook a t e a c h a va ila ble m ove .


2. Give a point ra ting to the boa rd position re sulting from e a c h m ove .
3. Pic k the m ove with the m inim um point ra ting.

T his a lgorithm for e stim a ting wha t a n oppone nt will do is ide ntic a l to the one use d for the prim a ry pla ye r, e xc e pt tha t in ste p 3,
we pic k the m ove with the minimum inste a d of max imum ra ting. T he be ne fit of this a pproa c h, c a lle d the minimax algorithm, is tha t
we use the sa m e point ra tings whe n working out the oppone nt’s m ove s tha t we use for the prim a ry AI pla ye r, but the n just twe a k the
third ste p a little to a djust.
T his is c ruc ia l: It turns out tha t if we c a n a void c a lc ula ting se pa ra te ra tings for ourse lve s a s for our oppone nt in the ga m e , the n
se a rc hing down the ga m e tre e for good m ove s be c om e s dra m a tic a lly e a sie r a nd fa ste r.

Note

T he ba sic m inim a x a lgorithm works only in two-pla ye r ga m e s. W he n thre e or m ore pla ye rs a re involve d in a ga m e , we c a n’t
re a lly sa y tha t “ W ha t is good for m y oppone nt is ba d for m e ” is c om ple te ly true a ny m ore . T his is be c a use a n a dditiona l truism
be c om e s im porta nt: “ T he e ne m y of m y e ne m y is m y frie nd. ” T his m e a ns tha t som e of m y oppone nts m a y, a t tim e s, a c t a s a frie nd
by m a king m ove s tha t ha rm a c om m on e ne m y, while not a ffe c ting m e dire c tly. W e ’ll disc uss this issue m ore in Cha pte r 20.
Tur ning M inimax into Ac tual Code
Now we ’re re a dy to put the m inim a x ide a into pra c tic e , like so:

(defun rate-position (tree player)


(let ((moves (caddr tree)))

(if moves
(apply (if (eq (car tree) player)

#'max

#'min)

(get-ratings tree player))

(let ((w (winners (cadr tree))))


(if (member player w)

(/ 1 (length w))

0)))))
T he rate-position func tion ge ne ra te s a num e ric point ra ting for a give n bra nc h of the ga m e tre e . In orde r to do this, we first

ne e d to figure out if the re a re a ny m ove s a va ila ble from the give n position (tha t is, the c urre nt m ove is not a n e nding m ove
in the ga m e ).
If m ove s a re a va ila ble , we ’ll ne e d to look a t a ll the subse que nt m ove s to de c ide how to ra te the c urre nt position. W e a c c om plish

this by c a lling get-ratings , a func tion tha t will re turn the point ra ting of e a c h follow-up m ove . As pe r m inim a x, we will

the n pic k e ithe r the be st (max) or worst (min) ra ting of a ll the follow-up m ove s, de pe nding on whe the r the m ove be ing
ra te d is for the AI pla ye r or its oppone nt.
If, on the othe r ha nd, the re a re no follow-up m ove s, we ’ll ne e d to c he c k who the winne r is for the c urre nt boa rd position . If

the pla ye r isn’t a m ong the winne rs of this position, we c a n give the position the m inim um ra ting of 0 . Othe rwise , we ’ll

divide one by the num be r of winne rs to de te rm ine our ra ting . By doing this, we a lso give a m e a ningful ra ting for tie s. If the
pla ye r is the sole winne r, the ra ting, using this form ula , will be the m a xim um va lue of 1. For a two-pla ye r tie , the ra ting will be a
se nsible 0.5.
He re is wha t the get-ratings func tion looks like :

(defun get-ratings (tree player)


(mapcar (lambda (move)
(rate-position (cadr move) player))
(caddr tree)))
T his func tion sim ply m a ps rate-position a c ross e a c h a va ila ble follow-up m ove for the give n bra nc h of the tre e .
Cr e ating a G ame Loop with an AI P laye r
E a rlie r, we wrote a func tion c a lle d handle-human tha t inte ra c te d with a hum a n to de c ide on a m ove in the ga m e . He re is a n
a na logous func tion, handle-computer, tha t inte ra c ts with our AI pla ye r to c hoose a m ove :

(defun handle-computer (tree)

(let ((ratings (get-ratings tree (car tree))))

(cadr (nth (position (apply #'max ratings) ratings) (caddr tree)))))

T his handle-computer func tion is quite stra ightforwa rd. First, we ge t the ra tings of e a c h a va ila ble m ove . T he n we pic k

the m ove tha t is ra te d the highe st .


Fina lly, le t’s c re a te a func tion tha t ha ndle s the m a in loop for pla ying a ga inst the c om pute r. T his one is a na logous to our e a rlie r
play-vs-human func tion:
(defun play-vs-computer (tree)

(print-info tree)

(cond ((null (caddr tree)) (announce-winner (cadr tree)))

((zerop (car tree)) (play-vs-computer (handle-human tree)))

(t (play-vs-computer (handle-computer tree)))))

As with the play-vs-human func tion, play-vs-computer first prints out inform a tion a bout the c urre nt sta te of the ga m e . If

no m ore m ove s a re a va ila ble , it the n c a lls the announce-winner func tion .
Ne xt, we ne e d to c he c k who the c urre nt pla ye r is. By c onve ntion, we ’ll ha ve the hum a n be pla ye r A (pla ye r 0). If the pla ye r

num be r is 0, we c a ll our old handle-human func tion to le t the hum a n de c ide on he r m ove . Othe rwise , we tre a t the pla ye r a s

a n AI pla ye r a nd use the handle-computer func tion to de c ide on a m ove .


W e ha ve now writte n a fully func tiona l AI e ngine for Dic e of Doom !
P laying O ur F ir st H uman vs. Compute r G ame
T he following is a n e xa m ple ga m e pla ying a ga inst the c om pute r AI. T he c om pute r pla ys a n optim a l ga m e a nd wins.
> (play-vs-computer (game-tree (gen-board) 0 0 t))
current player = a
a-3 b-3
a-2 b-2
choose your move:
1. 0 -> 3
1
current player = a
a-1 b-3
a-2 a-2
choose your move:
1. end turn
1
current player = b
a-2 b-3
a-2 a-2
current player = b
b-2 b-1
a-2 a-2
current player = a
b-3 b-1
a-2 a-2
choose your move:
1. 3 -> 1
1
current player = a
b-3 a-1
a-2 a-1
choose your move:
1. end turn
1
current player = b
b-3 a-1
a-2 a-1
current player = b
b-1 a-1
b-2 a-1
current player = b
b-1 a-1
b-1 b-1
current player = a
b-2 a-1
b-2 b-1
The winner is b
M aking Dic e of Doom F aste r
T he func tiona l progra m m ing style c a n le a d to slow c ode , a t le a st in the ha nds of a novic e progra m m e r. W e use d the func tiona l
style to de ve lop the c ore of Dic e of Doom . He nc e , this first ve rsion of our ga m e is e xc ruc ia tingly ine ffic ie nt. W e ha d to lim it our
ga m e to a 2-by-2 boa rd to m a ke it pla ya ble . But now we c a n inc re a se our boa rd siz e to 3-by-3, a s we optim iz e our ga m e e ngine .
L e t’s inc re a se the pa ra m e te rs c ontrolling the boa rd siz e to m a ke this ha ppe n. You m a y not wa nt to pla y a ga m e a t this ne w siz e
until you’ve im ple m e nte d a ll the optim iz a tions throughout the re st of this c ha pte r, unle ss you a re a n e xtre m e ly pa tie nt pe rson a nd
don’t m ind ha ving the c om pute r ta ke m inute s building the initia l ga m e tre e a nd de c iding on m ove s.
(defparameter *board-size* 3)
(defparameter *board-hexnum* (* *board-size* *board-size*))
T he re , we ’ve upgra de d the boa rd siz e to 3 by 3.
T he re st of this c ha pte r c ove rs som e im porta nt te c hnique s for optim iz ing func tiona l c ode . T he se te c hnique s a pply to a ll progra m s
writte n in the func tiona l style , whic h inc lude s Dic e of Doom . In la te r c ha pte rs, we ’ll a dd othe r optim iz a tions. E ve ntua lly, we ’ll be
a ble to pla y a ga inst a n AI pla ye r on m uc h m ore spa c ious boa rds, while still ha ving e le ga nt c ode writte n in the func tiona l style .
Closur e s
Be fore we sta rt optim iz ing Dic e of Doom , the re is a n im porta nt L isp progra m m ing c onc e pt we ne e d to disc uss: c losure s. Closure s
a re e xtra bits of da ta from the outside world tha t a re c a pture d whe ne ve r a la m bda func tion is c re a te d. T o unde rsta nd the hows a nd
whys of c a pturing va ria ble s in a c losure , c onside r the following e xa m ple :
> (defparameter *foo* (lambda ()

5))
*FOO*

> (funcall *FOO*)


5

In this e xa m ple , we ’re c re a ting a ne w, unna m e d func tion , a nd the n se tting *foo* e qua l to this func tion. Ne xt, we c a ll this

func tion using the funcall c om m a nd . As you would e xpe c t, the va lue re turne d from this func tion is 5. All the la m bda
func tion doe s is re turn this num be r.
Ne xt, c onside r this m ore inte re sting e xa m ple :

> (defparameter *foo* (let ((x 5))


(lambda ()

x)))
*FOO*

T his ve rsion of foo is e xa c tly the sa m e a s the pre vious ve rsion of *foo*, e xc e pt tha t we first de c la re a loc a l va ria ble x ,

whic h is se t to 5. T he n, in the body of the lambda, we re turn x . So, wha t do you think will ha ppe n if we c a ll this ne w ve rsion
of *foo*?
T he re a son this is a tough que stion is tha t x is de c la re d a s a “ loc a l” va ria ble . Howe ve r, x (a ppa re ntly) no longe r e xists onc e we
c a ll *foo*, sinc e we ’re a lre a dy long pa st the point whe re we ’re e va lua ting the body of the let e xpre ssion.
L e t’s try it out a nd se e wha t ha ppe ns:
> (funcall *foo*)
5
Holy c ow! Som e how the la m bda e xpre ssion we c re a te d re m e m be re d wha t x wa s a t the tim e it wa s c re a te d. T he va ria ble x, whic h
we pre viously thought of a s a loc a l va ria ble , ha s som e how m a na ge d to live on pa st the bloc k in whic h it wa s c re a te d!
W he n we first c ove re d let e xpre ssions in Cha pte r 2, you le a rne d tha t a dva nc e d L ispe rs pre fe r to c a ll va ria ble s c re a te d with a
let e xpre ssion le x ic al v ariable s. Now you c a n se e why: A va ria ble c re a te d in this wa y doe s not ne e d to be loc a l, if it is c a pture d in
a c losure , by using the va ria ble in a la m bda e xpre ssion.
T o unde rsta nd how c losure s work, re m e m be r tha t L isp use s ga rba ge c olle c tion. In fa c t, it wa s the first la ngua ge to ha ve this
fe a ture . Ga rba ge c olle c tion m e a ns tha t you ne ve r ha ve to “ fre e ” va ria ble s (a s you do in C progra m m ing). T he L isp
c om pile r/inte rpre te r is sm a rt e nough to know whe n va ria ble s a re no longe r in use a nd de stroys the m a utom a tic a lly.
Ga rba ge c olle c tion will ha ppe n a t som e a rbitra ry future tim e a fte r you’ve e xite d a let e xpre ssion. Pe riodic a lly, L isp will se a rc h
its m e m ory for ite m s tha t a re no longe r re fe re nc e d a nywhe re a nd c a n the re fore be sa fe ly de stroye d. If L isp notic e s tha t a va ria ble
de fine d in a let is no longe r use d by a nything, it will de stroy tha t va ria ble .
Howe ve r, if you c re a te a la m bda e xpre ssion within the let e xpre ssion (a s we did in the pre viously), it’s possible for those
va ria ble s to live on, be ing re fe re nc e d from within the la m bda e xpre ssion. In tha t c a se , the ga rba ge c olle c tor will le a ve those
va ria ble s a lone . Ba sic a lly, you’ve c re a te d va ria ble s tha t a re pe rm a ne nt— a t le a st a s long a s the la m bda e xpre ssion doe sn’t fa ll out
of use a nd ge t ga rba ge c olle c te d.
You c a n do a lot of c ool things using c losure s. T he y’re ofte n use d for c a c hing sm a ll pie c e s of inform a tion be twe e n use s of a
func tion. For insta nc e , he re a func tion tha t re m e m be rs wha t line num be r is c urre ntly be ing printe d:

> (let ((line-number 0))

(defun my-print (x)

(print line-number)
(print x)

(incf line-number)
nil))
MY-PRINT
> (my-print "this")
0
"this"
nil
> (my-print "is")
1
"is"
nil
> (my-print "a")
2
"a"
nil
> (my-print "test")
3
"test"
nil

In orde r to ke e p tra c k of the line num be r, we first c re a te a le xic a l va ria ble na m e d line-number . Ne xt, we de c la re our my-

print func tion using defun , in the body of the let. T his c om m a nd will c re a te a la m bda func tion be hind the sc e ne s, the re fore
le tting us a lso ge ne ra te a c losure .

W ithin the body of the my-print func tion, we c a n the n print the line-number , a nd e ve n m uta te it using incf .
(incf just a dds one to a va ria ble . ) Be c a use the line-number va ria ble is c a pture d in the c losure , it c a n “ live on” be twe e n c a lls to
my-print, a llowing us to c ount line num be rs.
M e moiz ation
T he first optim iz a tion we ’re going to pe rform is c a lle d me moization. T his te c hnique m a ke s use of c losure s. Me m oiz a tion works
only for func tions writte n in the func tiona l style . As you know, the be ha vior of a func tion in the func tiona l style de pe nds only on
the a rgum e nts pa sse d into it. Also, the only a c tion of a func tion in the func tiona l style is to c a lc ula te a va lue to re turn to the
c a lle r.
T his sugge sts a n obvious optim iz a tion: W ha t if we re m e m be r the a rgum e nts a nd re sult of e a c h c a ll of this func tion? T he n, if the
func tion e ve r ge ts c a lle d a ga in with the sa m e a rgum e nts, we won’t ne e d to re c a lc ula te the re sult. Inste a d, we c a n sim ply re turn the
pre c a lc ula te d re sult.
Se ve ra l func tions in Dic e of Doom c a n be ne fit from m e m oiz a tion.

M e moiz ing the ne ighbor s F unc tion

L e t’s sta rt with the neighbors func tion, whic h le ts us know whic h he xa gons on the boa rd c a n be a tta c ke d from a give n loc a tion:
> (neighbors 0)
(3 1 4)
W ha t neighbors is te lling us is tha t if we wa nt to a tta c k othe r he xa gons on the boa rd from he xa gon 0, we c a n re a c h only
he xa gon 3, 1, or 4 (ba se d on our ne w 3-by-3 boa rd siz e ).
As you m a y re m e m be r, the neighbors func tion ne e de d to do a ll kinds of ugly c he c king for the e dge s of the boa rd, sinc e
he xa gons a long the e dge s a re lim ite d in the he xa gons the y c a n a tta c k. Howe ve r, sinc e the sha pe of the boa rd ne ve r c ha nge s m id-
ga m e , the se num be rs ne ve r c ha nge for a give n boa rd position. T his m a ke s neighbors a pe rfe c t c a ndida te for m e m oiz a tion! He re is
the c ode tha t a c c om plishe s this:

(let ((old-neighbors (symbol-function 'neighbors))

(previous (make-hash-table)))

(defun neighbors (pos)

(or (gethash pos previous)

(setf (gethash pos previous) (funcall old-neighbors pos)))))


L e t’s disse c t this c ode to m a ke se nse of wha t’s ha ppe ning. First, we sa ve the old ve rsion of the neighbors func tion in a loc a l

va ria ble na m e d old-neighbors . T he symbol-function c om m a nd sim ply re trie ve s the func tion bound to a sym bol. Using
symbol-function he re a llows us to re ta in a c c e ss to the old va lue of neighbors, e ve n if we de fine a ne w func tion with the sa m e
na m e , a s we ’ll do shortly.

Ne xt, we de fine a loc a l va ria ble previous , whic h will hold a ll pre vious a rgum e nts a nd re sults the func tion ha s e ve r se e n.
T his c a n be re pre se nte d a s a ha sh ta ble , whe re the a rgum e nts a re the ha sh ke y a nd the re sults a re the va lue s.

Now we de fine a ne w neighbors func tion tha t will ove rride the old de finition of neighbors . T his ne w de finition will a dd
m e m oiz a tion to the old ve rsion of the func tion. T he n we look up the a rgum e nt pos in the ha sh ta ble a nd re turn it, if a va ila ble

. Othe rwise , we c a ll the old de finition of the func tion (tha t’s why we ne e de d to c re a te the old-neighbors le xic a l va ria ble )

a nd a dd this ne w a rgum e nt/re sult pa ir to the ha sh ta ble . Sinc e setf re turns the va lue be ing se t, this c om m a nd will a lso c a use
this ne wly c a lc ula te d re sult to be re turne d to the c a lle r of neighbors.

Note

Be c a re ful not to de c la re the m e m oiz e d ve rsion of the neighbors func tion m ore tha n onc e , without a lso re de c la ring the origina l
ve rsion of the func tion. Othe rwise , the neighbors func tion will be wra ppe d in m ultiple unsightly la ye rs of m e m oiz a tion, sinc e
the re a re no c he c ks if the m e m oiz a tion ha s a lre a dy be e n done .

M e moiz ing the G ame Tr e e

T he bigge st pa yoff by fa r for m e m oiz a tion in our progra m will be in the game-tree func tion. T his m a ke s se nse , if you think
a bout how a boa rd ga m e works. Ve ry ofte n, you c a n ge t the sa m e boa rd positions in a boa rd ga m e by pe rform ing the sa m e m ove s in
a slightly diffe re nt orde r. In our na ive ve rsion of the game-tree func tion, e ve ry diffe re nt m ove se que nc e le a ds to a c om ple te ly
diffe re nt bra nc h in the ga m e tre e tha t we ne e d to build in a tota lly re pe titive a nd ine ffic ie nt wa y.
In the m e m oiz e d ve rsion of the game-tree c ode , the func tion c a n sa y to itse lf, “ He y, I’ve se e n tha t boa rd position be fore !” a nd
c a n the n sha re bra nc he s of the ga m e tre e . He re is a m e m oiz e d ve rsion of game-tree tha t doe s this:
(let ((old-game-tree (symbol-function 'game-tree))

(previous (make-hash-table :test #'equalp)))


(defun game-tree (&rest rest)
(or (gethash rest previous)
(setf (gethash rest previous) (apply old-game-tree rest)))))
As you c a n se e , this m e m oiz a tion is virtua lly ide ntic a l to the one we use d for the neighbors func tion. T he only diffe re nc e is
tha t we ’re se tting the ha sh ta ble to use equalp inste a d of eql (the de fa ult) for the te st on the ke y .
T his is be c a use the ke y (tha t is, the a rgum e nts to game-tree) c onta ins the ga m e boa rd, in the form of a n a rra y. If we c ha nge the
te st func tion to be equalp, the n L isp will c he c k e ve ry he xa gon on the boa rd a nd m a ke sure it m a tc he s be fore using a pre vious
c a lc ula tion.

M e moiz ing the r ate -position F unc tion

Anothe r func tion tha t will be ne fit gre a tly from m e m oiz a tion is the rate-position func tion. He re it is, m e m oiz e d:
(let ((old-rate-position (symbol-function 'rate-position))

(previous (make-hash-table)))
(defun rate-position (tree player)

(let ((tab (gethash player previous)))

(unless tab

(setf tab (setf (gethash player previous) (make-hash-table))))


(or (gethash tree tab)

(setf (gethash tree tab)


(funcall old-rate-position tree player))))))
W e ne e d to do som e thing a bit spe c ia l for the m e m oiz a tion on this func tion to work c orre c tly, be c a use of the tree a rgum e nt
pa sse d into ra te -position. T he ga m e tre e is pote ntia lly huge , so we ne e d to m a ke sure we ne ve r c om pa re a ga m e tre e obje c t with
equal (or a sim ila r c om pa rison func tion tha t is slow with la rge lists). Inste a d, we wa nt to c om pa re it with eql. Be c a use of this, we
ha ndle the m e m oiz a tion of e a c h of the two pa ra m e te rs to rate-position (tree a nd player) se pa ra te ly. W e a c c om plish this by
ha ving ne ste d ha sh ta ble s.

First, we c re a te a n oute r ha sh ta ble with the de fa ult eql te st . T he n, we de fine a tab va ria ble tha t looks up one of our

va ria ble s (player) in the oute r ha sh ta ble , to re trie ve a n inne r ha sh ta ble . If tab is not found in the oute r ha sh ta ble ,

we ’ll c re a te a ne w, e m pty inne r ha sh ta ble , storing it in the oute r ha sh ta ble with the sa m e ke y . T he re st of the func tion is

sim ila r to our pre vious e xa m ple s, e xc e pt tha t we ’re now using our inne r ha sh ta ble , with the tree a rgum e nt a s a ke y .
T his m e m oiz a tion will bring us a ste p c lose r to ha ving la rge r, a nd m ore fun, boa rds for Dic e of Doom .

Note

You use m e m oiz a tion for optim iz ing the pe rform a nc e of c ode writte n in the func tiona l style . Howe ve r, m e m oiz a tion c ode is not,
in itse lf, writte n in the func tiona l style . It c a nnot be , sinc e it re quire s you to m a inta in a nd upda te a ta ble of pre vious c a lls to the
ta rge t func tion.
Tail Call O ptimiz ation
T he ne xt te c hnique we ’re going to use to optim iz e our func tiona l progra m is c a lle d tail c all optimization. T o unde rsta nd this
c onc e pt, le t’s study a sim ple func tion tha t c a lc ula te s the le ngth of a list:
> (defun my-length (lst)

(if lst

(1+ (my-length (cdr lst)))

0))
MY-LENGTH
> (my-length '(fie foh fum))
3

T he my-length func tion should be pre tty e a sy for you to unde rsta nd a t this point. First, it c he c ks if the list is e m pty . If

not, it re c ursive ly c a lls itse lf a ga inst the ta il of the list a nd a dds one to the tota l, using the 1+ func tion . If the list is e m pty,

the func tion just re turns 0 .


It turns out tha t this func tion is a c tua lly quite ine ffic ie nt. W e c a n e a sily se e this by trying to use it a ga inst a re a lly big list:
> (defparameter *biglist* (loop for i below 100000 collect 'x))
*BIGLIST*
> (my-length *biglist*)
*** - Program stack overflow. RESET
Ca lling this func tion in CL ISP a c tua lly c a use s the progra m to c ra sh! (Othe r Com m on L isp c om pile rs/inte rpre te rs m a y do be tte r,
de pe nding on whe the r the c om pile r write rs use a ny spe c ia l tric ks to a ntic ipa te this c om m on pitfa ll in L isp c ode . )
T his ha ppe ns be c a use of the 1+ func tion. It te lls L isp, “ F irst, figure out the le ngth of the shorte r list, the n c a ll 1+ on the re sult. ”
T he proble m is tha t e a c h tim e we c a ll my-length re c ursive ly, L isp m ust re m e m be r tha t we ne e d to a dd one to the re sult la te r
on, onc e the le ngth of the ta il of the list ha s be e n figure d out. Sinc e the list is 100, 000 ite m s long, it m ust re m e m be r this 99, 999
tim e s be fore it c a n pe rform a single a ddition! T he CL ISP inte rpre te r pla c e s a re m inde r for a ll of the se a dditions on the progra m
sta c k, whic h e ve ntua lly ove rflows, c ra shing the progra m .
So how do we a void this proble m ? W e do it by re writing our my-length func tion like so:
> (defun my-length (lst)

(labels ((f (lst acc)

(if lst

(f (cdr lst) (1+ acc))

acc)))

(f lst 0)))
MY-LENGTH
> (my-length '(fie foh fum))
3

He re , we de fine a loc a l func tion f tha t will a c t a s our list-e a te r. T his func tion ta ke s a n e xtra pa ra m e te r, ofte n c a lle d a n

accumulator, he re shorte ne d to acc . T his acc a rgum e nt ke e ps a running c ount of how m a ny ite m s in the list we ha ve

pre viously e nc ounte re d. W he n we initia lly c a ll the func tion f, we se t acc to 0 .

By m a king this a c c um ula tor a va ila ble , it m e a ns tha t whe n f c a lls itse lf re c ursive ly , it now longe r ne e ds to a dd one to the

re sult. Inste a d, it just a dds one to the a c c um ula tor. Onc e we re a c h the e nd of the list (lst is nil ), the n acc will e qua l the

tota l num be r of ite m s in the list, so we c a n just re turn it .


W ha t is im porta nt he re is tha t the v e ry last thing the func tion f doe s, in the c a se whe re m ore ite m s a re on the list, is c a ll itse lf

re c ursive ly . (T he a dditiona l line in the if sta te m e nt doe sn’t c ount, sinc e tha t pa rt won’t be c a lle d if the e xpre ssion
e va lua te s to true . ) W he n a func tion in L isp c a lls itse lf (or a nothe r func tion) a s its ve ry la st a c tion, we c a ll this a c tion a tail c all. A
sm a rt L isp c om pile r, whe n se e ing a ta il c a ll, c a n the n sa y to itse lf, “ He y, sinc e I don’t ne e d to do a nything m ore a fte r c a lling f
a ga in, I c a n just go stra ight to f, without ne e ding to put the c urre nt progra m c onte xt on the sta c k. ”
T his is a c tua lly sim ila r to pe rform ing a GOTO in BASIC or a longjmp in C++. In a ll of the se c a se s, we just “ forge t” whe re we
c a m e from , whic h is ve ry fa st a nd doe sn’t thra sh the sta c k. Howe ve r, in the c a se of a ta il c a ll in L isp, it is a lso pe rfe c tly sa fe .
Anyone who ha s use d GOTO or longjmp knows the y’re a nything but sa fe !
Notic e tha t the re a re two diffe re nt de finitions for lst tha t e xist in the pre c e ding e xa m ple c ode . One is a n a rgum e nt to the my-
length func tion, a nd the othe r is a n a rgum e nt to the func tion f . T he va lue s of the se two lst a rgum e nts will de via te a s the
progra m runs a nd f is c a lle d re c ursive ly. Howe ve r, within the func tion f, the ve rsion in its own a rgum e nt list will ta ke pre c e de nc e .
T his proc e ss of hiding one va ria ble with a nothe r through pre c e de nc e is c a lle d v ariable shadowing.

Note

I use d va ria ble sha dowing in the my-length func tion so it would be im possible for m e to a c c ide nta lly use the “ wrong list” whe n
writing the c ode inside of func tion f. Othe r progra m m e rs dislike this te c hnique , sinc e ha ving sim ila rly na m e d va ria ble s with
diffe re nt va lue s c a n le a d to c onfusion. You’ll ne e d to de c ide whic h of the se a rgum e nts is m ost c onvinc ing to you a nd whe the r
you’ll use va ria ble sha dowing in your own c ode .

Suppor t for Tail Calls in Common Lisp

Unfortuna te ly, you c a n’t be 100 pe rc e nt sure in Com m on L isp tha t a c om pile r/inte rpre te r will pe rform ta il c a ll optim iz a tions. It
is not re quire d by the ANSI Com m on L isp sta nda rd. (T he situa tion is a c tua lly diffe re nt in the Sc he m e dia le c t, sinc e Sc he m e ha s a
stric t re quire m e nt for ta il c a ll optim iz a tion. )
Howe ve r, m ost Com m on L isp c om pile rs support this fe a ture , a lthough CL ISP re quire s som e e xtra c a joling to m a ke ta il c a ll
optim iz a tion work for som e func tions, inc luding our e xa m ple func tion. T he re a son for this is tha t ta il c a lls c a n a c tua lly le a d to
pe rform a nc e proble m s the m se lve s, in som e e sote ric c a se s. Also, whe n we de bug a progra m , it’s nic e to be a ble to look a t the full
c a ll sta c k; ta il c a ll optim iz a tions will pre ve nt this, sinc e , by the ir na ture , the y will m inim iz e the inform a tion a va ila ble on the
sta c k.
He re ’s the e xtra ste p we ne e d to ta ke to ge t CL ISP to ta il c a ll optim iz e the my-length func tion:
(compile 'my-length)
Ca lling this func tion will te ll CL ISP to run the my-length func tion through its full c om pile r, whic h inc lude s a ta il c ode
optim iz a tion ste p. Now we c a n run my-length a ga inst our jum bo-siz e d list!
> (my-length *biglist*)
100000

Tail Call O ptimiz ation in Dic e of Doom

One func tion in our ga m e tha t c ould de finite ly be ne fit from ta il c a ll optim iz a tion is the add-new-dice func tion. He re ’s the fully
optim iz e d ve rsion:
(defun add-new-dice (board player spare-dice)

(labels ((f (lst n acc)


(cond ((zerop n) (append (reverse acc) lst))

((null lst) (reverse acc))


(t (let ((cur-player (caar lst))
(cur-dice (cadar lst)))
(if (and (eq cur-player player)
(< cur-dice *max-dice*))
(f (cdr lst)
(1-n)

(cons (list cur-player (1+ cur-dice)) acc))

(f (cdr lst) n (cons (car lst) acc))))))))


(board-array (f (coerce board 'list) spare-dice ()))))

As be fore , we ’re pe rform ing the list-e a ting in a func tion c a lle d f , whic h a lso ha s a n a c c um ula tor. Howe ve r, this tim e the
acc va ria ble will c onta in a list of ne wly upda te d he xa gons with e xtra dic e . W e c a n now c a ll f in ta il c a ll positions in two pla c e s

, whe re we cons ne w he xa gons to the acc va ria ble .


Onc e we ’ve proc e sse d the whole list of he xa gons on the boa rd, we c a n just re turn acc. Howe ve r, sinc e we ’ve c onse d stuff to acc
a s we we nt a long the list, acc will a c tua lly be re ve rse d. T he re fore , we ne e d to pe rform a n e xtra c a ll to reverse a t the ve ry e nd

.
W e ha ve now e xplore d som e ba sic te c hnique s for optim iz ing c om pute r progra m s writte n in the func tiona l style .
A Sample G ame on the 3-by-3 Boar d
Now le t’s e njoy the fruits of our la bor. T he following is a full ga m e a ga inst the AI pla ye r on a 3-by-3 boa rd. As you c a n se e , on
a n e ve nly m a tc he d sta rting boa rd, the c om pute r is now pra c tic a lly unbe a ta ble .
> (play-vs-computer (game-tree (gen-board) 0 0 t))
current player = a
b-1 a-2 a-3
a-1 b-1 b-2
b-2 a-2 b-3
choose your move:
1. 1 -> 4
2. 1 -> 0
3. 2 -> 5
4. 7 -> 4
3
current player = a
b-1 a-2 a-1
a-1 b-1 a-2
b-2 a-2 b-3
choose your move:
1. end turn
2. 1 -> 4
3. 1 -> 0
4. 5 -> 4
5. 7 -> 4
1
current player = b
b-1 a-3 a-1
a-1 b-1 a-2
b-2 a-2 b-3
current player = b
b-1 a-3 a-1
b-1 b-1 a-2
b-1 a-2 b-3
current player = a
b-1 a-3 a-1
b-1 b-1 a-2
b-1 a-2 b-3
choose your move:
1. 1 -> 4
2. 1 -> 0
3. 5 -> 4
4. 7 -> 4
5. 7 -> 3
6. 7 -> 6
1
current player = a
b-1 a-1 a-1
b-1 a-2 a-2
b-1 a-2 b-3
choose your move:
1. end turn
2. 4 -> 0
3. 4 -> 3
4. 7 -> 3
5. 7 -> 6
1
current player = b
b-1 a-1 a-1
b-1 a-2 a-2
b-1 a-2 b-3
current player = b
b-1 a-1 a-1
b-1 a-2 b-2
b-1 a-2 b-1
current player = a
b-2 a-1 a-1
b-1 a-2 b-2
b-1 a-2 b-1
choose your move:
1. 4 -> 3
2. 4 -> 8
3. 7 -> 3
4. 7 -> 6
5. 7 -> 8
2
current player = a
b-2 a-1 a-1
b-1 a-1 b-2
b-1 a-2 a-1
choose your move:
1. end turn
2. 7 -> 3
3. 7 -> 6
1
current player = b
b-2 a-1 a-1
b-1 a-1 b-2
b-1 a-2 a-1
current player = b
b-1 b-1 a-1
b-1 a-1 b-2
b-1 a-2 a-1
current player = a
b-1 b-1 a-1
b-1 a-1 b-2
b-1 a-2 a-1
choose your move:
1. 7 -> 3
2. 7 -> 6
1
current player = a
b-1 b-1 a-1
a-1 a-1 b-2
b-1 a-1 a-1
choose your move:
1. end turn
1
current player = b
b-1 b-1 a-1
a-1 a-1 b-2
b-1 a-1 a-1
current player = b
b-1 b-1 b-1
a-1 a-1 b-1
b-1 a-1 a-1
current player = a
b-1 b-1 b-1
a-1 a-1 b-1
b-1 a-1 a-1
The winner is b
W hat You've Le ar ne d
In this c ha pte r, we use d our knowle dge of func tiona l progra m m ing to de ve lop a boa rd ga m e with AI. Along the wa y you le a rne d
the following:
Func tiona l progra m m ing te c hnique s a llow you to write a ga m e progra m with a “ rule e ngine ” tha t is se pa ra te from the re st of
the c ode . You a c c om plish this by using func tion pipe lining a nd building a game tre e tha t is inde pe nde ntly tra ve rse d by othe r pa rts
of your ga m e c ode a s the ga m e progre sse s.
You c a n c re a te a n AI pla ye r for a two-pla ye r ga m e using the minimax algorithm. T his a lgorithm is ba se d on the truism “ W ha t
is good for m y e ne m y is ba d for m e . ” It a llows you to e ffic ie ntly ra te positions in a two-pla ye r boa rd ga m e .
L e xic a l va ria ble s (whic h we ’ve be e n c a lling loc al va ria ble s) c a n live on pa st the form in whic h the y we re c re a te d if the y a re
re fe re nc e d by a la m bda e xpre ssion. Ca pturing va ria ble s in this wa y is c a lle d c re ating a c losure .
Func tiona l progra m s c a n be optim iz e d using me moization, whic h re quire s you to c a c he pre vious re sults c a lc ula te d by a
func tion.
You c a n a lso im prove func tiona l progra m s by using tail c all optimizations, whic h a llow you to m a ke sure the c a ll sta c k isn’t
a buse d. You do this by c ontrolling whic h func tion a ppe a rs in the ta il c a ll (fina l) position of your list-e a te r func tions.
Chapte r 16. The M agic of Lisp M ac r os
Mac ro programming a llows you to m e ss a round inside your L isp c om pile r/inte rpre te r to turn L isp into your own c ustom
progra m m ing la ngua ge . W he n fa c e d with a diffic ult progra m m ing c ha lle nge , m a ny e xpe rie nc e d L ispe rs will first a sk the m se lve s,
“ W ha t progra m m ing la ngua ge c ould I use to m a ke this proble m e a sy to solve ? ” T he n the y’ll use m a c ros to c onve rt L isp into that
la ngua ge !
No othe r progra m m ing la ngua ge posse sse s suc h a sim ple a nd c om pre he nsive m a c ro syste m . One c a n e ve n a rgue tha t it would be
im possible to a dd this fe a ture to othe r progra m m ing la ngua ge s, for a sim ple re a son: T he L isp la ngua ge s a re the only one s in whic h
c om pute r c ode a nd progra m da ta a re m a de out of the sa m e “ stuff. ” As disc usse d m a ny tim e s in this book, the funda m e nta l
struc ture s for storing da ta in L isp a re sym bols, num be rs, a nd lists, whic h a re m a de of c ons c e lls. Sim ila rly, the c ode of a L isp
progra m is m a de out of the se sa m e ba sic building bloc ks. As you’ll se e in this c ha pte r, this sym m e try be twe e n c ode a nd da ta in
L isp is the m a gic tha t m a ke s the L isp m a c ro syste m possible .

Note

You m a y ha ve he a rd tha t othe r progra m m ing la ngua ge s, suc h a s C++, a lso ha ve a fe a ture c a lle d m a c ros. For insta nc e , in the C++
la ngua ge , you would c re a te the se using the #de fine dire c tive . Howe ve r, the se a re not the sa m e thing! L isp m a c ros work in a n
e ntire ly diffe re nt a nd fa r m ore sophistic a te d wa y.
A Simple Lisp M ac r o
Som e tim e s whe n you’re writing a c om pute r progra m , you ge t a fe e ling of dé jà v u. I’m sure you know this fe e ling. You’re typing
a wa y a t your c om pute r, a nd you sudde nly re a liz e , “ He y, this is the third tim e this we e k I’ve writte n this sa m e fra gm e nt of c ode !”
Suppose , for e xa m ple , tha t your progra m ne e ds a spe c ia l add func tion:
(defun add (a b)

(let ((x (+ a b)))


(format t "The sum is ˜a" x)
x))
T his func tion a dds toge the r two num be rs a nd prints out the sum on the RE PL a s a side e ffe c t. You m ight find this func tion use ful
in a progra m during de bugging:
> (add 2 3)
The sum is 5
5
T his add func tion se e m s stra ightforwa rd, but its c ode ha s a n a nnoya nc e : W hy do you ne e d so m a ny pa re nthe se s to de c la re your

va ria ble x ? T he let c om m a nd re quire s so m a ny pa re nthe se s tha t whe n you ne e d only a single va ria ble , the c ode e nds up
looking e spe c ia lly ludic rous.
T he pa re nthe se s re quire d by let a re a n e xa m ple of the kind of v isual noise a progra m m e r m ust de a l with a lm ost e ve ry da y.
Howe ve r, you c a n’t just write a re gula r func tion to hide those pa re nthe se s, be c a use the let c om m a nd c a n do things a re gula r L isp
func tion c a n’t support. T he let c om m a nd is a spe c ial form. It’s a c ore pa rt of the la ngua ge a nd ha s spe c ia l powe rs be yond those of
a sta nda rd L isp func tion.
Ma c ros le t us ge t rid of the supe rfluous pa re nthe se s. L e t’s c re a te a ne w m a c ro na m e d let1:

(defmacro let1 (var val &body body)


`(let ((,var ,val))
,@body))
As you c a n se e , the de finition of a m a c ro looks sim ila r to the de finition of a func tion. Howe ve r, inste a d of using defun, we use

defmacro to de fine it. L ike a func tion, it ha s a na m e (in this c a se , let1) a nd a rgum e nts pa sse d to it .
Onc e we ’ve de fine d the m a c ro let1, it c a n be use d just like let, e xc e pt tha t it works with fe we r pa re nthe se s:
> (let ((foo (+ 2 3)))
(* foo foo))
25
> (let1 foo (+ 2 3)
(* foo foo))
25
M ac r o Expansion
Although a m a c ro de finition looks ve ry sim ila r to a func tion de finition, a m a c ro is a c tua lly ve ry diffe re nt from a func tion. T o
unde rsta nd why, im a gine your L isp is a c tua lly a c ute little blob, m e rrily running your L isp progra m s.

T his blob unde rsta nds only sta nda rd L isp c ode . If it we re to se e our let1 c om m a nd, it would ha ve no ide a wha t to do.

Now im a gine tha t we ha ve a m a gic wa nd tha t tra nsform s the a ppe a ra nc e of our c ode just be fore L isp ge ts a pe e k a t it. In our
e xa m ple , it will tra nsform let1 into a re gula r let, so L isp will sta y ha ppy.
T his m a gic wa nd is c a lle d mac ro e x pansion. T his is a spe c ia l tra nsform a tion tha t your c ode is put through be fore the c ore of the
L isp inte rpre te r/c om pile r ge ts to se e it. T he job of the m a c ro e xpa nde r is to find a ny m a c ros in your c ode (suc h a s our le t1 m a c ro)
a nd to c onve rt the m into re gula r L isp c ode .
T his m e a ns a m a c ro is run a t a diffe re nt time tha n a func tion is run. A re gula r L isp func tion runs whe n you e xe c ute a progra m
tha t c onta ins the func tion. T his is c a lle d runtime . A m a c ro, on the othe r ha nd, runs be fore the progra m doe s, whe n the progra m is
re a d a nd c om pile d by your L isp e nvironm e nt. T his is c a lle d mac ro e x pansion time .
Now tha t we ’ve disc usse d the ba sic thinking be hind L isp m a c ros, le t’s ta ke a c lose r look a t how let1 wa s de fine d.
H ow M ac r os Ar e Tr ansfor me d
W he n we de fine a ne w m a c ro with the defmacro c om m a nd, we ’re ba sic a lly te a c hing the L isp m a c ro e xpa nsion syste m a ne w
tra nsform a tion tha t it c a n use to tra nsla te c ode be fore running a progra m . T he m a c ro re c e ive s ra w sourc e c ode in its a rgum e nts, in
the form of L isp e xpre ssions. Its job is to he lp the m a c ro e xpa nde r tra nsform this ra w c ode into sta nda rd L isp c ode tha t ke e ps the
L isp blob ha ppy.
L e t’s ta ke a c lose r look a t how our let1 m a c ro ge ts tra nsform e d. He re is its de finition onc e a ga in:

(defmacro let1 (var val &body body)


`(let ((,var ,val))
,@body))

T he first line of this defmacro c a ll te lls the m a c ro e xpa nde r, “ He y, if you se e a form in c ode tha t be gins with le t1, he re ’s
wha t you ne e d to do to tra nsform it into sta nda rd L isp. ” A m a c ro de fine d with defmacro m a y a lso ha ve a rgum e nts pa sse d into it,
whic h will c onta in the ra w sourc e c ode found inside the m a c ro whe n the m a c ro is use d. T he let1 m a c ro ha s thre e suc h a rgum e nts

pa sse d into it: var, val, a nd body . So wha t do the se thre e a rgum e nts re pre se nt?

As you c a n se e , whe n we use let1, we ’ll e nd up ha ving thre e diffe re nt e xpre ssions inside it, whic h a re the a rgum e nts to the let1
m a c ro:
var
T he first a rgum e nt is the na m e of the va ria ble we ’re de fining. T his na m e will be a va ila ble within our m a c ro using the
a rgum e nt na m e d var. In this e xa m ple , it will e qua l the sym bol foo.
val
T he se c ond e xpre ssion holds the c ode tha t de te rm ine s the va lue of the va ria ble . In our m a c ro, this is the se c ond a rgum e nt,
val. It will e qua l the list (+ 2 3).
body
T he third e xpre ssion inside a let1 c a ll is the body c ode , whic h m a ke s use of the ne w va ria ble tha t’s c re a te d (in this c a se ,
foo). It will be a va ila ble in the m a c ro through the a rgum e nt na m e d body.
Sinc e the let c om m a nd is a llowe d to ha ve m ultiple sta te m e nts in its body, we will wa nt to m irror this be ha vior in the let1
m a c ro. T his is why, in the defmacro c om m a nd de fining le t1, the fina l body a rgum e nt ha s the spe c ia l ke yword &body in front of it.
T his te lls the m a c ro e xpa nde r “ Give m e a ll re m a ining e xpre ssions in the m a c ro in a list. ” Be c a use of this, the body a rgum e nt in
our let1 e xa m ple is a c tua lly ((* foo foo))— a ne ste d list. In this e xa m ple , we put only a single sta te m e nt inside let1.
Now tha t you’ve se e n wha t the va lue s to the a rgum e nts of our let1 m a c ro a re , le t’s se e how the m a c ro use s this inform a tion to
tra nsform the let1 into a sta nda rd let tha t the L isp c om pile r c a n unde rsta nd. T he e a sie st wa y to tra nsform sourc e c ode in L isp is
to use ba c kquote synta x. (If you don’t re m e m be r how to use ba c kquote s, ple a se se e How Qua siquoting W orks in How Qua siquoting
W orks. ) W ith ba c kquote s, we c a n build the c ode for a prope r let c om m a nd using c ode pa sse d to let1. He re ’s our let1 m a c ro
a ga in for re fe re nc e :
(defmacro let1 (var val &body body)

`(let ((,var ,val))

,@body))

As you c a n se e , the let1 m a c ro re turns a ba c kquote d list sta rting with the sym bol let , followe d by the va ria ble na m e a nd
va lue , pla c e d in a prope r ne ste d list, whic h L isp’s let c om m a nd re quire s. T he c om m a s c a use the a c tua l va ria ble na m e a nd va lue
to be ploppe d in a t the se loc a tions. Fina lly, we pla c e the body c ode from the let1 in the a na logous pla c e in the let c om m a nd

.
T he body a rgum e nt is inse rte d into the tra nsform e d c ode using the splic ing c om m a (,@). T o unde rsta nd why the body ne e ds to be
ha ndle d in this spe c ia l wa y, c onside r the following use of our let1 m a c ro:
> (let1 foo (+ 2 3)
(princ "Lisp is awesome!")
(* foo foo))
Lisp is awesome!
25
In this c a se , we ’ve put m ore tha n one thing inside the body of our let. Re m e m be r tha t the let c om m a nd inc lude s a n im plic it
progn c om m a nd, a nd it c a n ha ve m ultiple L isp instruc tions inside . Our ne w let1 m a c ro a llows for this a s we ll by pla c ing the
spe c ia l &body m a rke r in front of the body a rgum e nt, c a using a ll re m a ining synta x e xpre ssions to be pa sse d into let1 a s a list. So, in
the pre c e ding e xa m ple , the body a rgum e nt c onta ins the c ode ((princ "Lisp is awesome!") (* foo foo)).
Using the Simple M ac r o
Now tha t we ’ve writte n our let1 m a c ro, le t’s re write our c ustom add func tion in a c le a ne r wa y:
(defun add (a b)
(let1 x (+ a b)
(format t "The sum is ˜a" x)
x))
Isn’t this m uc h e a sie r on the e ye s?
W e c a n use the macroexpand c om m a nd to se e c ode ge ne ra te d by a m a c ro. Sim ply pa ss the m a c ro’s c ode to macroexpand, like
this:
> (macroexpand '(let1 foo (+ 2 3)
(* foo foo)))

(LET ((FOO (+ 2 3))) (* FOO FOO)) ;

You c a n now se e the ra w c ode ge ne ra te d by let1 . T he T a t the e nd just m e a ns macroexpand wa s ha nde d a va lid
m a c ro tha t it wa s a ble to e xpa nd.
As your m a c ros be c om e m ore c om ple x, you’ll find tha t macroexpand is a va lua ble tool in te sting a nd de bugging the ir struc ture .
M or e Comple x M ac r os
L e t’s suppose you ne e d a c ustom my-length c om m a nd. T his is a c la ssic list-e a ting func tion tha t will c ount the le ngth of a list.
W e ’ll write it in the prope r “ ta il c a ll optim iz e d” style (disc usse d in Cha pte r 14), whe re the re c ursive func tion c a ll is in the ta il
position. He re ’s the c ode :
(defun my-length (lst)

(labels ((f (lst acc)

(if lst

(f (cdr lst) (1+ acc))


acc)))
(f lst 0)))
As you c a n se e , this func tion ha s tons of re pe titive stuff, onc e a ga in giving us tha t dre a de d fe e ling of dé jà v u. T he re a re two
re pe titive pa tte rns in this func tion:

As in othe r list-e a te r func tions, we ha ve the a nnoying c he c k to se e if the list is e m pty a nd the a ssoc ia te d use of cdr

W e did a ll this ve rbose work to c re a te a loc a l func tion f .


L e t’s write som e m a c ros tha t m a ke this func tion (a nd othe r func tions with the sa m e re pe tition) m ore pithy.
A M ac r o for Splitting Lists
First, le t’s c re a te a split m a c ro. It will le t us write c le a ne r list-e a te r func tions, suc h a s our my-length func tion.
L ist-e a te rs a lwa ys c he c k if the list is e m pty. If it isn’t, the y ta ke a pa rt the list using car a nd/or cdr, a nd the n pe rform ope ra tions
on the he a d a nd/or ta il of the list. T he split m a c ro doe s this for us. He re ’s wha t it looks like whe n we use the finishe d split
m a c ro:

> (split '(2 3)

(format t "This can be split into ˜a and ˜a." head tail)


(format t "This cannot be split."))
This can be split into 2 and (3).

> (split '()


(format t "This can be split into ˜a and ˜a." head tail)

(format t "This cannot be split."))


This cannot be split.

T he first a rgum e nt of the split m a c ro is a list you wa nt to split into a he a d a nd a ta il . If this is possible , the ne xt

e xpre ssion in the split m a c ro will be c a lle d . As a bonus, our split m a c ro a utom a tic a lly c re a te s two va ria ble s for us, na m e d

head a nd tail. T his wa y, we don’t a lwa ys ne e d to c a ll car a nd cdr inside list-e a ting func tions. If the list is e m pty , we c a ll

the e xpre ssion a t the e nd .


L e t’s look a t the c ode for the split m a c ro. Note tha t this initia l ve rsion of the m a c ro c onta ins som e bugs we ’ll disc uss shortly:
;Warning! Contains Bugs!

(defmacro split (val yes no)

`(if ,val

(let ((head (car ,val))

(tail (cdr ,val)))

,yes)

,no))

Our split m a c ro re quire s thre e (a nd only thre e ) e xpre ssions a s a rgum e nts . T his m e a ns whe n we use this m a c ro, we ’ll
a lwa ys ne e d e xa c tly thre e ite m s.

T he c ode tha t ne e ds to be ge ne ra te d by split is pre tty stra ightforwa rd. First, we ha ve a n if tha t c he c ks if the list is e m pty

. If it is, we bre a k a pa rt the list a nd stic k it into our two loc a l va ria ble s, head a nd tail . T he n we put in the c ode tha t

ha ndle s the “ ye s, we c a n split the list” c a se . If we c a n’t split the list, we c a ll the no c a se . Note tha t in the no c a se ,
we don’t ha ve a c c e ss to the head/tail va ria ble s, sinc e the y a re n’t c re a te d if the list c a n’t be split.
W ith this ne w split m a c ro, we c a n c le a n up our my-length m a c ro a bit:
(defun my-length (lst)
(labels ((f (lst acc)
(split lst

(f tail (1+ acc))


acc)))
(f lst 0)))

Notic e how we now m a ke use of the tail va ria ble c re a te d by split, sim plifying our c ode . Ma c ros tha t a utom a tic a lly
ge ne ra te va ria ble s like this a re c a lle d anaphoric mac ros.
Howe ve r, we a re not ye t finishe d with our split m a c ro. Although it ba sic a lly works, it c onta ins som e subtle bugs tha t we ne e d to
a ddre ss.
Avoiding Re pe ate d Exe c ution in M ac r os
One c om m on bug tha t c a n ha ppe n in a m a c ro is inc orre c t re pe a te d e xe c ution of c ode . In fa c t, our c urre nt ve rsion of the split
m a c ro c onta ins this fla w. He re is a n e xa m ple tha t c le a rly shows the proble m :

> (split (progn (princ "Lisp rocks!")


'(2 3))
(format t "This can be split into ˜a and ˜a." head tail)
(format t "This cannot be split."))
Lisp rocks!Lisp rocks!Lisp rocks!This can be split into 2 and (3).
In this use of split, the sta te m e nt “ L isp roc ks!” wa s printe d thre e tim e s, e ve n though it a ppe a rs only onc e in the origina l c ode .
How is this possible ?
Re m e m be r tha t the a rgum e nts pa sse d into a m a c ro c onsist of ra w sourc e c ode . T his m e a ns the val a rgum e nt pa sse d into split

c onta ins the ra w c ode of the progn sta te m e nt , inc luding the ra w c ode for the princ sta te m e nt within it. Sinc e we re fe re nc e
val thre e tim e s inside the split m a c ro, it c a use s the princ sta te m e nt to be e xe c ute d thre e tim e s.
W e c a n ve rify this by running this e xa m ple through macroexpand:
> (macroexpand '(split (progn (princ "Lisp rocks!")
'(2 3))
(format t "This can be split into ˜a and ˜a." head tail)
(format t "This cannot be split.")))

(IF (PROGN (PRINC "Lisp rocks!") '(2 3))


(LET

((HEAD (CAR (PROGN (PRINC "Lisp rocks!") '(2 3))))

(TAIL (CDR (PROGN (PRINC "Lisp rocks!") '(2 3)))))


(FORMAT T "This can be split into ˜a and ˜a." HEAD TAIL))
(FORMAT T "This cannot be split.")) ;
T

As you c a n se e , the princ sta te m e nt a ppe a rs thre e tim e s . T his c a use s une xpe c te d be ha vior a nd is ine ffic ie nt,
sinc e we ’re re pe a te dly running the sa m e c ode unne c e ssa rily.
If you give this proble m som e thought, the solution isn’t too ha rd to figure out. W e sim ply ne e d to c re a te a loc a l va ria ble inside
the split m a c ro, like this:
;Warning! Still contains a bug!
(defmacro split (val yes no)
`(let1 x ,val
(if x
(let ((head (car x))
(tail (cdr x)))
,yes)
,no)))
Note tha t we m a de use of let1 in this ne w ve rsion of split. As this shows, it is pe rfe c tly oka y to use m a c ros inside othe r m a c ros.
Now if we re run our pre vious e xa m ple , we c a n se e tha t split be ha ve s c orre c tly, princing the sta te m e nt only onc e :
> (split (progn (princ "Lisp rocks!")
'(2 3))
(format t "This can be split into ˜a and ˜a." head tail)
(format t "This cannot be split."))
Lisp rocks!This can be split into 2 and (3).
Unfortuna te ly, this ne w ve rsion of the split m a c ro introduc e s y e t anothe r bug. L e t’s ta c kle this ne w bug ne xt.
Avoiding Var iable Captur e
T o se e the bug in our ne we st ve rsion of split, try running the following:

> (let1 × 100


(split '(2 3)
(+ x head)
nil))
*** - +: (2 3) is not a number
Ca n you te ll wha t ha ppe ne d? W e just c re a te d a va ria ble x inside the ne w ve rsion of our split m a c ro! He re ’s wha t the c a ll to
split looks like if we macroexpand it:
> (macroexpand '(split '(2 3)
(+ x head)
nil))

(LET ((X '(2 3)))


(IF X (LET ((HEAD (CAR X)) (TAIL (CDR X))) (+ X HEAD)) NIL)) ;
T

Notic e how the e xpa nde d ve rsion of split c onta ins a de finition of x . T his bloc ks the c om pe ting de finition in our

trouble som e e xa m ple . In this sc e na rio, the split m a c ro a c c ide nta lly c apture d the va ria ble x a nd ove rwrote it in a n
une xpe c te d wa y. How c a n we a void this proble m ?
One sim ple solution would be to not c re a te a va ria ble x in the m a c ro, but to inste a d use a va ria ble with som e insa ne long na m e
like xqweopfjsadlkjgh. T he n we c ould fe e l pre tty c onfide nt the va ria ble use d inside the m a c ro will ne ve r c la sh with a va ria ble
inside the c ode tha t use s it. If fa c t, the re is a Com m on L isp func tion c a lle d gensym whose job it is to ge ne ra te c ra z y va ria ble
na m e s e xa c tly for this purpose :
> (gensym)
#:G8695
T he gensym func tion will c re a te a unique va ria ble na m e for you tha t is gua ra nte e d ne ve r to c la sh with a ny othe r va ria ble na m e
in your c ode . You m a y notic e tha t it a lso ha s a spe c ia l pre fix ( #:) tha t diffe re ntia te s it from othe r na m e s. Com m on L isp ha ndle s
the se gensym-based na m e s a s a spe c ia l c a se a nd will stop you from using the na m e of a gensym va ria ble dire c tly.
Now le t’s use the gensym func tion inside our split m a c ro to prote c t the m a c ro from c a using va ria ble c a pture :
;This function is finally safe to use
(defmacro split (val yes no)

(let1 g (gensym)

`(let1 ,g ,val
(if ,g
(let ((head (car ,g))
(tail (cdr ,g)))
,yes)
,no))))

In the first line of our re vise d m a c ro, we de fine a va ria ble g tha t c onta ins the gensym na m e . It’s ve ry im porta nt to notic e
tha t the re is not a ba c kquote a t the front of this line . T his m e a ns tha t this line of c ode is run a t mac ro e x pand time , not runtime ,
a nd it is pe rfe c tly fine to de fine the va ria ble g a t this point. T he let1 on the ne xt line , howe ve r, ha s a ba c kquote in front of it

. T his line will be run a t runtim e , so we don’t wa nt to use a ha rdc ode d va ria ble in this spot. In this ne w ve rsion, we inste a d
use the unique gensym na m e store d in g.
Now e ve ry tim e the split m a c ro is use d, a unique na m e is ge ne ra te d to hold the inte rna l va lue . W e c a n te st this by running
som e e xa m ple s through macroexpand:
> (macroexpand '(split '(2 3)
(+ x head)
nil))

(LET (( #:G8627 '(2 3))) (IF #:G8627 (LET ((HEAD (CAR #:G8627))
(TAIL (CDR #:G8627))) (+ X HEAD)) NIL)) ;
T
> (macroexpand '(split '(2 3)
(+ x head)
nil))

(LET (( #:G8628 '(2 3))) (IF #:G8628 (LET ((HEAD (CAR #:G8628))
(TAIL (CDR #:G8628))) (+ X HEAD)) NIL)) ;
T

Notic e how a diffe re ntly na m e d loc a l va ria ble wa s c re a te d in both insta nc e s . T his gua ra nte e s tha t the va ria ble na m e
will not only be unique within your c ode , but will a lso be unique if the split m a c ro is e ve r use d m ultiple tim e s in a ne ste d
fa shion. W e ha ve now c re a te d a fully de bugge d ve rsion of our split m a c ro.
Just be c a use it is now bug-fre e doe s not m e a n tha t it is fre e of va ria ble c a pture . Note tha t the m a c ro still de fine s the va ria ble s
head a nd tail. If you use d this func tion in othe r c ode in whic h he a d or ta il ha d a n a lte rna te m e a ning, your c ode would fa il!
Howe ve r, in the c a se of head a nd tail, the c a pture is on purpose . In this situa tion, the va ria ble c a pture is a fe ature , not a bug— it
is a n a na phoric m a c ro. As we ’ve disc usse d, this m e a ns tha t it m a ke s na m e d va ria ble s or func tions a va ila ble tha t we c a n use in the
body of the m a c ro.
A Re c ur sion M ac r o
L e t’s ta ke a nothe r look a t our im prove d my-length m a c ro:
(defun my-length (lst)
(labels ((f (lst acc)
(split lst
(f tail (1+ acc))
acc)))
(f lst 0)))
As we disc usse d, the re is a n a dditiona l re pe titive pa tte rn in this c ode : T he c re a tion of a loc a l func tion f. L e t’s write a nothe r
m a c ro tha t ge ts rid of this a dditiona l visua l noise : recurse. He re ’s a n e xa m ple of the recurse m a c ro in use :

> (recurse (n 9)

(fresh-line)

(if (zerop n)

(princ "lift-off!")

(progn (princ n)

(self (1-n)))))
9
8
7
6
5
4
3
2
1
lift-off!

T he first pa ra m e te r into the recurse m a c ro is a list of va ria ble s a nd the ir sta rting va lue s . In this c a se , we ’re de c la ring
only one va ria ble (n) a nd se tting its sta rting va lue to 9. T he re st of the line s in the m a c ro m a ke up the body of the re c ursive
func tion.

T he first thing we do in the body is sta rt a fre sh line . T he n we c he c k if n ha s re a c he d z e ro ye t . If it ha s, we print

“ lift-off!” . Othe rwise , we print the c urre nt num be r a nd c a ll the func tion a ga in, re c ursive ly. L ike our split m a c ro, the
recurse m a c ro is a na phoric . In the c a se of recurse, it m a ke s a func tion na m e d self a va ila ble , whic h we c a ll whe n we ’re re a dy to

pe rform a re c ursion . W e a lso subtra c t one from n a t this point to lowe r the c ountdown num be r.
Now tha t we ’ve se e n how recurse should work, le t’s write this recurse m a c ro. In orde r to proc e ss the list of a rgum e nts a nd
sta rting va lue s, it’s use ful for us to ha ve a func tion tha t c a n group ite m s into a list of pa irs. He re is a func tion, pairs, tha t
a c c om plishe s this:
> (defun pairs (lst)

(labels ((f (lst acc)

(split lst

(if tail

(f (cdr tail) (cons (cons head (car tail)) acc))

(reverse acc))

(reverse acc))))
(f lst nil)))
PAIRS
> (pairs '(a b c d e f))
((A . B) (C . D) (E . F))

T he pairs func tion is a ta il-c a ll-optim iz e d list-e a te r, whic h, ironic a lly, ha s its own loc a l func tion f . (Shortly, we won’t

ne e d to de c la re suc h a func tion a nym ore . ) It use s split to bre a k a n ite m off the list . Howe ve r, sinc e it ne e ds to proc e ss two

ite m s (a pa ir) from the list a t onc e , we ne e d to run a n a dditiona l c he c k to se e if the ta il is e m pty . If the re a re no ite m s in

the list (or only one ite m le ft ), we re turn our a c c um ula te d va lue s. Othe rwise , we re c ursive ly proc e ss the re st of the
list, with a ne w pa ir of ite m s pla c e d into the a c c um ula tor .
Now we ’re fina lly re a dy to write the recurse m a c ro:
(defmacro recurse (vars &body body)

(let1 p (pairs vars)

`(labels ((self ,(mapcar #'car p)


,@body))

(self ,@(mapcar #'cdr p)))))


As you c a n se e , it sim ply tra nsform s the re c ursion into a tra ditiona l loc a l func tion. First, it use s our ne w pairs func tion to ta ke

a pa rt the va ria ble na m e s a nd sta rting va lue s, a nd puts the re sult into p . T he n it de fine s a loc a l func tion sim ply na m e d self.

T he va ria ble na m e s for self a re the odd-num be re d ite m s from p . Sinc e we wa nt self to be a c c e ssible , a na phoric a lly, from
inside the m a c ro, we use a pla in na m e inste a d of a gensym na m e for this func tion. At the bottom of the m a c ro, we the n sim ply c a ll

self, pa ssing in a ll the sta rting va lue s .


Now tha t we ’ve c re a te d the recurse m a c ro, le t’s onc e a ga in c le a n up our my-length func tion using this ne w la ngua ge c onstruc t:
(defun my-length (lst)
(recurse (lst lst
acc 0)
(split lst
(f tail (1+ acc))
acc)))
As you c a n se e , the re is ve ry little re pe tition or visua l noise in this ve rsion of our my-length func tion.
Now you c a n a ppre c ia te how he lpful m a c ros c a n be whe n trying to write c le a n, suc c inc t c ode . Howe ve r, a libe ra l use of m a c ros
will a lso re quire you to be a r som e c osts tha t you ne e d to be a wa re of. W e ’ll look a t the pote ntia l downside s to m a c ros ne xt.
M ac r os: Dange r s and Alte r native s
Ma c ros a llow us to write c ode tha t ge ne ra te s othe r c ode , m a king the L isp la ngua ge s a wonde rful tool for m e ta progra m m ing a nd
prototyping ne w la ngua ge ide a s. But, a t som e le ve l, m a c ros a re just a sle ight of ha nd: T he y le t you tric k the L isp
c om pile r/inte rpre te r into a c c e pting your own c ustom iz e d la ngua ge c onstruc ts a nd tre a ting the m like sta nda rd L isp. T he y a re inde e d
a powe rful tool in a progra m m e r’s tool c he st, but the y a re not a s e le ga nt a s som e of the othe r progra m m ing tools you’ve
e nc ounte re d in this book.
T he m a in dra wba c k of m a c ros is tha t the y c a n m a ke it ha rd for othe r progra m m e rs to unde rsta nd your c ode . Afte r a ll, if you’re
c re a ting your own la ngua ge dia le c t, othe r progra m m e rs won’t be fa m ilia r with it. E ve n your future se lf— sa y, in a ye a r or two—
m a y ha ve a ha rd tim e unde rsta nding the struc ture of your c ode if you’ve m a de he a vy use of m a c ros. Be c a use of this, e xpe rie nc e d
L ispe rs will do the ir be st to use a lte rna te te c hnique s to m a c ro progra m m ing whe ne ve r possible . Ofte n, a be ginning L ispe r will write
a m a c ro in situa tions tha t c ould be a ddre sse d in othe r, c le a ne r wa ys.
For insta nc e , it’s fun to se e how we we re a ble to c le a n up our my-length func tion by a dding a c ouple of m a c ros na m e d split
a nd recurse. Howe ve r, in the pre vious two c ha pte rs, you le a rne d a bout a nothe r tool, func tiona l progra m m ing, whic h c a n a lso be
use d to c le a n up list-e a te r func tions. One powe rful func tion ofte n use d by func tiona l progra m m e rs is reduce. It is a highe r-orde r
func tion tha t a c c e pts a func tion a nd a list, a nd will c a ll the func tion onc e for e ve ry va lue in the list. He re is the my-length
func tion re writte n to use the powe rful reduce func tion, ra the r tha n m a c ros:
(defun my-length (lst)

(reduce (lambda (x i)

(1+ x))

lst

:initial-value 0))
As you c a n se e , this ne w ve rsion of my-length e a sily blows a wa y our pre vious ve rsion. It is shorte r, a nd it doe sn’t re ly on a ny of
the nonsta nda rd m a c ros tha t we c re a te d.

T he first a rgum e nt to reduce holds our re duc tion func tion . Its job is to ke e p tra c k of, a nd upda te , a n a c c um ula te d va lue ,
he re na m e d x. T his va ria ble x will hold the c urre nt a c c um ula te d va lue , whic h in this c a se will be the le ngth of the list so fa r. T his

m e a ns we c a n sim ply a dd one to x to upda te it to its ne w va lue . Sinc e the re duc tion func tion will be c a lle d onc e for e ve ry
ite m in the list, it will, in the e nd, ge ne ra te the le ngth of the list. (T he re duc tion func tion a lso re c e ive s, a s a n a rgum e nt, the
c urre nt ite m in the list, he re give n a s the va ria ble i. Howe ve r, we do not ne e d it for c a lc ula ting the list’s le ngth. ) T he ne xt ite m

pa sse d to reduce is the list we wa nt to re duc e . Fina lly, sinc e the a c c um ula te d le ngth we ’re c a lc ula ting should ha ve a n

initia l va lue of z e ro, we indic a te this by se tting the :initial-value ke yword a rgum e nt to z e ro .
Cle a rly, the re a re othe r sc e na rios whe re the list-e a te r m a c ros we ’ve c re a te d in this c ha pte r a re still use ful. T he re a re m a ny c a se s
whe re the reduce func tion c ould not be so e a sily use d. So in the e nd, the re a re still m a ny situa tions whe re c re a ting your own L isp
dia le c t is e xa c tly the right solution to a proble m , a s you’ll se e in the ne xt c ha pte r.
W hat You've Le ar ne d
T his c ha pte r c ove re d m a c ro progra m m ing. You’ve le a rne d the following:
Ma c ros le t you write c ode tha t write s c ode . W ith m a c ros, you c a n c re a te your own progra m m ing la ngua ge a nd c onve rt it to
sta nda rd L isp just be fore the c om pile r c a n ge t a pe e k a t it.
Ma c ros a llow you to ge t rid of tha t fe e ling of dé jà v u whe n writing your c ode , in situa tions whe n nothing e lse c a n do so.
You m ust be c a re ful whe n writing m a c ros so tha t the y don’t le a d to uninte ntiona l, re pe a te d e xe c ution of c ode .
You ne e d to be c a re ful to a void uninte nde d v ariable c apture in m a c ros. You c a n a void this by using gensym na m e s.
If va ria ble s c re a te d by a m a c ro a re e xpose d on purpose , a s a fe a ture of the m a c ro, the m a c ro is c a lle d a n anaphoric mac ro.
Ma c ro progra m m ing is a ve ry powe rful te c hnique . Howe ve r, try to use func tiona l progra m m ing inste a d to solve a proble m
whe ne ve r possible . Ma c ros should a lwa ys be a la st re sort.
Chapte r 17. Domain-Spe c ific Language s
One of the be st re a sons for using m a c ros is to pe rform domain-spe c ific language (DSL) progra m m ing. DSL progra m m ing is a n
a dva nc e d m a c ro progra m m ing te c hnique tha t a llows us to solve diffic ult progra m m ing proble m s by dra stic a lly c ha nging the
struc ture a nd a ppe a ra nc e of L isp c ode to optim iz e it for a spe c ia liz e d purpose . Although m a c ros a re not stric tly ne c e ssa ry for doing
DSL progra m m ing, by writing a se t of m a c ros, you c a n e a sily c re a te a DSL in L isp.
W hat Is a Domain?
Ac c ording to the 2000 US Ce nsus, the a ve ra ge fa m ily in the Unite d Sta te s ha d 1. 86 c hildre n. Sinc e no individua l fa m ily ha s
e xa c tly 1. 86 c hildre n, it is obvious tha t no pa rtic ula r fa m ily is truly pe rfe c tly a ve ra ge . In the sa m e wa y, the re is no suc h thing a s
a n a ve ra ge c om pute r progra m . E ve ry progra m is de signe d to solve a spe c ific proble m , a nd e ve ry a re a of hum a n inquiry, or domain,
ha s its own idiosync ra tic re quire m e nts tha t influe nc e progra m s tha t solve proble m s in the give n a re a . W ith DSL s, we e nha nc e the
c ore of our progra m m ing la ngua ge to ta ke the se dom a in-spe c ific re quire m e nts into a c c ount, pote ntia lly m a king our re sulting c ode
e a sie r to write a nd unde rsta nd.
L e t’s ta ke a look a t som e spe c ific dom a ins a nd c re a te som e DSL s tha t le t us e a sily work within the se dom a ins using L isp. In this
c ha pte r, we ’ll c re a te two diffe re nt DSL s. First, we ’ll c re a te a DSL for writing sc alable v e c tor graphic s (SV G) file s. T he n we ’ll
write a DSL for c re a ting c om m a nds in a te xt a dve nture — we ’re fina lly going to upgra de our W iz a rd’s Adve nture Ga m e from
Cha pte r 5 a nd Cha pte r 6 to m a ke it fully pla ya ble !
W r iting SVG F ile s

T he SVG form a t is a file form a t for dra wing gra phic s. In this form a t, you spe c ify obje c ts like c irc le s a nd polygons, a nd the n pa ss
the m to a c om pa tible c om pute r progra m to vie w. Be c a use the SVG form a t spe c ifie s a dra wing using pure m a th func tions inste a d of
ra w pixe ls, it is e a sy for a progra m to re nde r a n SVG im a ge a t a ny siz e , m a king im a ge s in this form a t e a sily sc a la ble .
T he SVG form a t is c urre ntly re c e iving a lot of a tte ntion from we b de ve lope rs. All m ode rn browse rs (e xc luding Mic rosoft Inte rne t
E xplore r) support SVG na tive ly. Re c e ntly, Google re le a se d a se t of libra rie s c a lle d SVG W e b tha t a dds de c e nt support for SVG,
e ve n in Inte rne t E xplore r. T his a llows SVG to work in m ore tha n 90 pe rc e nt of c urre nt we b browse rs. Fina lly, SVG ha s be c om e a
pra c tic a l a nd e ffic ie nt option for dra wing gra phic s on we bsite s.
T he SVG form a t is built on top of the XML form a t. He re is a n e xa m ple of wha t a c om ple te SVG file looks like :
<svg xmlns="http://www.w3.org/2000/svg">
<circle cx="50"
cy="50"
r="50"
style="fill:rgb(255,0,0);stroke:rgb(155,0,0)">
</circle>
<circle cx="100"
cy="100"
r="50"
style="fill:rgb(0,0,255);stroke:rgb(0,0,155)">
</circle>
</svg>
Sim ply c opy this te xt a nd pla c e it in a file na m e d e x ample . sv g (or downloa d this file from http://la ndoflisp. c om /). T he n you c a n
ope n the file from the Fire fox we b browse r (the Sa fa ri, Chrom e , a nd Ope ra we b browse rs should a lso work).
He re is wha t you should se e , with a re d a nd blue c irc le :
Now, le t’s write som e m a c ros a nd func tions to le t us c re a te a pic ture like this dire c tly in Com m on L isp!
Cr e ating XM L and H TM L with the tag M ac r o
T he XML da ta form a t (just like the HT ML da ta form a t) c onsists prim a rily of ne ste d ta gs:

<mytag>
<inner_tag>
</inner_tag>

</mytag>

E ve ry ta g a lso ha s a m a tc hing c losing ta g . T he c losing ta g ha s the sa m e na m e , but with a sla sh pre c e ding it.
Additiona lly, ta gs m a y c onta in a ttribute s:
<mytag color="BLUE" height="9"></mytag>
In this e xa m ple , we c re a te a ta g na m e d mytag tha t ha s the a ttribute of be ing blue a nd ha s a he ight of 9.

W r iting a M ac r o H e lpe r F unc tion

Ofte n, whe n writing a m a c ro to pe rform a ta sk, you’ll find a lot of wha t your m a c ro ne e ds to do c a n be ha ndle d by a func tion
inste a d. Be c a use of this, it is ofte n prude nt to first write a he lpe r func tion tha t doe s m ost of wha t the m a c ro ne e ds to do. T he n you
write the m a c ro, ke e ping it a s sim ple a s possible by le ve ra ging the he lpe r func tion. T his is wha t we ’re going to do a s we write a
m a c ro to c re a te XML -style ta gs in L isp.
He re is our he lpe r func tion, c a lle d print-tag, whic h prints a single ope ning (or c losing) ta g:
(defun print-tag (name alst closingp)

(princ #\<)

(when closingp
(princ #\/))

(princ (string-downcase name))

(mapc (lambda (att)

(format t " ˜a=\"˜a\"" (string-downcase (car att)) (cdr att)))


alst)

(princ #\>))

First, the print-tag func tion prints a n ope ning a ngle bra c ke t . Sinc e this is only a c ha ra c te r, we use the lite ra l c ha ra c te r

synta x by pre fixing the bra c ke t with #\. T he n we c he c k the pre dic a te closingp . If it is true , the ta g ne e ds to ha ve a sla sh in
front of it to m a ke it a c losing ta g. T he n we print the na m e of the ta g, c onve rte d to lowe rc a se with the string-downcase func tion

. Ne xt, we ite ra te through a ll the a ttribute s in the alst of a ttribute s a nd print out e a c h a ttribute /va lue pa ir .

Fina lly, we e nd by putting in a c losing a ngle bra c ke t .


T he following is a n e xa m ple use of the print-tag func tion. Sinc e it is a pla in func tion a nd not a m a c ro, it’s e a sy to de bug in
the RE PL . T his is a nothe r re a son why he lpe r func tions a re a good ide a whe n c re a ting m a c ros.
> (print-tag 'mytag '((color . blue) (height . 9)) nil)
<mytag color="BLUE" height="9">
As you c a n se e , this func tion doe s a fine job of printing a n XML ta g. Howe ve r, it would be a re a l c hore if a ll ta gs ha d to be
c re a te d in this wa y. T ha t’s why we ’re going to write the tag m a c ro ne xt.

Cr e ating the tag M ac r o

T he tag m a c ro we ’ll c re a te ha s be e n a dopte d from the m a c ro of the sa m e na m e in Pa ul Gra ha m ’s Arc L isp dia le c t. It im prove s
on the print-tag func tion in se ve ra l c ruc ia l wa ys, a ll of whic h c ould not be re m e die d without ha ving a m a c ro:

T a gs a lwa ys c om e in pa irs. Howe ve r, if we wa nt to ne st ta gs, a func tion would not be a ble to print ta gs tha t
surround the ta gs printe d inside it. T his is be c a use it re quire s us to e xe c ute c ode be fore a nd a fte r ne ste d ta gs a re
e va lua te d. T his is possible in a m a c ro, but not a func tion.
T a g na m e s a nd a ttribute na m e s usua lly do not ne e d to c ha nge in a dyna m ic wa y. Be c a use of this, it’s re dunda nt
to ne e d to pre fix ta g na m e s with a single quote . In othe r words, ta g na m e s should by de fa ult be tre a te d a s if the y
we re in da ta m ode .
Unlike ta g na m e s, it’s ve ry de sira ble for the va lue s of a ttribute s to be dyna m ic a lly ge ne ra te d. Our m a c ro will
ha ve a synta x tha t pla c e s the a ttribute va lue s into c ode m ode so we c a n e xe c ute L isp c ode to popula te the se va lue s.

Ide a lly, this is how we would like the ta g m a c ro to work, whe n we use it in the RE PL :
> (tag mytag (color 'blue height (+ 4 5)))
<mytag color="BLUE" height="9"></mytag>
Notic e tha t the ta g na m e a nd a ttribute list no longe r ne e d quote s in front of the m . Additiona lly, it is now e a sy to c a lc ula te a n
a ttribute dyna m ic a lly with L isp c ode . In this c a se , we ’re c a lc ula ting tha t the he ight is 4 plus 5.
He re ’s the m a c ro tha t a c c om plishe s this ta sk:
(defmacro tag (name atts &body body)

`(progn (print-tag ',name

(list ,@(mapcar (lambda (x)

`(cons ',(car x) ,(cdr x)))

(pairs atts)))
nil)

,@body

(print-tag ',name nil t)))

As you would e xpe c t, the m a c ro first c a lls print-tag to ge ne ra te the ope ning ta g . T his is a bit tric ky whe n we ge ne ra te
the a list of a ttribute s for print-tag, sinc e we wa nt the va lue s for the a ttribute s to be in c ode m ode . W e a c c om plish this by

wra pping the a ttribute s using list . T he n we mapcar through the a ttribute s, whic h we ’ve pa ire d with the pairs func tion
. (Re m e m be r tha t we c re a te d the pairs func tion towa rd the e nd of the pre vious c ha pte r. ) For e a c h a ttribute pa ir, we ge ne ra te a
c ode fra gm e nt in the list tha t c onsists of c ons, without a quota tion m a rk in front of the va lue of the a ttribute , so tha t we c a n

dyna m ic a lly c a lc ula te it .

Ne xt, we put a ll the c ode ne ste d inside our tag m a c ro, so tha t it is c a lle d a fte r the ope ning ta g . Fina lly we c re a te a

c losing ta g .
T o m a ke m ore se nse of how this m a c ro ha ndle s the a ttribute list, le t’s pa ss the output from our e xa m ple to macroexpand:
> (macroexpand '(tag mytag (color 'blue height (+ 4 5))))
(PROGN (PRINT-TAG 'MYTAG

(LIST (CONS 'COLOR 'BLUE)

(CONS 'HEIGHT (+ 4 5)))


NIL)
(PRINT-TAG 'MYTAG NIL T)) ;
T

L ooking a t the m a c ro e xpa nsion, it should be c le a r how the tag m a c ro builds the a ttribute list to pa ss to print-tag a nd

how it a llows us to dyna m ic a lly ge ne ra te a ttribute va lue s, suc h a s the he ight a ttribute .
He re is a nothe r e xa m ple of this m a c ro in use , now with two inne r ta gs:
> (tag mytag (color 'blue size 'big)
(tag first_inner_tag ())
(tag second_inner_tag ()))

<mytag color="BLUE" size="BIG">


<first_inner_tag></first_inner_tag>
<second_inner_tag></second_inner_tag>
</mytag>
Notic e how it c orre c tly surrounds the inne r, ne ste d ta gs with prope r XML ope ning a nd c losing ta gs. Note a lso tha t I ha ve a dde d

line bre a ks a nd inde nta tion to the output for c la rity. T he a c tua l output of the tag func tion a lwa ys prints on a single line ,
without line bre a ks or inde nta tion.

Using the tag M ac r o to G e ne r ate H TM L

T he tag m a c ro c a n be use d for ge ne ra ting XML or HT ML . For insta nc e , we c ould do the following to ge ne ra te a “ He llo W orld”
HT ML doc um e nt:
> (tag html ()
(tag body ()
(princ "Hello World!")))
<html><body>Hello World!</body></html>
Sinc e HT ML use s pre de fine d ta gs (unlike XML , whe re the ta gs c a n ha ve a ny na m e ), we c ould write sim ple m a c ros for spe c ific
HT ML ta gs tha t m a ke the m e ve n e a sie r to write HT ML in L isp. For insta nc e , he re a re som e sim ple html a nd body m a c ros:
(defmacro html (&body body)
`(tag html ()
,@body))
(defmacro body (&body body)
`(tag body ()
,@body))
Now we c ould write our “ He llo W orld” HT ML e xa m ple e ve n m ore e le ga ntly:
> (html
(body
(princ "Hello World!")))
<html><body>Hello World!</body></html>
Howe ve r, we wa nt to use the tag m a c ro to c re a te SVG dra wings inste a d. So le t’s e xpa nd our DSL for the SVG dom a in.
Cr e ating SVG -Spe c ific M ac r os and F unc tions
First, le t’s write the svg m a c ro, whic h e m bodie s a n e ntire SVG im a ge . He re it is:
(defmacro svg (&body body)

`(tag svg (xmlns "http://www.w3.org/2000/svg"

"xmlns:xlink" "http://www.w3.org/1999/xlink")
,@body))
T he svg m a c ro is built on top of the tag m a c ro. SVG im a ge s, for our purpose s, re quire two spe c ia l a ttribute s to be c re a te d:

T he xmlns a ttribute te lls the SVG vie we r (in our c a se , the Fire fox we b browse r) whe re it c a n find the prope r

doc um e nta tion for the SVG form a t .

T he se c ond a ttribute e na ble s hype rlinks inside the pic ture . W e ’ll be using this hype rlinking fe a ture in m ore
a dva nc e d e xa m ple s, sta rting in the ne xt c ha pte r.

T o dra w pic ture s, we ’ll ne e d to m a nipula te c olors. T o ke e p things sim ple , we ’re just going to re pre se nt c olors a s RGB triple s
store d in a list. For insta nc e , the c olor (255 0 0) is bright re d.
Ofte n, it is use ful to ge ne ra te lighte r or da rke r va ria nts of a pa rtic ula r c olor. T he following brightness func tion doe s this for us:
(defun brightness (col amt)
(mapcar (lambda (x)
(min 255 (max 0 (+ x amt))))
col))
If you pa ss bright re d into this func tion a nd se t the brightne ss to ne ga tive 100, you c a n se e tha t it will ge ne ra te a da rke r re d:
> (brightness '(255 0 0) −100)
(155 0 0)
Ne xt, le t’s c re a te a func tion tha t se ts the style of a n SVG pic ture e le m e nt:
(defun svg-style (color)
(format nil

"˜{fill:rgb(˜a,˜a,˜a);stroke:rgb(˜a,˜a,˜a)˜}"
(append color

(brightness color −100))))

T he svg-style func tion a c c e pts a c olor, a nd the n se ts the fill a nd stroke (outline ) of a pic ture e le m e nt . By using our

brightne ss func tion, we c a n m a ke the outline a da rke r va ria nt of the fill . T his wa y, we ne e d to spe c ify only a single c olor for
e ve ry e le m e nt in our pic ture s, while m a inta ining a ple a sing a ppe a ra nc e .
Now, le t’s c re a te a func tion to dra w a c irc le . Sinc e we won’t ne e d to ne st othe r SVG ta gs inside a c irc le , the re is no ne e d to
write a m a c ro for dra wing c irc le s— a func tion suffic e s.
(defun circle (center radius color)

(tag circle (cx (car center)

cy (cdr center)

r radius

style (svg-style color))))

W e ’ll wa nt to se t the c e nte r, ra dius, a nd c olor of e a c h c irc le . T he c e nte r ne e ds to be a ssigne d to the cx a nd cy SVG

a ttribute s of the c irc le . T he ra dius is put in the r a ttribute . W e se t the style of our c irc le with our svg-style func tion
.
W e a re now re a dy to dra w the sim ple SVG pic ture of two c irc le s shown e a rlie r, using our ne w DSL ! He re ’s how we do it:
> (svg (circle '(50 . 50) 50 '(255 0 0))
(circle '(100 . 100) 50 '(0 0 255)))
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/
xlink"><circle cx="50" cy="50" r="50"
style="fill:rgb(255,0,0);stroke:rgb(155,0,0)"></circle><circle cx="100"
cy="100" r="50" style="fill:rgb(0,0,255);stroke:rgb(0,0,155)"></circle></svg>
W e now ha ve a func tiona l SVG DSL . L e t’s a dd som e m ore func tiona lity to our DSL so we c a n a ppre c ia te the powe r a DSL c a n
give to our progra m s.
Building a M or e Complic ate d SVG Example
L e t’s a dd a ne w func tion to our SVG DSL tha t m a ke s it e a sy to dra w a n a rbitra ry polygon:
(defun polygon (points color)

(tag polygon (points (format nil

"˜{˜a,˜a ˜}"

(mapcan (lambda (tp)


(list (car tp) (cdr tp)))
points))
style (svg-style color))))

An SVG polygon store s a ll the points of the polygon in the points a ttribute . W e c onstruc t the list of points by using a

format sta te m e nt, whic h c onta ins the ˜{ ˜} c ontrol strings . Re m e m be r from Cha pte r 11 tha t the se c ontrol strings le t us
ite ra te through a list inside the format func tion. In this c a se , we ’re ite ra ting through the list of points. W e the n fla tte n the list of

point pa irs using mapcan , whic h you m a y re m e m be r is the sa m e a s using a mapcar followe d by a n append.
In this e xa m ple , we ’re going to dra w som e ra ndom wa lks. A random walk is a gra ph of wha t you would ge t if you de c ide , a t e a c h
m om e nt in tim e , to flip a c oin a nd the n m ove e ithe r up or down a ste p. Ra ndom wa lks a re ve ry sim ila r in be ha vior to stoc ks in the
stoc k m a rke t. T he y a re ofte n use d a s a sta rting point for fina nc ia l m ode ling. He re is a func tion tha t ge ne ra te s a ra ndom wa lk:
(defun random-walk (value length)
(unless (zerop length)
(cons value

(random-walk (if (zerop (random 2))


(1-value)
(1+ value))
(1-length)))))
T his func tion builds a list of num be rs, sta rting with the value pa ra m e te r. T he n it inc re a se s or de c re a se s this va lue ra ndom ly. W e

c hoose whic h dire c tion to m ove using the random func tion . (Note tha t, in orde r to ke e p it sim ple , this func tion isn’t ta il c a ll
optim iz e d, sinc e the cons ha ppe ns a fte r the re c ursive c a ll. )
He re ’s a n e xa m ple of how we c a n use the random-walk func tion:
> (random-walk 100 10)
(100 101 102 101 100 101 102 103 102 103)
Now le t’s use our SVG DSL to dra w a sle w of ra ndom wa lks in a pic ture :
(with-open-file (*standard-output* "random_walk.svg"
:direction :output

:if-exists :supersede)

(svg (loop repeat 10

do (polygon (append '((0 . 200))


(loop for x
for y in (random-walk 100 400)
collect (cons x y))

'((400 . 200)))
(loop repeat 3

collect (random 256))))))


Sinc e the a m ount of da ta c re a te d in this e xa m ple is quite huge , we ’re dum ping the da ta stra ight to a file (na m e d

random_walk . sv g), inste a d of printing it to the RE PL . W e do this by re dire c ting the *standard-output* dyna m ic va ria ble , a
te c hnique introduc e d in Cha pte r 12. Notic e how we c a n m ix L isp c ode fre e ly with our DSL c om m a nds. For insta nc e , we c a n loop

right inside the SVG m a c ro to ge ne ra te 10 polygons a t onc e .


T o m a ke the gra ph pre tty, we ’re going to fill in the a re a unde r e a c h gra ph line with a c olor. T o do this, we ’ll re pre se nt e a c h
line using a polygon, with the ba se line a long the bottom of the gra ph (with a y-c oordina te of 200) inc lude d a s points to c lose the
sha pe :
T his is why we a dd points for the bottom -le ft a nd bottom -right c orne r a s we c re a te e a c h polygon. For e ve n m ore

fun, we a lso ra ndom iz e the c olor of e a c h gra ph line .


He re is a n e xa m ple of som e ra ndom gra phs ge ne ra te d by this ve ry sim ple DSL c ode :

Now tha t you’ve se e n how e a sily you c a n write XML , HT ML , a nd SVG DSL s in L isp, le t’s c re a te a n e ntire ly diffe re nt kind of
DSL — one tha t will le t us build c ustom ga m e c om m a nds for our W iz a rd’s Adve nture Ga m e from Cha pte r 5 a nd Cha pte r 6!
Cr e ating Custom G ame Commands for W iz ar d's Adve ntur e G ame

If you re m e m be r, whe n we la st e nc ounte re d the ga m e sta rring our wiz a rd a nd a ppre ntic e in Cha pte r 5 a nd Cha pte r 6, we c ould
wa lk a round the world a nd pic k up obje c ts. Howe ve r, we c ouldn’t re a lly pe rform a ny othe r inte re sting or fun a c tions. T o m a ke a
ga m e fun, it should inc lude spe c ia l a c tions tha t c a n be pe rform e d with c e rta in obje c ts a nd/or a t c e rta in loc a tions in the ga m e . W e
ne e d frogs tha t c a n be kisse d, dra gons tha t c a n be fought, a nd pe rha ps e ve n m a ide ns tha t c a n be re sc ue d!
Cre a ting the se kinds of inte re sting a c tivitie s in the ga m e pose s a unique c ha lle nge . On the one ha nd, the re a re c le a rly m a ny
sim ila ritie s be twe e n suc h diffe re nt ga m e a c tions. For insta nc e , m ost of the m will re quire us to ha ve a n obje c t in our posse ssion. On
the othe r ha nd, the y a ll ne e d to ha ve unique and idiosy nc ratic prope rtie s (e na ble d through c om m a nd-spe c ific L isp c ode ) or the
ga m e be c om e s boring. As you’ll se e , a DSL c a n he lp you a dd m a ny suc h unique c om m a nds to your ga m e .
T o run the c ode from he re until the e nd of this c ha pte r, we ’re going to use a ll the ga m e c ode from Cha pte r 5 a nd Cha pte r 6. Just
put the c ode from those c ha pte rs into a file na m e d wizards_game . lisp (or downloa d wizards_game . lisp from http://la ndoflisp. c om /).
As soon a s the ga m e is loa de d, you c a n type ga m e c om m a nds like look dire c tly in the CL ISP RE PL . Alte rna tive ly, you c a n use the
game-repl c om m a nd we c re a te d in Cha pte r 6 to ge t a m ore polishe d ga m e e xpe rie nc e . Re m e m be r tha t the quit c om m a nd will
ta ke you out of the ga m e RE PL .
He re ’s wha t you do to loa d the ga m e c ode from the RE PL a nd sta rt running ga m e c om m a nds:
> (load "wizards_game.lisp")
;; Loading file wizards_game.lisp ...
;; Loaded file wizards_game.lisp
T
> (look)
(YOU ARE IN THE ATTIC. THERE IS A GIANT WELDING TORCH IN THE CORNER. THERE IS
A LADDER GOING DOWNSTAIRS FROM HERE.)
> (game-repl)
look
You are in the living-room. A wizard is snoring loudly on the couch. There is
a door going west from here. There is a ladder going upstairs
from here. You see a whiskey on the floor. You see a bucket on the floor.
quit
Cr e ating Ne w G ame Commands by H and
So wha t should our ga m e DSL look like ? T he only wa y to re a lly know is to first c re a te som e c om m a nds by ha nd. T he n we c a n
se e if the re a re a ny c om m on pa tte rns be twe e n diffe re nt c om m a nds tha t we c a n use a s the ba sis of our DSL .

A Command for W e lding

In the a ttic of the wiz a rd’s house is a we lding m a c hine . L e t’s a llow the pla ye rs to we ld the c ha in to the buc ke t if the y bring
those ite m s to tha t loc a tion. He re ’s the c ode to m a ke this ha ppe n:

(defun have (object)


(member object (inventory)))

(defparameter *chain-welded* nil)

(defun weld (subject object)

(if (and (eq *location* 'attic)


(eq subject 'chain)
(eq object 'bucket)
(have 'chain)
(have 'bucket)
(not *chain-welded*))
(progn (setf *chain-welded* t)
'(the chain is now securely welded to the bucket.))

'(you cannot weld like that.)))

First, we ne e d a n e a sy wa y of c he c king whe the r the pla ye r is c urre ntly c a rrying a n obje c t, using the have func tion .
Re m e m be r tha t we c re a te d a c om m a nd for c he c king wha t the pla ye r is c a rrying, na m e d inventory. If a n obje c t is a m e m be r of
the inve ntory, it m e a ns the pla ye r m ust “ ha ve ” tha t obje c t.
Ne xt, our progra m ne e ds som e wa y of ke e ping tra c k of whe the r or not the c ha in a nd buc ke t a re we lde d toge the r, sinc e the re will
be a c tions la te r in the ga m e tha t a re possible only onc e this we lding ha s ha ppe ne d. For this purpose , we c re a te a globa l, dyna m ic

va ria ble na m e d *chain-welded* .

Fina lly, we ne e d to c re a te the we lding c om m a nd itse lf . W e lding is possible only if a sle w of c onditions a re m e t :

You m ust be in the a ttic .


You m ust ha ve chain a nd bucket a s the subje c t a nd obje c t of the we lding c om m a nd.
You m ust be c a rrying the c ha in a nd buc ke t with you.
T he c ha in a nd buc ke t c a n’t a lre a dy be we lde d toge the r.

If the se c onditions a re m e t, we se t our *chain-welded* va ria ble to true a nd print a m e ssa ge indic a ting this suc c e ss. If a ny

of the c onditions fa il, we indic a te tha t the we lding wa s unsuc c e ssful .


L e t’s try the c om m a nd in the CL ISP RE PL :
> (weld 'chain 'bucket)
(YOU CANNOT WELD LIKE THAT.)
W e ll, tha t’s e xa c tly the right re sponse . Afte r a ll, we ’re not in the a ttic , a nd we a re n’t c a rrying the right obje c ts. So fa r, so good.
Ne xt, le t’s try our ne w c om m a nd in our fa nc y game-repl:
> (game-repl)
weld chain bucket
I do not know that command.
quit
W ha t? W hy doe sn’t it “ know” tha t c om m a nd? T he a nswe r is sim ple : Our game-repl ha s som e ba sic prote c tions a ga inst running
una uthoriz e d c om m a nds. T o re m e dy this, we ne e d to a dd weld to our list of pe rm itte d c om m a nds:
> (pushnew 'weld *allowed-commands*)
(WELD LOOK WALK PICKUP INVENTORY)
> (game-repl)
weld chain bucket
You cannot weld like that.
By using the pushnew c om m a nd, the weld func tion is a dde d only to the a llowe d c om m a nds if it wa sn’t a lre a dy pre se nt in tha t
list. Proble m solve d!

A Command for Dunking

In the wiz a rd’s ga rde n, the re is a we ll. L e t’s c re a te a c om m a nd tha t le ts the pla ye r dunk the buc ke t in the we ll to fill it with
wa te r:
(setf *bucket-filled* nil)

(defun dunk (subject object)

(if (and (eq *location* 'garden)


(eq subject 'bucket)
(eq object 'well)
(have 'bucket)
*chain-welded*)
(progn (setf *bucket-filled* 't)
'(the bucket is now full of water))
'(you cannot dunk like that.)))

(pushnew 'dunk *allowed-commands*)

As with our weld c om m a nd, we first ne e d a va ria ble to ke e p tra c k of whe the r the buc ke t ha s be e n fille d ye t . Ne xt, we

ne e d a dunk func tion . Notic e how, with dunking, we onc e a ga in ha ve a long list of c onditions tha t ne e d to be m e t be fore we

c a n suc c e ssfully c om ple te the a c tion . Som e of the se a re sim ila r to those we ne e de d for our we lding c om m a nd. For insta nc e ,
dunking a lso re quire s the pla ye r to be in a spe c ific loc a tion with the c orre c t obje c t. Othe r c onditions a re dunking-spe c ific , suc h a s
the fa c t tha t the pla ye r ne e ds to ha ve a we lde d c ha in be fore be ing a ble to dunk. Fina lly, we ne e d to push the dunk func tion onto

our list of a llowe d a c tions .


The game -ac tion M ac r o

Now tha t we ’ve c re a te d two c ustom ga m e a c tions for our ga m e , it’s obvious tha t the weld a nd dunk c om m a nds a re ve ry sim ila r
in som e wa ys. Howe ve r, a s in our SVG libra ry, e a c h ga m e c om m a nd ne e ds to c onta in a c e rta in a m ount of dyna m ic logic in it, to
c ustom iz e the be ha vior of the c om m a nd. L e t’s write a game-action m a c ro tha t a ddre sse s the se issue s. It will m a ke it m uc h e a sie r
to c re a te ne w ga m e c om m a nds.

(defmacro game-action (command subj obj place &body body)

`(progn (defun ,command (subject object)

(if (and (eq *location* ',place)


(eq subject ',subj)
(eq object ',obj)

(have ',subj))

,@body

'(i cant ,command like that.)))

(pushnew ',command *allowed-commands*)))


T his game-action m a c ro e m bodie s the c om m on pa tte rn be twe e n our dunk a nd weld c om m a nds. T he pa ra m e te rs to game-action
a re the na m e of the c om m a nd, the two obje c ts involve d in the a c tion, the pla c e it ne e ds to oc c ur, a nd som e a rbitra ry a dditiona l
c ode in the body pa ra m e te r tha t le ts us a dd c ustom logic to the c om m a nd .

T he m a in job of the game-action m a c ro is to de fine a ne w func tion for a c om m a nd . It m a y be surprising to you tha t a
m a c ro c a n do som e thing a s powe rful a s de fine a ne w func tion on its own, but the re is nothing to stop it from doing this. I hope this
e xa m ple shows you just how fle xible a nd m ind-be nding the Com m on L isp m a c ro syste m c a n be .
Sinc e a ll ga m e a c tions for this ga m e re quire the loc a tion, subje c t, a nd obje c t, we c a n ta ke c a re of som e of the c onditions

dire c tly within this m a c ro . Howe ve r, we ’re going to le a ve othe r c onditions ope n for e a c h spe c ific c om m a nd. Notic e , for

e xa m ple , tha t the subje c t of the ga m e se nte nc e ne e ds to be owne d by the pla ye r , but the obje c t doe s not. T his m a ke s se nse ,
sinc e the re a re m a ny a c tions tha t c a n be pe rform e d, suc h a s “ throw roc k dra gon, ” whe re the obje c t of the se nte nc e (dra gon) doe s
not ne e d to be in the pla ye r’s inve ntory.
Onc e the ba sic m a c ro-le ve l c onditions ha ve be e n m e t, we will de fe r the re st of the logic to the le ve l of the individua l c om m a nd

. If the c onditions we re not m e t, we print a n e rror m e ssa ge , c ustom iz e d with the na m e of the c urre nt c om m a nd .

Fina lly, we pushnew the c om m a nd into the list of a llowe d c om m a nds for our fa nc y game-repl .
One thing we do not do in this m a c ro is de fine or se t a ny globa l va ria ble s. If a ga m e c om m a nd ne e ds to de fine a *chain-
welded* or *bucket-filled* globa l va ria ble , it m ust do this itse lf. T his m a ke s se nse , sinc e the re is c le a rly no gua ra nte e tha t the re
will be a one -to-one re la tionship be twe e n sta te va ria ble s for our ga m e a nd pa rtic ula r c om m a nds. For insta nc e , som e c om m a nds
m a y be pe rm itte d m ultiple tim e s, m a king the sta te unne c e ssa ry. Or a n a c tion m a y de pe nd on m ultiple sta te va ria ble s. Ha ving this
kind of va ria tion in the c om m a nds is wha t m a ke s the m unique a nd fun.
W ith this m a c ro, we now ha ve a sim ple DSL for c re a ting ne w ga m e a c tions! E sse ntia lly, this c om m a nd give s us our own
progra m m ing la ngua ge , spe c ia liz e d for the dom a in of c re a ting ga m e c om m a nds. L e t’s re write our pre vious weld a nd dunk
c om m a nds using our ne w ga m e c om m a nd progra m m ing la ngua ge :
(defparameter *chain-welded* nil)

(game-action weld chain bucket attic


(if (and (have 'bucket) (not *chain-welded*))
(progn (setf *chain-welded* 't)
'(the chain is now securely welded to the bucket.))
'(you do not have a bucket.)))

(setf *bucket-filled* nil)


(game-action dunk bucket well garden
(if *chain-welded*
(progn (setf *bucket-filled* 't)
'(the bucket is now full of water))
'(the water level is too low to reach.)))
As you c a n se e , the se c om m a nds ha ve be c om e m uc h e a sie r on the e ye s. Notic e how weld c he c ks for owne rship of the buc ke t,
whe re a s dunk doe s not ne e d to c he c k for owne rship of the we ll.
T o furthe r illustra te the va lue of using m a c ros to im ple m e nt our ga m e c om m a nd DSL , le t’s im ple m e nt a m ore c om plic a te d
ga m e c om m a nd, splash:
(game-action splash bucket wizard living-room
(cond ((not *bucket-filled*) '(the bucket has nothing in it.))
((have 'frog) '(the wizard awakens and sees that you stole his frog.
he is so upset he banishes you to the
netherworlds-you lose! the end.))
(t '(the wizard awakens from his slumber and greets you warmly.
he hands you the magic low-carb donut-you win! the end.))))
For this c om m a nd, the re a re thre e distinc t sc e na rios tha t m ight ha ppe n:

T he buc ke t is e m pty.
Your buc ke t is full, but you stole the frog. In tha t c a se , you lose .
Your buc ke t is full a nd you didn’t ste a l the frog. You win!

W ith our game-action m a c ro, we c a n support m a ny a c tion c om m a nds, e a c h with spe c ia l idiosync ra tic be ha vior. Still, we a re
a ble to a void unne c e ssa ry re pe tition.

Note

T h e game-action c om m a nd e xpose s the subject a nd object va ria ble s within the body of the m a c ro. T his a llows ga m e
c om m a nds to a c c e ss this inform a tion, but it m ight a lso c a use a na m e c ollision if the c ode tha t c re a te s the game-action c om m a nds
a lso ha s va ria ble s na m e d subject a nd object. As a n e xe rc ise , try m odifying the game-action m a c ro so tha t the subject a nd
object va ria ble s a re re pla c e d by gensym na m e s, a s disc usse d in Cha pte r 16.
Le t's Tr y the Comple te d W iz ar d's Adve ntur e G ame !
He re is a sa m ple run through of the W iz a rd’s Adve nture Ga m e tha t shows off som e of the ric h func tiona lity we ’ve put into this
ga m e . Pla y the ga m e yourse lf a nd se e if you c a n win the m a gic donut!

> (game-repl)
look
You are in the living-room. There is a wizard snoring loudly on the couch.
There is a door going west from here. There is a ladder going upstairs from
here. You see a whiskey on the floor. You see a bucket on the floor.
pickup bucket
You are now carrying the bucket
pickup whiskey
You are now carrying the whiskey
inventory
Items-whiskey bucket
walk upstairs
You are in the attic. There is a giant welding torch in the corner. There is a
ladder going downstairs from here.
walk east
You cannot go that way.
walk downstairs
You are in the living-room. A wizard is snoring loudly on the couch. There is
a door going west from here. There is a ladder going upstairs from here.
walk west
You are in a beautiful garden. There is a well in front of you. There is a
door going east from here. You see a frog on the floor. You see
a chain on the floor.
dunk bucket well
The water level is too low to reach.
pickup chain
You are now carrying the chain
walk east
You are in the living-room. A wizard is snoring loudly on the couch. There is
a door going west from here. There is a ladder going upstairs
from here.
splash bucket wizard
The bucket has nothing in it.
W hat You've Le ar ne d
T his c ha pte r de m onstra te d how to c re a te DSL s in L isp. You le a rne d the following:
W he n you ne e d to do som e we ird progra m m ing for a ve ry spe c ific dom a in, Ma c ros a re a gre a t solution. W ith the m , you c a n
c re a te your own DSL .
Ofte n, it m a ke s se nse to first write a he lpe r func tion for a m a c ro (like print-tag), a nd the n write a m a c ro (like tag) to a dd
im prove m e nts tha t only a m a c ro c a n provide . T he se im prove m e nts usua lly involve be ing a ble to a c c e ss the c ode with a c le a re r,
a nd ofte n sa fe r, synta x.
You c a n m ix DSL s with re gula r L isp c ode , whic h give s you a lot of powe r.
DSL s a re use ful whe n you ne e d to write ve ry spe c ific c ode — whe the r it’s c ode for a we b pa ge , c ode tha t dra ws a pic ture , or
c ode tha t builds spe c ia l ga m e c om m a nds.
Chapte r 18. Laz y P r ogr amming
In Cha pte r 14, you le a rne d tha t your progra m s c a n be sim ple r a nd c le a ne r whe n built with c le a n, m a th-like func tions. T he se
func tions a lwa ys re turn the sa m e re sult, whic h de pe nds sole ly on the a rgum e nts pa sse d into the m . W he n you re ly only on the se
type s of func tions, you a re using the func tional programming sty le .
Howe ve r, whe n we use d the func tiona l progra m m ing style to c re a te the Dic e of Doom ga m e in Cha pte r 15, a proble m be c a m e
e vide nt: If your func tions re ly e ntire ly on the a rgum e nts pa sse d into the m , the stuff tha t you ne e d to pa ss into the m ofte n be c om e s
huge .
In the Dic e of Doom ga m e , we pa ss a round the game-tree va ria ble , whic h holds a ll the possible future sta te s of the ga m e boa rd.
T his is a truly m a ssive struc ture , e ve n on a m e a sly 3-by-3 boa rd! So while the ga m e ’s c urre nt de sign m a ke s our c ode ve ry sim ple
a nd e le ga nt, it doe sn’t a ppe a r to sc a le we ll to la rge r ga m e boa rds, whic h would ha ve e xpone ntia lly la rge r ga m e tre e s. T he only
wa y we c ould c onc e iva bly m a inta in our e le ga nt c ode while a llowing m ore c om ple x ga m e s on la rge r boa rds is to m a ke our progra m
sm a rt e nough not to look a t e ve ry c onc e iva ble m ove right from the sta rt of the ga m e . Is this possible ? Ye s, it is possible , using a
fe a ture c a lle d lazy e v aluation. In this c ha pte r, we ’ll e m ploy la z y e va lua tion to c re a te a n im prove d ve rsion of Dic e of Doom .
Adding Laz y Evaluation to Lisp
W ith la z y e va lua tion, we c a n still c re a te our e ntire ga m e tre e in a single pla c e in our c ode — a t the be ginning of our ga m e .
Howe ve r, we use som e c le ve r tric ks so tha t som e bra nc he s of our ga m e tre e a re hidde n in c louds:

T he bra nc he s of the ga m e tre e a re still de c la re d right from the sta rt. Howe ve r, we don’t bothe r doing a ll the a c tua l c a lc ula tions
for the bra nc he s in c louds, a s we would do whe n we c re a te a “ re a l” bra nc h. T his is the lazy pa rt of la z y e va lua tion.
Inste a d, we wa it to se e if a nyone “ looks” a t a c loudy bra nc h. T he m om e nt this ha ppe ns, POOF!, we c re a te a re a l bra nc h of our
ga m e tre e a t tha t spot:
T his m e a ns tha t the se bra nc he s in the ga m e tre e a re c re a te d only if som e pa rt of the c ode ha ppe ns to look a t the m . If the pla ye r
ne ve r c hoose s a pa rtic ula r m ove in the ga m e , a nd the AI ne ve r de c ide s to c onte m pla te it, our progra m will la z ily a void the
c a lc ula tions ne e de d to figure out wha t the give n bra nc h a c tua lly looks like .
Som e la ngua ge s, suc h a s Ha ske ll a nd Clojure L isp, c onta in support for la z y e va lua tion a s pa rt of the c ore of the la ngua ge . In
fa c t, Clojure e nc oura ge s its use a nd c le a rly de m onstra te s how use ful it is for func tiona l progra m m ing. Howe ve r, the ANSI Com m on
L isp sta nda rd doe s not c onta in a ny sim ila r fe a ture for suc h la z y e va lua tion. Fortuna te ly, with Com m on L isp’s powe rful m a c ro
syste m , we c a n e a sily a dd this fe a ture to the la ngua ge ourse lve s!
Cr e ating the laz y and for c e Commands
T he m ost ba sic c om m a nds for la z y e va lua tion we ’re going to c re a te a re lazy a nd force. T he lazy c om m a nd will be a wra ppe r
you c a n put a round a pie c e of c ode , te lling L isp tha t you would like the c ode to be e va lua te d in a la z y wa y, like this:
> (lazy (+ 1 2))
#<FUNCTION ...>
As you c a n se e , the c om pute r doe s not try to c a lc ula te the va lue of 1 plus 2. Inste a d, it sim ply re turns a func tion. T o ge t the
a c tua l re sult of the c a lc ula tion, we m ust c a ll our othe r ba sic la z y e va lua tion c om m a nd on a la z y va lue :
> (force (lazy (+ 1 2)))
3
T he im porta nt thing is tha t the c a lc ula tion wa s pe rform e d, but not whe n the la z y va lue wa s c re a te d— only whe n it wa s forc e d.
T o se e tha t this is the c a se , le t’s look a t a m ore c om ple x e xa m ple :

> (defun add (a b)


(princ "I am adding now")
(+ a b))
ADD

> (defparameter *foo* (lazy (add 1 2)))


*FOO*

> (force *foo*)

I am adding now
3
He re , we ’ve c re a te d our own add func tion, whic h, a s a side e ffe c t, prints a m e ssa ge to the c onsole showing whe n the a ddition is

ha ppe ning . Ne xt, we la z ily a dd two num be rs with our func tion a nd store the re sult in the va ria ble *foo* . So fa r, we
know the a ddition ha sn’t a c tua lly ha ppe ne d, sinc e the m e ssa ge “ I a m a dding now” ha s not ye t a ppe a re d.

T he n we force our va ria ble . By forc ing the va lue , the c a lc ula tion is a c tua lly pe rform e d, a nd the re sult of 3 is re turne d.

You c a n se e tha t the a ddition took pla c e whe n we forc e d the la z y va lue , sinc e our m e ssa ge wa s a lso printe d in the c onsole .
He re is the c ode for a sim ple im ple m e nta tion of lazy:

(defmacro lazy (&body body)

(let ((forced (gensym))


(value (gensym)))

`(let ((,forced nil)

(,value nil))

(lambda ()

(unless ,forced

(setf ,value (progn ,@body))

(setf ,forced t))

,value))))

W e im ple m e nt lazy by de c la ring a m a c ro . T his m a c ro will re quire two va ria ble s in the c ode it ge ne ra te s. W e ne e d to

de c la re the se a s gensym na m e s , a s disc usse d in Cha pte r 16. Ne xt, we be gin ge ne ra ting the c ode tha t the m a c ro will output

(note the ba c kquote a t the be ginning of this line ).

At the top of the c ode ge ne ra te d by the m a c ro is a de c la ra tion for two loc a l va ria ble s, using the gensym na m e s we c re a te d

. T he first va ria ble te lls us whe the r this la z y va lue ha s be e n forc e d ye t . If it is nil, the va lue c a n hide in a c loud. If the
va ria ble is true , the va lue is no longe r hidde n in a c loud, be c a use it ha s be e n forc e d.
Onc e the va lue ha s be e n c a lc ula te d through a c a ll to force, we store the re sulting va lue in a nothe r va ria ble , though initia lly this

va lue isn’t use d a nd is se t to nil . W he n our lazy m a c ro is c a lle d, we wa nt it to re turn a func tion, whic h c a n be c a lle d a t a

la te r tim e to forc e our la z y va lue to re turn a re sult. T he re fore , we de c la re a la m bda func tion ne xt .
Re m e m be r tha t a ny loc a l va ria ble s de c la re d outside this la m bda func tion will be c a pture d by the func tion a s a c losure . T his
m e a ns tha t the loc a l va ria ble s a bove will pe rsist be twe e n subse que nt c a lls of the la m bda func tion. W hy doe s this
m a tte r? W e ll, onc e the c loud goe s POOF!, we ha ve c om ple te d a ll the work to c a lc ula te a va lue , a nd we don’t wa nt to do it a ga in
whe n the la z y va lue is forc e d a nd c he c ke d a ga in m ultiple tim e s in the future . W e c a n a void this by re m e m be ring the va lue a fte r

the first force he re be twe e n c a lls.


W he n our la z y va lue is forc e d (by c a lling the la m bda func tion we c re a te d), the first que stion we m ust a sk ourse lve s is whe the r it

ha s be e n forc e d a lre a dy or is still hidde n be hind the c loud . For a va lue tha t ha s not ye t be e n forc e d, we go POOF! a nd

pe rform the la z y c a lc ula tion , a nd sa ve it a s our value. W e a lso m a rk it a s ha ving be e n forced . Now the c loud ha s
be e n de stroye d.

Onc e the c loud is gone , we c a n sim ply re turn our c a lc ula te d va lue . T his m a y ha ve be e n just c a lc ula te d, or it m a y a lre a dy
e xist from a pre vious c a ll to force.
Unlike the (a dm itte dly m ind-be nding) c ode for the lazy m a c ro, the force func tion is supe r-sim ple . All it doe s is c a ll the la m bda
func tion c re a te d by lazy:
(defun force (lazy-value)
(funcall lazy-value))
W e now ha ve a fully func tiona l se t of prim itive la z y e va lua tion c om m a nds. Ma ny diffe re nt type s of sophistic a te d tools c ould be
built on top of the se sim ple lazy a nd force c om m a nds.
Cr e ating a Laz y Lists Libr ar y
W e will now e m ploy our ne w c om m a nds to build a libra ry for lazy lists, ba se d loose ly on the ir im ple m e nta tion in Clojure . (In
Clojure , la z y lists a re re fe rre d to a s lazy se que nc e s. )
Sinc e the funda m e nta l c om m a nd for working with L isp lists is the cons c om m a nd, you shouldn’t be surprise d tha t the first
c om m a nd we c re a te for working with la z y lists is the lazy-cons c om m a nd:
(defmacro lazy-cons (a d)
`(lazy (cons ,a ,d)))
T his m a c ro e m ula te s the be ha vior of cons, e xc e pt tha t the re sult is wra ppe d in the lazy m a c ro. T o a c c om pa ny lazy-cons, we ’ll
a lso c re a te lazy-car a nd lazy-cdr c om m a nds:
(defun lazy-car (x)
(car (force x)))
(defun lazy-cdr (x)
(cdr (force x)))
All the se func tions do is forc e the la z y va lue a nd the n c a ll car a nd cdr, re spe c tive ly. L e t’s try using the se ne w c om m a nds:

> (defparameter *foo* (lazy-cons 4 7))


*FOO*

> (lazy-car *foo*)


4

> (lazy-cdr *foo*)


7

As you c a n se e , we c a n use lazy-cons e xa c tly a s we would use cons . T he n we c a n ta ke a pa rt a la z y c ons in the sa m e wa y

we would ta ke a pa rt a c ons .
So fa r, it looks like our la z y list func tions a re n’t a ny diffe re nt from the sta nda rd cons, car, a nd cdr func tions. Howe ve r, we c a n
a c tua lly use the m to pe rform som e pre tty a m a z ing fe a ts. Conside r, for insta nc e , the following de finition:
(defparameter *integers*

(labels ((f (n)

(lazy-cons n (f (1+ n)))))


(f 1)))
He re , we ’ve use d the lazy-cons c om m a nd to de c la re som e thing im possible : a va ria ble tha t holds a list of a ll positive inte ge rs!

W e do this by c re a ting a loc a l func tion f , whic h we the n c a ll re c ursive ly to build a n infinite c ha in of lazy-conse s, using a n

e ve r-inc re a sing num be r n . Onc e we ’ve de c la re d this se e m ingly im possible *integers* va ria ble , we c a n use it just a s you
m ight e xpe c t:
> (lazy-car *integers*)
1
> (lazy-car (lazy-cdr *integers*))
2
> (lazy-car (lazy-cdr (lazy-cdr *integers*)))
3
As long a s we stic k to using only our lazy- c om m a nds, we c a n pull wha te ve r we wa nt out of our infinite list of inte ge rs, forc ing
m ore a nd m ore num be rs from *integers* on a n a s-ne e de d ba sis.
Sinc e not a ll lists a re infinite (a s is the list of positive inte ge rs), we ’ll a lso ne e d to ha ve a c onc e pt of a lazy-nil to te rm ina te a
list. Sim ila rly, we ne e d a lazy-null func tion tha t we c a n use to c he c k if we ’ve re a c he d the e nd of a list, just a s the null func tion
c a n be use d to c he c k for the e nd of a re gula r list.
(defun lazy-nil ()
(lazy nil))
(defun lazy-null (x)
(not (force x)))
Now tha t we ha ve a ll the ba sic building bloc ks for working with la z y lists, le t’s c re a te som e use ful func tions for our libra ry.
Conve r ting Be twe e n Re gular Lists and Laz y Lists
One obvious thing we would wa nt to be a ble to do is c onve rt a re gula r list into a la z y list. T he make-lazy func tion a llows us to
do this:
(defun make-lazy (lst)

(lazy (when lst

(cons (car lst) (make-lazy (cdr lst))))))


As the make-lazy func tion c le a rly shows, writing la z y list libra ry func tions is sort of like writing z e n koa ns. T he only wa y to
unde rsta nd the m is to sta re a t the m for a long tim e . T he E nglish la ngua ge doe sn’t ha ve a ppropria te words for c le a rly e xpla ining
func tions like make-lazy.

In broa d te rm s, make-lazy use s re c ursion to tra ve l a c ross the list , a nd the n wra ps e a c h c ons in a c a ll to the lazy m a c ro

. Howe ve r, to ge t the full m e a ning of this func tion (a nd the othe r re m a ining func tions in our la z y libra ry), you’ll just ha ve to
try to think c a re fully a bout wha t lazy a nd force re a lly m e a n, a nd m e dita te a bit ove r e a c h func tion. L uc kily, onc e our little la z y
list libra ry is c om ple te , it will hide m ost of the stra nge ne ss of la z y e va lua tion.
Just a s we wrote the make-lazy func tion to c onve rt re gula r lists to la z y lists, we c a n c re a te som e func tions to do the re ve rse —
c onve rt la z y lists into re gula r one s. T he take a nd take-all func tions a llow us to do this.

(defun take (n lst)


(unless (or (zerop n) (lazy-null lst))
(cons (lazy-car lst) (take (1-n) (lazy-cdr lst)))))

(defun take-all (lst)


(unless (lazy-null lst)
(cons (lazy-car lst) (take-all (lazy-cdr lst)))))
T he re a son we wa nt two diffe re nt c om m a nds for going from la z y to re gula r lists is tha t, unlike re gula r lists, la z y lists c a n be
infinite . T he re fore , it is use ful to ha ve a n a dditiona l c om m a nd tha t le ts us ta ke just a spe c ifie d num be r of ite m s from the list. T he

take func tion a c c e pts a n e xtra a rgum e nt n tha t indic a te s just how m a ny va lue s we wa nt to ta ke . If we just wa nt a ll va lue s,

we c a n c a ll the take-all func tion . Of c ourse , this func tion c a nnot be use d on infinite lists— ta king a ll ite m s from a n infinite
list would le a d to a n infinite loop.
L e t’s try out our ne w la z y list c onve rsion func tions:

> (take 10 *integers*)


(1 2 3 4 5 6 7 8 9 10)

> (take 10 (make-lazy '(q w e r t y u i o p a s d f)))


(Q W E R T Y U I O P)
> (take-all (make-lazy '(q w e r t y u i o p a s d f)))

(Q W E R T Y U I O P A S D F)
As you would e xpe c t, if we ta ke the first 10 inte ge rs off the list of a ll positive inte ge rs, we just ge t the num be rs 1 through 10 a s a

re sult . T he take func tion c a n a lso be use d on a finite list we ’ve c re a te d by c a lling make-lazy . Howe ve r, if a list is

finite , we c a n use the sim ple r take-all func tion a nd just ge t a re gula r list of a ll ite m s in the la z y list .
M apping and Se ar c hing Ac r oss Laz y Lists
W e a lso wa nt to be a ble to m a p a nd se a rc h a c ross la z y lists. He re a re som e func tions to a llow tha t:
(defun lazy-mapcar (fun lst)
(lazy (unless (lazy-null lst)
(cons (funcall fun (lazy-car lst))
(lazy-mapcar fun (lazy-cdr lst))))))
(defun lazy-mapcan (fun lst)
(labels ((f (lst-cur)
(if (lazy-null lst-cur)
(force (lazy-mapcan fun (lazy-cdr lst)))
(cons (lazy-car lst-cur) (lazy (f (lazy-cdr lst-cur)))))))
(lazy (unless (lazy-null lst)
(f (funcall fun (lazy-car lst)))))))
(defun lazy-find-if (fun lst)
(unless (lazy-null lst)
(let ((x (lazy-car lst)))
(if (funcall fun x)
x
(lazy-find-if fun (lazy-cdr lst))))))
(defun lazy-nth (n lst)
(if (zerop n)
(lazy-car lst)
(lazy-nth (1-n) (lazy-cdr lst))))
T he se func tions a re a na logous to the func tions mapcar, mapcan, find-if, a nd nth. T he only diffe re nc e is tha t the y a c c e pt a nd
re turn la z y lists. T his m e a ns tha t inste a d of using null, car, a nd cdr, the y use the la z y ve rsions of the se func tions (lazy-null,
lazy-car, a nd lazy-cdr) tha t we just c re a te d.
Using the se func tions is pre tty stra ightforwa rd:

> (take 10 (lazy-mapcar #'sqrt *integers*))


(1 1.4142135 1.7320508 2 2.236068 2.4494898
2.6457512 2.828427 3 3.1622777)

> (take 10 (lazy-mapcan (lambda (x)


(if (evenp x)

(make-lazy (list x))

(lazy-nil)))
*integers*))
(2 4 6 8 10 12 14 16 18 20)

> (lazy-find-if #'oddp (make-lazy '(2 4 6 7 8 10)))


7

> (lazy-nth 4 (make-lazy '(a b c d e f g)))


E
Ca lling lazy-mapcar to m a p the squa re root func tion a c ross the positive inte ge rs give s us a la z y list of the squa re roots of the

positive inte ge rs. T he first 10 a re shown . Ne xt, we c a ll lazy-mapcan a nd c he c k if e a c h positive inte ge r is e ve n. If it is,

we re turn a la z y list of the num be rs . If it isn’t, we re turn the la z y e m pty list . T he re sult is tha t we ’ve filte re d out a ll

the e ve n num be rs from our la z y list of inte ge rs. W e c a n use lazy-find-if to find the first odd num be r in a la z y list . In this

c a se , the num be r wa s 7. Fina lly, we c a n use lazy-nth to pic k a num be r out of a spe c ific loc a tion in a la z y list .
W e ha ve now writte n a n e ntire , if ra the r sim ple , la z y list libra ry. Pla c e a ll the func tions we ’ve writte n so fa r in this c ha pte r in a
file na m e d lazy . lisp (or sim ply downloa d tha t file from http://la ndoflisp. c om /).
Now, you’re going to se e tha t la z y lists a llow us to gre a tly boost the powe r of our Dic e of Doom ga m e e ngine !
Dic e of Doom, Ve r sion 2
In Cha pte r 15, we c re a te d the first ve rsion of our Dic e of Doom ga m e . W e a re now going to m odify som e of the func tions from
tha t ve rsion. T o proc e e d, pla c e the c ode from tha t c ha pte r into a file na m e d dic e _of_doom_v 1. lisp so tha t we c a n re fe re nc e it in
this ne w ve rsion (or just downloa d tha t file from http://la ndoflisp. c om /).
T o use our pre vious Dic e of Doom a nd our ne w la z y list libra ry, run the following in the RE PL :
> (load "dice_of_doom_v1.lisp")
> (load "lazy.lisp")
Ne xt, we ’re going to inc re a se the siz e of our boa rd to a m ore room y 4-by-4:
> (defparameter *board-size* 4)
> (defparameter *board-hexnum* (* *board-size* *board-size*))
T o a llow the ga m e to run a t a re a sona ble spe e d a t this la rge r siz e , we ’ll m a ke the list of m ove s a t e a c h bra nc h of our ga m e tre e
a la z y list, inste a d of just a re gula r list. By sim ply c onve rting this one struc ture in our ga m e from a re gula r list to a la z y list, the
e ntire ga m e tre e will be c om e la z y a s a re sult. T o a c c om plish this, we now ne e d to re de fine som e of the func tions from the first
ve rsion of our ga m e to use our ne w la z y list func tions.
First, le t’s m a ke som e sm a ll m odific a tions to the func tions tha t c a lc ula te the a tta c king a nd pa ssing m ove s possible from a give n
boa rd position:
(defun add-passing-move (board player spare-dice first-move moves)
(if first-move
moves

(lazy-cons (list nil


(game-tree (add-new-dice board player
(1-spare-dice))
(mod (1+ player) *num-players*)
0
t))
moves)))

(defun attacking-moves (board cur-player spare-dice)


(labels ((player (pos)
(car (aref board pos)))
(dice (pos)
(cadr (aref board pos))))

(lazy-mapcan
(lambda (src)
(if (eq (player src) cur-player)

(lazy-mapcan
(lambda (dst)
(if (and (not (eq (player dst)
cur-player))
(> (dice src) (dice dst)))

(make-lazy
(list (list (list src dst)
(game-tree (board-attack board
cur-player
src
dst
(dice src))
cur-player
(+ spare-dice (dice dst))
nil))))

(lazy-nil)))

(make-lazy (neighbors src)))

(lazy-nil)))

(make-lazy (loop for n below *board-hexnum*


collect n)))))
As you c a n se e , the add-passing-move func tion ne e ds only one sm a ll c ha nge . Sinc e the list of m ove s is now a la z y list, we use

lazy-cons to a dd a pa ssing m ove to the top of the list of possible m ove s .


T he attacking-moves func tion re quire s a fe w m ore c ha nge s. First, sinc e it now ne e ds to re turn a la z y list, we use lazy-mapcan

in lie u of mapcan in two pla c e s a s the m ove s a re c a lc ula te d . T he lazy-mapcan func tion a lso re quire s the lists c re a te d

inside it to be la z y, whic h we a c c om plish with the make-lazy func tion . Also, a ny pla c e we re turne d nil we now

inste a d re turn a lazy-nil . Fina lly, we a lso m a ke the list of c a lc ula te d boa rd positions la z y , sinc e it is fe d into
the oute r lazy-mapcan.
Ne xt, le t’s m a ke sim ila r c ha nge s to two of the func tions tha t de a l with hum a n pla ye rs:
(defun handle-human (tree)
(fresh-line)
(princ "choose your move:")
(let ((moves (caddr tree)))
(labels ((print-moves (moves n)

(unless (lazy-null moves)

(let* ((move (lazy-car moves))


(action (car move)))
(fresh-line)
(format t "˜a. " n)
(if action
(format t "˜a -> ˜a" (car action) (cadr action))
(princ "end turn")))

(print-moves (lazy-cdr moves) (1+ n)))))


(print-moves moves 1))
(fresh-line)

(cadr (lazy-nth (1- (read)) moves))))


(defun play-vs-human (tree)
(print-info tree)

(if (not (lazy-null (caddr tree)))


(play-vs-human (handle-human tree))
(announce-winner (cadr tree))))
In the handle-human func tion, we ha ve a loc a l func tion print-moves, whic h is a list-e a te r func tion a c ross the list of m ove s. W e

m odify it to use our la z y c om m a nds whe n c he c king for the e nd of the list , ta king a m ove off the front of the list , a nd

re c ursing a c ross the ta il of the list . Fina lly, we m odify handle-human to use lazy-nth to pic k a m ove a fte r the hum a n

c hoose s it from the list of options .


In the play-vs-human func tion, we m a ke just a single pinpoint c ha nge . In orde r to de te rm ine whe the r we ’ve re a c he d the e nd of a
ga m e , we ne e d to c he c k whe the r the list of subse que nt possible m ove s is e m pty, a nd the n a nnounc e the winne r. W e sim ply use

lazy-null to c he c k if the la z y list of m ove s is e m pty .


W ith the se sim ple c ha nge s in pla c e , you c a n pla y Dic e of Doom a ga inst a nothe r hum a n on m uc h la rge r boa rd siz e s, sinc e no
m ove in the tre e is re a liz e d unle ss one of the pla ye rs de c ide s to m a ke it. On our la rge r, 4-by-4 boa rd, e nte r the following to sta rt a
ga m e (just a s for ve rsion 1 of our ga m e ):
> (play-vs-human (game-tree (gen-board) 0 0 t))
current player = a
a-1 a-3 a-1 b-2
b-3 a-3 a-3 a-1
a-3 a-3 b-1 a-2
b-3 a-3 a-1 a-3
choose your move:
1. 5 -> 10
2. 6 -> 10
3. 9 -> 10
4. 11 -> 10
5. 15 -> 10
Ve rsion 1 would sc re e c h to a ha lt the m om e nt this c om m a nd wa s e xe c ute d. T his is be c a use it would ne e d to ge ne ra te the
e ntire ty of the ga m e tre e , for e v e ry possible mov e of the whole game , be fore the ga m e would e ve n sta rt pla ying.
W ith our la z y ve rsion of Dic e of Doom , the ga m e sta rts insta ntly!
M aking O ur AI W or k on Lar ge r G ame Boar ds
Ne xt, we ’re going to a djust our ga m e AI func tions to use the ne w la z y list libra ry whe n proc e ssing m ove s. Along the wa y, we will
m a ke som e a dditiona l im prove m e nts to the AI c ode .
Tr imming the G ame Tr e e

In ve rsion 1 of Dic e of Doom , our AI c ode wa s, in c e rta in wa ys, e xtre m e ly powe rful. T his is be c a use , a t e ve ry de c ision point, the
AI pla ye r would look a t e v e ry possible future board position to c hoose the a bsolute be st ne xt m ove . In this wa y, it c ould pla y a
pe rfe c t ga m e of Dic e of Doom , winning e ve ry ga m e tha t wa s winna ble .
Howe ve r, suc h a de sign doe s not sc a le to la rge r boa rds. T his is be c a use it be c om e s im possible to c onte m pla te e ve ry single
possible future m ove onc e the re a re too m a ny. In fa c t, the whole point of our ne w la z y ga m e tre e is to a void c onte m pla ting e ve ry
possible m ove . T he re fore , we ne e d a wa y to te ll the c om pute r, “ Conside r only this m a ny m ove s, a nd no m ore . ” In othe r words, we
wa nt to be a ble te ll it to look only two, thre e , or four m ove s a he a d, a nd the n stop looking a ny furthe r.
T he func tiona l progra m m ing style of Dic e of Doom a llows us to do this in a ve ry e le ga nt but nonobvious wa y.
T he obv ious solution to the proble m would be to m odify the get-ratings a nd rate-position from ve rsion 1 to ha ve a ne w
a rgum e nt c a lle d search-depth. T he n we c ould a sk ourse lve s a t e ve ry c a ll of those func tions, “ Ha ve we re a c he d the m a xim um
se a rc h de pth we wa nt? ”
T he proble m with this a pproa c h is tha t it gunks up those func tions with e xtra , c onfusing c ode . In fa c t, the wa y we e va lua te boa rd
positions is the ore tic a lly a se pa ra te issue from how de e p we wish to se a rc h. As progra m m e rs like to sa y, the se issue s a re orthogonal,
a nd it would be be st if we c ould write se pa ra te func tions to de a l with e a c h of the se issue s inde pe nde ntly.
In fa c t, with our ne w la z y ga m e tre e , it is possible to write a se pa ra te func tion tha t is sole ly re sponsible for “ trim m ing” the
se a rc h tre e a nd is c om ple te ly inde pe nde nt from the m a in AI c ode tha t c onte m pla te s a nd ra te s possible m ove s.
He re is the func tion tha t trim s our tre e :

(defun limit-tree-depth (tree depth)


(list (car tree)
(cadr tree)

(if (zerop depth)

(lazy-nil)
(lazy-mapcar (lambda (move)
(list (car move)

(limit-tree-depth (cadr move) (1-depth))))


(caddr tree)))))

T his is a pre tty sim ple func tion tha t ta ke s just two a rgum e nts: a la z y tre e a nd the de pth to whic h we wish to trim it . As a
re sult, it just outputs a ne w ga m e tre e , c a lling itse lf re c ursive ly, de c re m e nting the de pth for e a c h le ve l it tra ve ls into the tre e

. Onc e this de pth re a c he s z e ro , we know we ’re a t the le ve l tha t we wa nt to trim , a nd we se t the la z y list of m ove s to

the e m pty list .


Now a ll we ne e d to do is c a ll our ne w limit-tree-depth func tion be fore doing our AI ra ting c a lc ula tions. W e do this by
twe a king our handle-computer func tion a bit:
(defparameter *ai-level* 4)

(defun handle-computer (tree)

(let ((ratings (get-ratings (limit-tree-depth tree *ai-level*)


(car tree))))
(cadr (lazy-nth (position (apply #'max ratings) ratings)

(caddr tree)))))
Be fore c a lling get-ratings to ge t a ra ting for e ve ry ne xt a va ila ble m ove , we tra nsform our ga m e tre e into our trim m e d ga m e
tre e . All of our AI c ode c a n now run on the trim m e d tre e , c om ple te ly oblivious to the fa c t tha t a la rge r ga m e tre e e xists or
tha t the re a re de e pe r m ove s it isn’t inc luding in its c a lc ula tions. W ith this te c hnique , we ha ve m a na ge d to de c ouple the c ode tha t
lim its the AI se a rc h de pth from the a lgorithm tha t a c tua lly e va lua te s boa rd positions. One othe r sm a ll m odific a tion is to use lazy-

nth whe n pic king a m ove out of the la z y list of m ove s .

Note

T he limit-tree-depth func tion use s a pre tty c rude m e thod for trim m ing our tre e : It sim ply trim s a ll tre e bra nc he s be yond a
c e rta in de pth. For m ost boa rd ga m e s, doing this is a n optim a l wa y of trim m ing the ga m e tre e . Howe ve r, Dic e of Doom ha s the
unc om m on prope rty tha t m ultiple m ove s in a row a re a llowe d for e a c h pla ye r. It would proba bly be m ore optim a l if limit-tree-
depth took into a c c ount how m a ny tim e s we ’ve switc he d pla ye rs a s a c rite rion for trim m ing a bra nc h. But our sim ple r ve rsion
works we ll e nough.

At this point, we should a lso m a ke a pinpoint c ha nge to play-vs-computer:


(defun play-vs-computer (tree)
(print-info tree)

(cond ((lazy-null (caddr tree)) (announce-winner (cadr tree)))


((zerop (car tree)) (play-vs-computer (handle-human tree)))
(t (play-vs-computer (handle-computer tree)))))

He re , we just a dde d a lazy-null to c he c k for the e nd of the la z y list of m ove s in a single spot .
Now le t’s look a t a nothe r tric k tha t will im prove the powe r of our AI c ode .
Applying H e ur istic s
By trim m ing our ga m e tre e , we ’ve funda m e nta lly c ha nge d our AI pla ye r. W ithout trim m ing, the AI pla ye r wa s a ble to pla y a
pe rfe c t ga m e a t a ll tim e s. By trim m ing the tre e , howe ve r, it is possible for the AI to “ m iss som e thing, ” sinc e it is no longe r
c onte m pla ting e ve ry possible future m ove . In ve rsion 2 of Dic e of Doom , the c om pute r pla ye r will no longe r be a ble to pla y a
pe rfe c t ga m e — just a “ pre tty good” ga m e is possible .
Ba sic a lly, we ’ve e xc ha nge d the AI’s a bility to pla y a pe rfe c t ga m e for m uc h be tte r pe rform a nc e . In the proc e ss, we ’ve turne d
the AI c ode from som e thing pre c ise tha t c a n be a na lyz e d by m a the m a tic s into som e thing tha t is “ squishie r” a nd fa r le ss pre c ise . As
c om pute r sc ie ntists would sa y, we ha ve now e nte re d into the re a lm of he uristic s.
In c om pute r sc ie nc e , he uristic s a re progra m m ing te c hnique s tha t a re im pe rfe c t, but a llow us to ge t good re sults ve ry quic kly.
Broa dly spe a king, a ny te c hnique tha t is fa st but not gua ra nte e d to work 100 pe rc e nt of the tim e is a he uristic . W he n we write c ode
tha t use s he uristic s (a s our Dic e of Doom AI e ngine now doe s), it is ofte n worthwhile to use som e c re a tive thinking a nd to “ pla y
a round” with the c ode in diffe re nt wa ys.
Ba sic a lly, sinc e we ’re a lre a dy give n up on our goa l of a pe rfe c t solution a nd a re now using im pre c ise te c hnique s, it’s possible
tha t twe a king the knobs on the he uristic c ode in diffe re nt wa ys c ould dra m a tic a lly im prove our re sults. And inde e d, it turns out tha t
the re is a sim ple c ha nge we c a n m a ke to our Dic e of Doom AI he uristic s tha t will signific a ntly im prove the AI pla ye r’s ga m e .
W inning by a Lot vs. W inning by a Little
In ve rsion 1 of our Dic e of Doom c ode , the AI pla ye r ha d no re a son to e ve r worry a bout its m a rgin of vic tory. All it c a re d a bout
wa s tha t whe n the ga m e e nde d, it ha d owne rship of a t le a st one m ore te rritory of the boa rd tha n its oppone nt, whic h m e a nt it ha d
won.
Howe ve r, now tha t we ’re using im pre c ise he uristic s in our AI c ode , it m a tte rs a lot how la rge the le a d is a t a ny point in the
ga m e . A he uristic rule for this situa tion is “ If I a m tota lly whom ping m y oppone nt in the ga m e , it is pre tty unlike ly he /she will be
a ble to re c ove r, e ve n if I look only a fe w m ove s a he a d. ”
Re m e m be r tha t a m inim a x a lgorithm (a s we ’re using in our AI) a ssigns a point sc ore to e ve ry fina l le a f bra nc h in the tre e . In
ve rsion 1 of our ga m e , this sc ore wa s e ithe r 0 or 1, or som e tim e s 1/2 whe n the ga m e e nde d in a tie . In ve rsion 2, the se a re not truly
“ fina l le a ve s” in the tre e , but sim ply le a ve s in our m uc h sm a lle r trim m e d tre e . In this situa tion, it would be m uc h be tte r if our
le a f point sc ore s ha d a la rge r ra nge of va lue s, so tha t we c a n te ll whic h m ove s le a d to a ga m e we ’re winning by “ a lot” a nd whic h
m ove s le a d to a ga m e we ’re winning by only “ a little . ”
L e t’s write a score-board func tion tha t use s som e m ore c om ple x he uristic s to sc ore the boa rd position a t a le a f:
(defun score-board (board player)

(loop for hex across board


for pos from 0

sum (if (eq (car hex) player)

(if (threatened pos board)

2)

−1)))

T he score-board func tion loops a c ross a ll of the he xe s of the boa rd a nd builds a running tota l of points for e a c h he x using

the sum dire c tive of the loop m a c ro. If the pla ye r we ’re sc oring owns the c urre nt he x , we wa nt to a dd positive points to the

tota l .
T o de c ide e xa c tly how m a ny points to a dd to the tota l for a n oc c upie d he x, we m a ke a nothe r he uristic obse rva tion: He xe s tha t
ne ighbor a stronge r oppone nt a re n’t quite a s va lua ble a s he xe s without strong ne ighbors. W e ’ll c a ll a he x tha t ne ighbors a n e ne m y

he x tha t ha s m ore dic e on it a thre ate ne d he x . For he xe s tha t a re thre a te ne d , we ’ll a dd only 1 point to the point tota l .

For he xe s tha t a re unthre a te ne d, we ’ll a dd 2 points . Fina lly, for e a c h he x owne d by a n opposing pla ye r, we ’ll subtra c t 1 point

from the tota l .


Aga in, the im porta nt thing to re a liz e is tha t score-board is a he uristic func tion, a nd the re is no truly right or wrong wa y to
ge ne ra te suc h a sc ore . Inste a d of a dding 2 points for unthre a te ne d he xe s, we c ould just a s e a sily ha ve a dde d 1. 5 points. In
de ve loping this e xa m ple , I ra n som e sim ula tions pla ying va rious oppone nts using diffe re nt ve rsions of the score-board func tion,
a nd this ve rsion e nde d up working re a sona bly we ll. De ve loping he uristic s is not a n e xa c t sc ie nc e .
He re is the func tion tha t de te rm ine s whe the r a give n he x is thre a te ne d:
(defun threatened (pos board)

(let* ((hex (aref board pos))


(player (car hex))
(dice (cadr hex)))

(loop for n in (neighbors pos)

do (let* ((nhex (aref board n))


(nplayer (car nhex))
(ndice (cadr nhex)))

(when (and (not (eq player nplayer)) (> ndice dice))

(return t))))))

First, we ge t the he x in que stion a nd figure out who the oc c upying pla ye r is, a nd how m a ny dic e tha t pla ye r ha s . T he n we

loop through a ll the ne ighboring squa re s for the c urre nt position . Afte r tha t, we find out the pla ye r a nd dic e c ount for e a c h of

the ne ighbors . As soon a s we find a ne ighboring he x owne d by a n oppone nt with a la rge r dic e c ount (a thre a te ning ne ighbor)

, we c a n re turn true . Ca lling return in this wa y c a use s the loop to stop e a rly with true a s a re sult.
Now tha t we ha ve c om ple te d our score-board a nd threatened func tions, we ’re re a dy to write our im prove d get-ratings a nd
rate-position func tions:
(defun get-ratings (tree player)

(take-all (lazy-mapcar (lambda (move)


(rate-position (cadr move) player))
(caddr tree))))
(defun rate-position (tree player)
(let ((moves (caddr tree)))

(if (not (lazy-null moves))


(apply (if (eq (car tree) player)
#'max
#'min)
(get-ratings tree player))

(score-board (cadr tree) player))))

As you c a n se e , we ’ve upda te d a c ouple line s of c ode to be c om pa tible with our ne w la z y ga m e tre e . Notic e tha t a ny

ga m e positions tha t la c k follow-up m ove s (tha t is, le a ve s) now c a use our ne w sc ore -boa rd func tion to be c a lle d .
Now tha t we ha ve a fully working he uristic AI pla ye r tha t c a n pla y on la rge r ga m e boa rds, le t’s try it out. As usua l, a ll m ove s for
pla ye r B in the following e xa m ple a re be ing a utom a tic a lly c a lc ula te d by the AI a lgorithm :
> (play-vs-computer (game-tree (gen-board) 0 0 t))
current player = a
a-1 b-2 b-1 a-3
b-3 a-1 a-3 a-3
b-3 b-2 b-2 b-2
a-3 a-3 a-2 a-2
choose your move:
1. 3 -> 2
2. 6 -> 2
3. 6 -> 10
4. 6 -> 1
5. 6 -> 11
6. 7 -> 11
7. 7 -> 2
8. 13 -> 9
3
current player = a
a-1 b-2 b-1 a-3
b-3 a-1 a-1 a-3
b-3 b-2 a-2 b-2
a-3 a-3 a-2 a-2
choose your move:
1. end turn
2. 3 -> 2
3. 7 -> 11
4. 7 -> 2
5. 13 -> 9
1
current player = b
a-2 b-2 b-1 a-3
b-3 a-1 a-1 a-3
b-3 b-2 a-2 b-2
a-3 a-3 a-2 a-2
current player = b
a-2 b-1 b-1 a-3
b-3 b-1 a-1 a-3
b-3 b-2 a-2 b-2
a-3 a-3 a-2 a-2
current player = b
b-2 b-1 b-1 a-3
b-1 b-1 a-1 a-3
b-3 b-2 a-2 b-2
a-3 a-3 a-2 a-2
current player = b
b-2 b-1 b-1 a-3
b-1 b-1 b-1 a-3
b-3 b-2 a-2 b-1
a-3 a-3 a-2 a-2
current player = a
b-3 b-2 b-2 a-3
b-1 b-1 b-1 a-3
b-3 b-2 a-2 b-1
a-3 a-3 a-2 a-2
choose your move:
1. 3 -> 2
2. 7 -> 11
3. 7 -> 2
4. 7 -> 6
5. 10 -> 6
6. 10 -> 5
7. 10 -> 11
8. 13 -> 9
9. 15 -> 11
...
W ith the se c ha nge s in pla c e , the AI pla ye r will win a round 65 to 70 pe rc e nt of a ll ga m e s (de pe nding on the boa rd siz e a nd AI
le ve l) whe n pitte d a ga inst a pla ye r tha t c hoose s only ra ndom m ove s. T his is a c tua lly a ve ry good re sult. Our sim ple gen-board
func tion ofte n c re a te s ve ry lopside d sta rting positions, so m a ny of the re m a ining 30 pe rc e nt of the ga m e s a re sim ply unwinna ble for
the c om pute r.
Alpha Be ta P r uning
L e t’s a dd one fina l im prove m e nt to ve rsion 2 of our Dic e of Doom AI.
A lpha-be ta pruning is a we ll-known optim iz a tion of the m inim a x a lgorithm tha t im prove s pe rform a nc e by skipping ove r som e
bra nc he s (pruning those bra nc he s) if it is c e rta in tha t the y will not im pa c t the fina l m inim a x e va lua tion.

W he n would a bra nc h in the ga m e tre e be una ble to im pa c t the fina l re sult? In orde r to unde rsta nd how a lpha -be ta pruning
works, look a t the following pic ture , showing the ga m e tre e for a sim ple 2-by-2 boa rd:
At the top of this pic ture is the sta rting position of the ga m e . T he a rrows point to possible m ove s. Above e a c h boa rd it sta te s
whic h pla ye r (A or B) c urre ntly is m a king a m ove .
T he pic ture a lso shows the re sults of a m inim a x a na lysis of the ga m e tre e . On the bottom right of e a c h boa rd, you c a n se e a
num be r showing how our la te st get-ratings func tion (with the ne w score-board logic ) would ra te tha t position. For le a f node s (the
boa rds a long the ve ry bottom ), this num be r is c a lc ula te d through score-board. For bra nc h node s, the num be r is c a lc ula te d ba se d on
the m inim a x a lgorithm .
E ve ry position in the ga m e tre e tha t a llows a c hoic e of m ove s is m a rke d e ithe r a s a MAX node or MIN node . Sinc e the a na lysis
in the pic ture is ba se d on finding the be st m ove for pla ye r A, a ll pla c e s a llowing c hoic e s for pla ye r A a re m a rke d a s MAX. All
positions a llowing c hoic e s for pla ye r B a re m a rke d a s MIN. As you c a n se e from the pic ture , this ga m e is pre tty une xc iting, a nd
the re is only one position whe re pla ye r B a c tua lly ha s a c hoic e of m ove s. In othe r words, only one MIN node e xists in the ga m e
tre e .
W orking le ft to right, the m inim a x a lgorithm tra ve ls, de pth first, e xploring a ll the wa y down to the le a ve s. T his is c a lle d a
de pth-first se arc h. (W e ’re a ssum ing no trim m ing is oc c urring, with *ai-level* se t ve ry high. ) T he n it c hoose s e ithe r the m a xim um
or m inim um sc ore s for a ny node s tha t ha ve m ore tha n one bra nc h.
W he n it doe s this, the first (le ft) bra nc h of the MIN node in the pic ture e nds up with a sc ore of 8. If the AI e ngine now dips into
the right bra nc h, it re a lly only c a re s wha t it finds the re a s long a s the sc ore re m a ins be low 8. Afte r a ll, the m inim um of 8 a nd a ny
la rge r num be r la rge r tha n 8 will still be 8, m a king suc h la rge num be rs irre le va nt to the e ve ntua l outc om e of the c a lc ula tion.
As soon a s the AI finds a node in the right bra nc h tha t ha s a sc ore of 8 (m a rke d with a sta r in the pic ture ), it knows the re st of the
right bra nc h is irre le va nt a nd c a n be prune d a wa y from our c a lc ula tions. T his m e a ns the m inim a x a lgorithm ha s no ne e d to look a t
the bra nc h in the tre e m a rke d with the dotte d line in the pic ture .
T his is a sim ple e xa m ple , showing a lpha -be ta pruning in a c tion. In the ga m e tre e shown in the pic ture , this pruning le a ds to only
m ode st sa vings, sinc e just a sm a ll num be r of the tota l node s c a n be prune d. Howe ve r, with la rge r ga m e tre e s, the sa vings from
a lpha -be ta pruning a re typic a lly im m e nse , c onstituting a m a jority of the node s in the ga m e tre e .
W e ’re going to ta ke som e libe rtie s in how we im ple m e nt a lpha -be ta pruning in our ga m e to ke e p things sim ple . First, a n a lpha -
be ta pruning a lgorithm usua lly will pa ss a round two va ria ble s c a lle d, na tura lly, alpha a nd beta.
T his is be c a use it’s possible to write c ode tha t ha ndle s both the MAX node s a nd MIN node s a t onc e by switc hing alpha a nd beta
be twe e n the high a nd low lim its. In our e xa m ple , we ’re going to use the va ria ble s upper-limit a nd lower-limit inste a d,
indic a ting the highe st a nd lowe st va lue s we c a re a bout a s we tra ve rse the ga m e tre e . As a c ost, the re will be som e re pe titive -
looking c ode for ha ndling the MAX a nd MIN c a se s. Howe ve r, thinking of a lpha -be ta pruning in te rm s of upper-limit a nd lower-
limit m a ke s the c ode a bit e a sie r to unde rsta nd.
Anothe r c om prom ise we ’re m a king is tha t we ’re not de c oupling the pruning c ode from the m inim a x c ode . Re m e m be r tha t with
the trim m ing c ode , we wrote a n inde pe nde nt func tion na m e d limit-tree-depth, whic h se pa ra te d the a c t of trim m ing from the re st
of the AI c ode . W e c ould use a sim ila r a pproa c h for se pa ra ting the a lpha -be ta pruning c ode a s we ll, c re a ting a func tion tha t c a n
tra nsform the ga m e tre e into a prune d ve rsion on its own. Howe ve r, doing this is a bit m ore involve d, be c a use the a lpha -be ta
pruning c ode m ust ha ve a c c e ss to inte rm e dia te m inim a x c a lc ula tions. For a m ore a dva nc e d AI e ngine , this would be a good ide a .
For our sim ple e ngine , we will just a dd our a lpha -be ta pruning c he c k dire c tly inside our m inim a x func tions.
So le t’s ge t sta rte d. First, we ’ll re write our get-ratings func tion a s two ne w func tions: ab-get-ratings-max a nd ab-get-
ratings-min.
Re m e m be r tha t the get-ratings func tion wa s re sponsible for c a lc ula ting the be st sc ore out of m ultiple a va ila ble m ove s from a
single -boa rd a rra nge m e nt. Now, howe ve r, we wa nt it to stop e a rly in its e va lua tion of m ove s onc e it de c ide s it ha s found a m ove
tha t’s “ a s good a s is possible . ” De te rm ining whe the r it ha s re a c he d this point is subtly diffe re nt de pe nding on whe the r the node in
que stion is a MAX m ove (a m ove of the c urre nt pla ye r) or a MIN m ove (a m ove for the oppone nt).
L e t’s look a t the ve rsion re sponsible for MAX node s first:

(defun ab-get-ratings-max (tree player upper-limit lower-limit)


(labels ((f (moves lower-limit)
(unless (lazy-null moves)

(let ((x (ab-rate-position (cadr (lazy-car moves))


player
upper-limit
lower-limit)))

(if (>= x upper-limit)

(list x)

(cons x (f (lazy-cdr moves) (max x lower-limit))))))))


(f (caddr tree) lower-limit)))

W e ’re now pa ssing in a n e xtra upper-limit a nd lower-limit a rgum e nt into ab-get-ratings-max . T his func tion won’t
a c tua lly e ve r c he c k the lower-limit a rgum e nt dire c tly, sinc e it is c onc e rne d only with finding the m a xim um ra ting possible from
the give n loc a tion in the tre e . Howe ve r, it will pa ss this va lue on to c hild bra nc he s, whic h m a y c onta in MIN node s tha t do c a re
a bout the lowe r lim it.

W he n we ra te the ne xt bra nc h of the tre e (by c a lling ab-rate-position, whic h we ’ll write shortly), we sa ve the re sult a s

x. If x is gre a te r tha n or e qua l to our upper-limit , we know we got a re sult a s good a s we c a n hope for, a nd c a n just re turn

the la te st ra ting a s a fina l va lue in our list .

If x isn’t la rge e nough, we ne e d to ke e p looking a t the re m a ining bra nc he s . Note tha t x will be c om e the ne w lower-limit
if it’s la rge r tha n the pre vious lower-limit.
Ne xt, le t’s look a t the ab-get-ratings-min func tion:
(defun ab-get-ratings-min (tree player upper-limit lower-limit)
(labels ((f (moves upper-limit)
(unless (lazy-null moves)
(let ((x (ab-rate-position (cadr (lazy-car moves))
player
upper-limit
lower-limit)))
(if (<= x lower-limit)
(list x)
(cons x (f (lazy-cdr moves) (min x upper-limit))))))))
(f (caddr tree) upper-limit)))
T he ab-get-ratings-min func tion is ba sic a lly ide ntic a l to the ab-get-ratings-max func tion, e xc e pt the role s of the uppe r a nd
lowe r lim its a re flippe d. Ba se d on the re pe titive ne ss of the se two func tions, you c ould proba bly im a gine how the ab-get-ratings-
max a nd ab-get-ratings-min func tions c ould be c om bine d into a single func tion. As m e ntione d e a rlie r, with tha t a pproa c h, ra the r
tha n upper-limit a nd lower-limit, you would use the m ore ge ne ric te rm s alpha a nd beta, a s the se will diffe r ba se d on whe the r
the node is a MAX node or a MIN node .
Ne xt, we ne e d to twe a k rate-position, the func tion tha t ra te s a single -boa rd a rra nge m e nt:
(defun ab-rate-position (tree player upper-limit lower-limit)
(let ((moves (caddr tree)))
(if (not (lazy-null moves))

(if (eq (car tree) player)

(apply #'max (ab-get-ratings-max tree


player
upper-limit
lower-limit))

(apply #'min (ab-get-ratings-min tree


player
upper-limit
lower-limit)))
(score-board (cadr tree) player))))

In our ne w ab-rate-position, we c he c k if this node in the ga m e tre e is a m ove for us or a m ove for a n oppone nt . If it’s a

m ove for us, the n it’s a MAX node , a nd we wa nt to dispa tc h to ab-get-ratings-max . If it’s the oppone nt’s turn, we inste a d
dispa tc h to ab-get-ratings-min . Othe rwise , ab-rate-positon is the sa m e a s our pre vious rate-position func tion.
T o c om ple te our support for a lpha -be ta pruning, we ne e d to m odify one m ore func tion: the handle-computer func tion tha t kic ks
off our m inim a x c a lc ula tions:
(defun handle-computer (tree)

(let ((ratings (ab-get-ratings-max (limit-tree-depth tree *ai-level*)


(car tree)

most-positive-fixnum

most-negative-fixnum)))
(cadr (lazy-nth (position (apply #'max ratings) ratings) (caddr tree)))))

T his func tion sta rts off the m inim a x c a lc ula tion by c a lling ab-get-ratings-max , sinc e the first m ove m ost de finite ly
be longs to the ta rge t pla ye r a nd the re fore is a MAX node .
W he n we c a ll this func tion, we ’ll ne e d to pa ss in our sta rting upper-limit a nd lower-limit. Sinc e we ’re a t the ve ry be ginning
of our m inim a x se a rc hing, we ’ll wa nt to se t the se to be a s la rge a nd a s sm a ll a s possible . Ide a lly, we would wa nt the m to be
positiv e infinity a nd ne gativ e infinity . Although m a ny L isp e nvironm e nts c onta in support for suc h c onc e pts, the y a re not pa rt of the
ANSI Com m on L isp sta nda rd. Howe ve r, the sta nda rd doe s de fine most-positive-fixnum a nd most-negative-fixnum, whic h a re
ve ry la rge positive a nd ne ga tive num be rs, m a king the m pe rfe c tly suite d for our purpose s. He nc e , we pa ss the se into ab-get-

ratings-max to sta rt off our lim its .


If we wa nte d to sque e z e out a ta d m ore e ffic ie nc y from our AI e ngine , we c ould, inste a d, se t the upper-limit a nd lower-limit
to be the m a xim um a nd m inim um va lue s from our score-board func tion. T ha t would slightly im prove the a m ount of pruning tha t
is possible . Howe ve r, the score-board func tion m a y re turn a diffe re nt ra nge of sc ore s ba se d on the siz e of the boa rd. a nd it m ight
ha ve othe r de pe nde nc ie s if we de c ide to optim iz e boa rd sc oring e ve n m ore in the future . T he re fore , it is be st for the tim e be ing if
we se t our lim its to nigh infinity for the sta rt of our m inim a x c a lc ula tions so we don’t ne e d to worry a bout this.
As a fina l re wa rd for onc e a ga in im proving the pe rform a nc e of our AI, le t’s inc re a se the siz e of the boa rd to use a 5-by-5 ga m e
fie ld. W ith our ne w la z y, trim m e d, a nd prune d AI a lgorithm s, we should be a ble to ha ndle this la rge r boa rd without a swe a t:
(defparameter *board-size* 5)
(defparameter *board-hexnum* (* *board-size* *board-size*))

Note

Re m e m be r tha t we use d m e m oiz a tion for som e of our e a rlie r func tions. If you ha ve a lre a dy pla ye d som e ga m e s in this c ha pte r
on a 4-by-4 boa rd, one func tion in pa rtic ula r, the neighbors func tion, m a y re turn re sults ba se d on this old boa rd siz e . T his is only
a n issue if you’ve a lre a dy pla ye d a ga m e on the 4-by-4 boa rd without re sta rting your L isp in the inte rim . T o fix this, sim ply re run
the de finition of the neighbors func tion in dice_of_doom_v1.lisp from the RE PL (inc luding the m e m oiz e d re vision a t the bottom
of the file ) to c le a r a ny c a c he d re sults.

He re ’s wha t our ga m e looks like now:


> (play-vs-computer (game-tree (gen-board) 0 0 t))
current player = a
a-2 b-2 a-1 b-2 b-2
a-1 b-2 b-3 b-3 a-3
a-1 b-2 a-3 b-1 b-2
b-1 b-3 a-2 b-2 a-1
b-3 b-1 b-1 a-3 b-3
choose your move:
1. 9 -> 13
2. 9 -> 4
3. 9 -> 14
4. 12 -> 13
5. 17 -> 22
6. 23 -> 18
7. 23 -> 22
3
current player = a
a-2 b-2 a-1 b-2 b-2
a-1 b-2 b-3 b-3 a-1
a-1 b-2 a-3 b-1 a-2
b-1 b-3 a-2 b-2 a-1
b-3 b-1 b-1 a-3 b-3
choose your move:
1. end turn
2. 12 -> 13
3. 14 -> 13
4. 14 -> 15
5. 17 -> 22
6. 23 -> 18
7. 23 -> 22
1
current player = b
a-3 b-2 a-1 b-2 b-2
a-1 b-2 b-3 b-3 a-1
a-1 b-2 a-3 b-1 a-2
b-1 b-3 a-2 b-2 a-1
b-3 b-1 b-1 a-3 b-3
current player = b
a-3 b-1 a-1 b-2 b-2
b-1 b-2 b-3 b-3 a-1
a-1 b-2 a-3 b-1 a-2
b-1 b-3 a-2 b-2 a-1
b-3 b-1 b-1 a-3 b-3
current player = b
a-3 b-1 b-1 b-1 b-2
b-1 b-2 b-3 b-3 a-1
a-1 b-2 a-3 b-1 a-2
b-1 b-3 a-2 b-2 a-1
b-3 b-1 b-1 a-3 b-3
current player = b
a-3 b-1 b-1 b-1 b-1
b-1 b-2 b-3 b-3 b-1
a-1 b-2 a-3 b-1 a-2
b-1 b-3 a-2 b-2 a-1
b-3 b-1 b-1 a-3 b-3
current player = b
a-3 b-1 b-1 b-1 b-1
b-1 b-1 b-3 b-3 b-1
b-1 b-2 a-3 b-1 a-2
b-1 b-3 a-2 b-2 a-1
b-3 b-1 b-1 a-3 b-3
current player = b
a-3 b-1 b-1 b-1 b-1
b-1 b-1 b-3 b-3 b-1
b-1 b-2 a-3 b-1 a-2
b-1 b-1 b-2 b-2 a-1
b-3 b-1 b-1 a-3 b-3
current player = b
a-3 b-1 b-1 b-1 b-1
b-1 b-1 b-3 b-3 b-1
b-1 b-2 a-3 b-1 a-2
b-1 b-1 b-2 b-2 b-2
b-3 b-1 b-1 a-3 b-1
current player = a
a-3 b-2 b-2 b-2 b-2
b-2 b-2 b-3 b-3 b-1
b-1 b-2 a-3 b-1 a-2
b-1 b-1 b-2 b-2 b-2
b-3 b-1 b-1 a-3 b-1
choose your move:
1. 0 -> 4
2. 0 -> 1
3. 0 -> 5
4. 12 -> 13
5. 14 -> 10
6. 14 -> 9
7. 14 -> 13
8. 14 -> 15
9. 23 -> 18
10. 23 -> 17
11. 23 -> 22
12. 23 -> 24
At this point, our RE PL ga m e inte rfa c e is be c om ing re a lly im pra c tic a l for suc h a la rge ga m e fie ld. W e ’ll be a ddre ssing tha t
ne xt.
W hat You've Le ar ne d
In this c ha pte r, we m a de the c om pute r pla ye r for our Dic e of Doom ga m e m uc h m ore sophistic a te d. W e im ple m e nting the ga m e
tre e using la z y lists, a nd a pplie d se ve ra l optim iz a tion te c hnique s to lim it the num be r of boa rd positions tha t a re se a rc he d by the AI
e ngine . Along the wa y, you le a rne d the following:
Lazy programming a llows you to work with ve ry la rge (a nd e ve n infinite ) da ta struc ture s a nd do so e ffic ie ntly.
Onc e you ha ve a lazy m a c ro a nd a force func tion, you c a n use the m to build m ore sophistic a te d la z y ope ra tions, inc luding
building a la z y list libra ry.
He uristic s a re im pe rfe c t a lgorithm s tha t c a n be use d to im prove the pe rform a nc e of your c ode , with som e c re a tive thinking. In
our e xa m ple , we m a de som e he uristic c ha nge s to how we sc ore le a f node s.
Onc e we c onve rte d Dic e of Doom to use a la z y tre e , we we re a ble to e le ga ntly trim the ga m e tre e in orde r to lim it how de e p
the AI thinks whe n c onte m pla ting its m ove s.
Alpha -be ta pruning le ts us im prove pe rform a nc e e ve n m ore , by pruning bra nc he s tha t ha ve no wa y of im pa c ting the fina l sc ore s
on the m ove s be ing c onside re d by the AI.
Chapte r 19. Cr e ating a G r aphic al, W e b-Base d Ve r sion of Dic e of Doom
In the pre vious c ha pte r, we c re a te d a se c ond ve rsion of Dic e of Doom to pla y on la rge r ga m e boa rds. It ha s be c om e quite
diffic ult to unde rsta nd the boa rd a nd m a ke m ove s using our c rude c onsole inte rfa c e . Ce rta inly, Dic e of Doom would be infinite ly
be tte r if we ha d a pre tty gra phic a l ga m e boa rd tha t a llowe d us to sim ply c lic k whe re we wa nte d to m a ke our m ove s. W e ll, I ha ve
good ne ws for you . . .
In this c ha pte r, we ’ll put toge the r a lot of c ode from e a rlie r c ha pte rs to tra nsform Dic e of Doom into a full-fe a ture d, gra phic a l
ga m e you c a n pla y right inside a we b browse r!
Dr awing the G ame Boar d Using the SVG F or mat
W e ’ve a lre a dy writte n a prim itive we b se rve r in Cha pte r 13. Also, we ’ve c ove re d how to dra w SVG gra phic s with a DSL in
Cha pte r 17. L uc ky for us, the ne w HT ML 5 sta nda rd inc lude s fe a ture s tha t m a ke it possible to e m be d SVG pic ture s dire c tly inside a
sta nda rd HT ML doc um e nt. In this wa y, we ’ll be a ble to use our sim ple little we b se rve r to se rve up som e fully inte ra c tive ve c tor
gra phic s. You’ll be a m a z e d a t how e a sy it is to do this.

Note

At the tim e this book wa s writte n, the only we b browse r to support inline SVG within HT ML wa s Fire fox 3. 7 Alpha . Use this, or a
m ore re c e nt re le a se of Fire fox with our ne w ve rsion of Dic e of Doom . If you’re ha ving proble m s, try na viga ting to the a bout:c onfig
pa ge in the Fire fox a ddre ss ba r, a nd se t the htm l5. e na ble c onfigura tion se tting to true . T his will a llow Fire fox to use the la te st
HT ML 5 se ttings.
Also, re m e m be r tha t our we b se rve r libra ry is not pure ANSI Com m on L isp, a nd m a ke s use of som e CL ISP-spe c ific e xte nsions.
T his m e a ns it re quire s CL ISP to func tion.

First, we ’ll ne e d to pull in c ode from va rious othe r c ha pte rs to ge t re a dy. In the pre vious c ha pte r, we c re a te d ve rsion 2 of our
Dic e of Doom e ngine . Pla c e a ll the c ode from tha t c ha pte r in a file na m e d dic e _of_doom_v 2. lisp. You should a lso a lre a dy ha ve
c re a te d a file na m e d we bse rv e r. lisp from Cha pte r 13. (T he se file s a re a ll fre e ly a va ila ble from http://la ndoflisp. c om /. )
L e t’s loa d in the se file s:
> (load "dice_of_doom_v2.lisp")
> (load "webserver.lisp")
For our SVG support, we ’ll a lso ne e d the SVG-re nde ring c ode from Cha pte r 16 a nd Cha pte r 17. Pla c e those func tions in sv g. lisp.
(T his file is a lso a va ila ble from http://la ndoflisp. c om /. ) For re fe re nc e , the func tions we ’ll ne e d a re let1, split, pairs, print-tag,
tag, svg, brightness, svg-style, a nd polygon. L oa d this file ne xt:
> (load "svg.lisp")
Now le t’s write som e c ode tha t c a n dra w a pre tty ve rsion of our ga m e boa rd using SVG. First, we ’ll wa nt to de fine som e c onsta nts
tha t c ontrol the va rious dim e nsions ne e de d to dra w the boa rd:
(defparameter *board-width* 900)
(defparameter *board-height* 500)

(defparameter *board-scale* 64)

(defparameter *top-offset* 3)

(defparameter *dice-scale* 40)

(defparameter *dot-size* 0.05)


T he boa rd width a nd he ight will be 900-by-500 pixe ls, whic h is a good siz e for pla ying a ga m e in a browse r on m ost pe ople ’s

c om pute r sc re e ns. T he boa rd sc a le re pre se nts ha lf the width of a single he x on the sc re e n in pixe ls. T he *top-offset*

va ria ble te lls us we wa nt thre e e xtra he x he ights of fre e spa c e a bove the ba se of the boa rd. W e ’ll ne e d this be c a use a he x
with lot of dic e on it will ha ve its dic e stic king out, upwa rd, a nd we ne e d room for the se dic e to be visible on the sc re e n. T he

*dice-scale* va ria ble te lls us tha t a single die will be a bout 40 pixe ls ta ll a nd wide on the sc re e n. Fina lly, we se t *dot-

size* to 0.05, whic h te lls us tha t e a c h dot will be a bout 0. 05 tim e s the siz e of a die .
Dr awing a Die
Now we ’re re a dy to write a func tion tha t c a n dra w a die . Note tha t we won’t use bitm a ps or a nything like tha t to dra w. Inste a d,
we ’re dra wing a die “ the ha rd wa y, ” by re nde ring it dire c tly out of ra w SVG polygons. He re ’s the c ode :

(defun draw-die-svg (x y col)

(labels ((calc-pt (pt)


(cons (+ x (* *dice-scale* (car pt)))
(+ y (* *dice-scale* (cdr pt)))))

(f (pol col)
(polygon (mapcar #'calc-pt pol) col)))

(f '((0 . −1) (−0.6 . −0.75) (0 . −0.5) (0.6 . −0.75))


(brightness col 40))
(f '((0 . −0.5) (−0.6 . −0.75) (−0.6 . 0) (0 . 0.25))
col)
(f '((0 . −0.5) (0.6 . −0.75) (0.6 . 0) (0 . 0.25))
(brightness col −40))

(mapc (lambda (x y)
(polygon (mapcar (lambda (xx yy)
(calc-pt (cons (+ x (* xx *dot-size*))
(+ y (* yy *dot-size*)))))
'(−1 −1 1 1)
'(−1 1 1 −1))
'(255 255 255)))

'(−0.05 0.125 0.3 −0.3 −0.125 0.05 0.2 0.2 0.45 0.45 −0.45 −0.2)
'(−0.875 −0.80 −0.725 −0.775 −0.70 −0.625
−0.35 −0.05 −0.45 −0.15 −0.45 −0.05))))

T o dra w a die , we ne e d to pa ss in thre e a rgum e nts . T he first two a re the x a nd y position a t whic h the die should a ppe a r in
the SVG pic ture . T he third is the c olor we wa nt the die to be . T his func tion will ta ke som e libe rtie s with tha t c olor a nd m odify it
a s ne e de d to give the die a little sha ding.
Anything we dra w in this func tion will ne e d to be re nde re d in a sc a le d fa shion, ba se d on the *dice-scale* c onsta nt we de fine d.

T he re fore , we first de fine a loc a l func tion calc-pt tha t sc a le s a point for us . Sinc e we ’ll ne e d to dra w se ve ra l sc a le d
polygons, le t’s a lso c re a te a c onve nie nc e func tion, f, tha t runs calc-pt a ga inst a ll points in a polygon a nd the n dra ws it by c a lling

the polygon func tion .


A die in our pic ture will ha ve thre e visible fa c e s: the top fa c e , the front fa c e , a nd the right fa c e . W e dra w the se by c a lling our

func tion f thre e tim e s sta rting he re a nd using som e ha rd-c ode d c oordina te s for the thre e fa c e s.

T he la st thing we ne e d to do is dra w the little dots on the fa c e s of the die . W e do this by mapcing the c oordina te s for the

dots a ga inst a la m bda func tion tha t c a n re nde r a dot. T his la m bda func tion use s the *dot-size* va ria ble to sc a le down a
squa re -sha pe d polygon tha t re pre se nts e a c h dot on the die fa c e . W e c ould write m ore sophistic a te d c ode to dra w c irc ula r a nd/or
e lliptic a l dots, but the dots a re so sm a ll tha t squa re s look just fine .
L e t’s try dra wing a die a t x=50 a nd y=50 with a n RGB re d (255 0 0) c olor:
> (svg 100 100 (draw-die-svg 50 50 '(255 0 0)))
<svg xmlns="http://www.w3.org/2000/svg" xmlns
:xlink="http://www.w3.org/1999/xlink" height="100" width="100"><polygon
points="50,10 26.0,20.0 50,30.0 74.0,20.0 " style="fill:rgb(255,40,40);stroke:rgb
(155,0,0)"></polygon><polygon points="50,30.0 26.0,20.0 26.0,50 50,60.0
" style="fill:rgb(255,0,0);stroke:rgb(155,0,0)"></polygon><polygon points="50,
30.0 74.0,20.0 74.0,50 50,60.0 " style="fill:rgb(215,0,0);
stroke:rgb(115,0,0)"></polygon><polygon points="46.0,
13.0 46.0,17.0 50.0,17.0 50.0,13.0 " style="fill:rgb(255,255,255);stroke:rgb(155,155,
155)"></polygon><polygon points="53.0,16.0 53.0,20.0 57.0,20.0 57.0,16.0
" style="fill:rgb(255,255,255);stroke:rgb(155,155,155)"></polygon><polygon
points="60.0,18.999998 60.0,23.0 64.0,23.0 64.0,18.999998 "
style="fill:rgb(255,255,255);stroke:rgb(155,155,155)"></polygon><polygon
points="36.0,17.0 36.0,21.000002 40.0,21.000002 40.0,17.0 " style="fill:rgb(255,255,
255);stroke:rgb(155,155,155)"></polygon><polygon points="43.0,20.0 43.0,
24.0 47.0,24.0 47.0,20.0 " style="fill:rgb(255,255,255);stroke:rgb(155,155,155)"><
/polygon><polygon points="50.0,23.0 50.0,27.0 54.0,27.0 54.0,23.0 "
style="fill:rgb(255,255,255);
stroke:rgb(155,155,155)"></polygon><polygon points="56.0,34.0 56.0,38.0
60.0,38.0 60.0,34.0 " style="fill:rgb(255,255,255);stroke:rgb(155,155,155)"></polygon>
<polygon points="56.0,46.0 56.0,50.0 60.0,50.0 60.0,46.0 "
style="fill:rgb(255,255,255);stroke:rgb(155,155,155)">
</polygon><polygon points="66.0,30.0 66.0,34.0 70.0,34.0 70.0,30.0 " style=
"fill:rgb(255,255,255);stroke:rgb(155,155,155)"></polygon><polygon
points="66.0,42.0 66.0,46.0 70.0,46.0 70.0,42.0 " style="fill:rgb
(255,255,255);stroke:rgb(155,155,155)"></polygon><polygon points="30.0,30.0
30.0,34.0 34.0,34.0 34.0,30.0 " style="fill:rgb(255,255,255);stroke:rgb
(155,155,155)"></polygon><polygon points="40.0,46.0 40.0,50.0 44.0,
50.0 44.0,46.0 " style="fill:rgb(255,255,255);stroke:rgb(155,155,155)"
></polygon></svg>
If you wa nt to se e wha t the fina l die looks like , just sa ve this gobble dygook to a file na m e d die . sv g. T he n loa d the re sult in
Fire fox, whe re you should se e the following pic ture (shown a t a blown-up siz e ):
Dr awing a Tile
Ne xt, le t’s write the func tion to dra w a n e ntire he x tile , inc luding the ba se a nd the dic e on the tile :
(defun draw-tile-svg (x y pos hex xx yy col chosen-tile)

(loop for z below 2

do (polygon (mapcar (lambda (pt)


(cons (+ xx (* *board-scale* (car pt)))
(+ yy (* *board-scale*
(+ (cdr pt) (* (- 1 z) 0.1))))))

'((−1 . −0.2) (0 . −0.5) (1 . −0.2)


(1 . 0.2) (0 . 0.5) (−1 . 0.2)))

(if (eql pos chosen-tile)


(brightness col 100)
col)))

(loop for z below (second hex)

do (draw-die-svg (+ xx
(* *dice-scale*
0.3

(if (oddp (+ x y z))


−0.3
0.3)))
(- yy (* *dice-scale* z 0.8)) col)))
T his func tion ta ke s in a lot of pa ra m e te rs, be c a use a lot of inform a tion is e nc ode d in a single tile of the boa rd. You’ll le a rn the
pre c ise m e a ning of e a c h of the se pa ra m e te rs whe n we dra w the boa rd in the ne xt se c tion.
First, our draw-tile-svg func tion dra ws the ba se . T o give the ba se a m ild 3D look, we ’ll dra w it twic e , with one le ve l sta c ke d

on top of the othe r. He re is the loop tha t dra ws the two ba se s. W ithin tha t loop, we ne e d to dra w a he xa gona l polygon .

W e m a p a sc a ling func tion a c ross the c oordina te s so tha t the y a re sc a le d to our *board-scale* va ria ble . He re you c a n se e
the six points of a he xa gon in pe rspe c tive e nc ode d using de c im a l nota tion. T he c olor of the ba se will be brighte ne d slightly if it
ha s be e n c hose n by the pla ye r to pe rform a m ove . W e do this by inc re a sing the brightne ss of the tile whe n c re a ting our polygons

.
Afte r we ’ve finishe d dra wing the tile ba se , we ne e d to dra w the dic e tha t re side on the tile . W e do this by looping a c ross the

num be r of dic e a nd the n c a lling our draw-die-svg func tion . W he n c a lc ula ting the x a nd y positions of the dic e , we
ne e d to pe rform a bit of sc a ling m a th. T he m ost inte re sting pie c e of this m a th is tha t we shift the dic e a bit to the le ft or right,

de pe nding on whe the r the sum of the x-, y-, a nd z -c oordina te s for a give n die is odd or e ve n . T his m a ke s the sta c ks look a
little im pe rfe c t a nd will give the sta c ke d dic e for the c om ple te boa rd a ple a sing, na tura l a ppe a ra nc e .
Now le t’s c a ll our func tion to dra w a finishe d tile a nd se e how it looks. Aga in, just c opy the output from this c om m a nd to a file
na m e d som e thing like tile . sv g.
> (svg 300 300 (draw-tile-svg 0 0 0 '(0 3) 100 150 '(255 0 0) nil))
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/
1999/xlink" height="300" width="300"><polygon points="36,143.6 100,124.4
164,143.6 164,169.2 100,188.4 36,169.2 " style="fill:rgb
(255,0,0);stroke:rgb(155,0,0)">
...
He re ’s wha t you should se e whe n looking a t the file in Fire fox:
Dr awing the Boar d
Now we ’re re a dy to write a func tion tha t dra ws a n e ntire ga m e boa rd a s a n SVG. It will be ve ry sim ila r to our draw-board
func tion, whic h we ’ve be e n using to dra w the boa rd to the c onsole . It fulfills the sa m e role , but sim ply outputs the re sult a s SVG
da ta .
(defparameter *die-colors* '((255 63 63) (63 63 255)))

(defun draw-board-svg (board chosen-tile legal-tiles)

(loop for y below *board-size*

do (loop for x below *board-size*

for pos = (+ x (* *board-size* y))


for hex = (aref board pos)
for xx = (* *board-scale* (+ (* 2 x) (- *board-size* y)))
for yy = (* *board-scale* (+ (* y 0.7) *top-offset*))
for col = (brightness (nth (first hex) *die-colors*)
(* −15 (- *board-size* y)))

do (if (member pos legal-tiles)


(tag g ()

(tag a ("xlink:href" (make-game-link pos))

(draw-tile-svg x y pos hex xx yy col chosen-tile)))


(draw-tile-svg x y pos hex xx yy col chosen-tile)))))

(defun make-game-link (pos)


(format nil "/game.html?chosen=˜a" pos))
T he draw-board-svg func tion ta ke s the boa rd a s a n a rgum e nt, but a lso re quire s two othe r a rgum e nts tha t will be im porta nt for

using the pic ture a s the front e nd of the use r inte rfa c e for our ga m e . One a rgum e nt is chosen-tile, whic h indic a te s a tile
tha t the pla ye r ha s c lic ke d with the m ouse . W e ’re going to c olor tha t tile a bit lighte r, so the pla ye r c a n te ll tha t the c om pute r ha s
re c ogniz e d the se le c tion. Anothe r a rgum e nt is legal-tiles, whic h indic a te s whic h tile s the pla ye r c a n le ga lly c lic k ne xt.
It so ha ppe ns tha t SVG pic ture s ha ve a fe a ture for we b links, whic h works just like the <a href="..."> hype rlinks in re gula r
HT ML . If a tile is a le ga l tile for the pla ye r’s ne xt m ove , we ’ll wra p the SVG for tha t tile in suc h a link, m a king it c lic ka ble .
Ha ving the legal-tiles pa ra m e te r le ts us know whic h tile s we wa nt to be c lic ka ble .

T he draw-board-svg func tion c onsists of a c ouple of ne ste d loops tha t loop through the y a nd x c oordina te s of the
tile boa rd. For e a c h tile , we the n de fine a ton of loc a l va ria ble s (using the fa c ility for loc a l va ria ble s in the loop m a c ro introduc e d

in Cha pte r 10). First, we de c la re pos , whic h indic a te s the position of c urre nt tile in the he x a rra y. T he n we fe tc h tha t he x.
Ne xt, we c a lc ula te the pixe l c oordina te s for the tile s, in the va ria ble s xx a nd yy. As you c a n se e , the m a th for the se c oordina te s
ge ts a bit tric ky, sinc e the boa rd is dra wn in pe rspe c tive on the sc re e n.
T he fina l loc a l va ria ble we de fine is col, whic h will hold the c olor of the tile a nd dic e in the c urre nt spot. W e do this by using
a list of die c olors, whic h c urre ntly holds the c olors re d (for pla ye r A) a nd blue (for pla ye r B). W e a lso da rke n the c olor a bit ba se d
on the y-c oordina te using the brightness func tion (disc usse d in Cha pte r 17). T his da rke ns the rows in the ba c k a bit, a dding to the
3D a ppe a ra nc e of our SVG ga m e boa rd.

If the c urre nt tile is a m e m be r of the le ga l tile s , we ’re going to wra p it in a we b link, a s m e ntione d pre viously. In SVG,

this is done with a ta g in the form <a xlink:href="...">, whic h we c re a te he re . Notic e tha t we a lso wra p e a c h tile in a
<g> ta g, whic h te lls the SVG re nde re r to tre a t the polygons in this tile a s a group. T o figure out the a c tua l URL we wa nt to link to,
we c a ll the make-game-link func tion. T his func tion builds a n a ppropria te URL . You’ll unde rsta nd the form a t of the URL be tte r
onc e we sta rt writing the c ode tha t ha ndle s the we b se rve r for our ga m e .

Fina lly, we ’re re a dy to c a ll our draw-tile func tion . T he re a re two diffe re nt ve rsions of the c a ll in our c ode : one for the
hype rlinke d ve rsion a nd one for the nonlinke d ve rsion.
Phe w! Now we c a n fina lly dra w a full ga m e boa rd dyna m ic a lly, using the SVG form a t:
> (svg *board-width* *board-height* (draw-board-svg (gen-board) nil nil))
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/
1999/xlink" height="500" width="900"><polygon points="256,185.6 320,166.4
384,185.6 384,211.2 320,230.4 256,211.2 "
...
If you sa ve the output to board. sv g a nd loa d it in Fire fox, he re is wha t you should se e :
Building the W e b Se r ve r Inte r fac e
Now tha t we ’ve c om ple te d the gra phic a l side of Dic e of Doom ve rsion 3, we ’re re a dy to write the side tha t inte rfa c e s with the
we b se rve r.
W r iting O ur W e b Re que st H andle r
T he c e ntra l func tion for our we b se rve r ha ndling is c a lle d dod-request-handler. It is the func tion tha t we c a n pa ss to the serve
c om m a nd in our we b se rve r libra ry, a nd it is re sponsible for ha ndling a ll the we b re que sts c om ing from the we b browse r. He re is the
c ode for dod-request-handler:
(defparameter *cur-game-tree* nil)
(defparameter *from-tile* nil)
(defun dod-request-handler (path header params)

(if (equal path "game.html")

(progn (princ "<!doctype html>")

(tag center ()
(princ "Welcome to DICE OF DOOM!")
(tag br ())

(let ((chosen (assoc 'chosen params)))

(when (or (not *cur-game-tree*) (not chosen))


(setf chosen nil)

(web-initialize))
(cond ((lazy-null (caddr *cur-game-tree*))

(web-announce-winner (cadr *cur-game-tree*)))


((zerop (car *cur-game-tree*))

(web-handle-human
(when chosen
(read-from-string (cdr chosen)))))

(t (web-handle-computer))))
(tag br ())

(draw-dod-page *cur-game-tree* *from-tile*)))


(princ "Sorry... I don't know that page.")))

First, this func tion c he c ks whe the r the c urre nt pa ge be ing fe tc he d from the we b se rve r is game.html . T his is the pa ge

whe re our ga m e will re side on the we b se rve r. At the top of the pa ge , we spe c ify the doctype . W he n done in this wa y, it te lls
the we b browse r to e xpe c t a n HT ML 5-e nc ode d we b pa ge . T he n we put in som e sim ple HT ML to c e nte r the pa ge a nd print a

we lc om e m e ssa ge .
T he params pa sse d from the we b se rve r libra ry m a y c onta in a n im porta nt va lue na m e d chosen, whic h we fe tc h using this line

. If the re is no c hose n tile , or if the ga m e tre e is c urre ntly e m pty , it m e a ns the pla ye r m ust be sta rting a bra nd-ne w

ga m e . If tha t’s the c a se , we will c a ll a func tion na m e d web-initialize .


Ne xt, we ne e d to find out whe the r the ga m e ha s e nde d. W e c a n te ll this by c he c king if the list of m ove s is e m pty (whic h, a s you

m ight re m e m be r, is store d in the caddr loc a tion of the tre e ). In tha t c a se , we ’ll a nnounc e a winne r .
Following tha t, we ne e d to se e if the c urre nt pla ye r is pla ye r z e ro, whic h m e a ns the pla ye r is the hum a n pla ye r. In tha t c a se ,

we ’ll c a ll the func tion web-handle-human to build the re st of the HT ML da ta in the body of the pa ge . W e a lso use the read-
from-string func tion to pull the num be r of the c hose n tile from the chosen pa ra m e te r, if it e xists.

In a ll othe r c a se s, we know we ’re de a ling with a c om pute r pla ye r a nd ha nd ove r c ontrol to web-handle-computer to build
the re st of the HT ML .
L a stly, the dod-request-handler func tion ne e ds to c a ll the draw-dod-page func tion to dra w the ga m e boa rd, whic h we do he re

.
Limitations of O ur G ame W e b Se r ve r
T he lim ita tions of our ga m e we b se rve r a re quite signific a nt. First of a ll, for sim plic ity’s sa ke , the dod-request-handler
func tion m a ke s a bsolute ly no e ffort to try to de te rm ine from whom the we b re que st is c om ing. It be ha ve s a s if a ll ga m e
inte ra c tions we re c om ing from a single pla ye r, a nd the re fore isn’t a true m ultipla ye r se rve r for Dic e of Doom . If m ultiple pla ye rs
we re to try to pla y diffe re nt ga m e s a t the sa m e tim e , the dod-request-handler would ge t c onfuse d a nd bad things would ha ppe n.
It would not be too diffic ult to e xpa nd dod-request-handler into a true we b se rve r for m ultiple , pa ra lle l ga m e s. T o do this, we
would ne e d to pull se ssion inform a tion out of the he a de r da ta it re c e ive s a s a n a rgum e nt from the we b se rve r, a nd the n a ll va ria ble s
it re fe re nc e s (suc h a s *cur-game-tree*, for insta nc e ) would ne e d to live in a ha sh ta ble , using the se ssion inform a tion a s a ke y.
T his wa y, e a c h pla ye r would ha ve he r own ga m e tre e , a nd our e ngine c ould the n se rve m ultiple ga m e s in pa ra lle l. T he
im ple m e nta tion of suc h a m ultiga m e ve rsion of the dod-request-handler is “ a n e xe rc ise for the re a de r. ”
Anothe r lim ita tion of dod-request-handler is tha t it re a ds inform a tion from the URL using the read-from-string func tion. As
you’ve le a rne d in e a rlie r c ha pte rs, this func tion c a n be c om prom ise d to run a rbitra ry c ode in the ha nds of a n e xpe rie nc e d (a nd e vil)
L ispe r.
Initializ ing a Ne w G ame
He re is the web-initialize func tion, whic h initia liz e s our ga m e e ngine to sta rt a bra nd-ne w ga m e of Dic e of Doom :
(defun web-initialize ()
(setf *from-tile* nil)

(setf *cur-game-tree* (game-tree (gen-board) 0 0 t)))


As you c a n se e , it ge ne ra te s a ra ndom ga m e boa rd, builds a tre e from it, a nd the n store s the re sult in the globa l *cur-game-

tree* va ria ble .


Announc ing a W inne r
He re is the func tion tha t a nnounc e s the winne r within the we b browse r:
(defun web-announce-winner (board)
(fresh-line)
(let ((w (winners board)))
(if (> (length w) 1)
(format t "The game is a tie between ˜a" (mapcar #'player-letter w))
(format t "The winner is ˜a" (player-letter (car w)))))

(tag a (href "game.html")


(princ " play again")))
It is e xa c tly the sa m e a s our pre vious announce-winner func tion, e xc e pt tha t it now inc lude s som e e xtra c ode a t the e nd to build

a we b link , whic h will a llow us to c onve nie ntly sta rt a bra nd-ne w ga m e , sinc e the c urre nt ga m e ha s e nde d.
H andling the H uman P laye r
T he web-handle-human func tion is re sponsible for c re a ting the HT ML a nd doing the bookke e ping whe n the pla ye r ta king the
c urre nt turn is the hum a n pla ye r.
(defun web-handle-human (pos)

(cond ((not pos) (princ "Please choose a hex to move from:"))


((eq pos 'pass) (setf *cur-game-tree*
(cadr (lazy-car (caddr *cur-game-tree*))))

(princ "Your reinforcements have been placed.")


(tag a (href (make-game-link nil))
(princ "continue")))

((not *from-tile*) (setf *from-tile* pos)


(princ "Now choose a destination:"))

((eq pos *from-tile*) (setf *from-tile* nil)


(princ "Move cancelled."))

(t (setf *cur-game-tree*
(cadr (lazy-find-if (lambda (move)
(equal (car move)
(list *from-tile* pos)))
(caddr *cur-game-tree*))))
(setf *from-tile* nil)
(princ "You may now ")

(tag a (href (make-game-link 'pass))


(princ "pass"))
(princ " or make another move:"))))
T he re c e nt c hoic e s the hum a n ha s m a de dic ta te wha t this func tion will do. T he web-handle-human func tion knows the hum a n’s
c hoic e s by re fe re nc ing the m ost re c e ntly c hose n position, whic h de rive s from a va ria ble pa sse d a s a pa ra m e te r through the we b
re que st. It a lso c a n re fe re nc e the *from-tile* globa l va ria ble , whic h te lls it whic h tile the pla ye r initia lly c hose to use a s a
sta rting loc a tion for a m ove . It ne e ds both of the se va lue s, sinc e a m ove ha s both a sourc e loc a tion a nd a de stina tion loc a tion.

If the pla ye r ha s not ye t c hose n a loc a tion, we wa nt to print a m e ssa ge re que sting tha t the pla ye r c hoose a he x . If the

pla ye r c hose to pa ss, we wa nt to print a m e ssa ge sa ying tha t pla ye r’s re inforc e m e nts ha ve be e n pla c e d . (Re m e m be r tha t
re inforc e m e nts a re pla c e d right a fte r som e one pa sse s. )
Ne xt, we c he c k if the *from-tile* va ria ble is nil. If this is the c a se , it m e a ns the pla ye r ha s not ye t c hose n a sta rting loc a tion

for a dic e a tta c k. If it’s nil, we c a n se t *from-tile* e qua l to the loc a tion tha t wa s just se le c te d , a s we ll a s a sk the pla ye r
to se le c t a de stina tion.
If the c urre ntly se le c te d loc a tion is the sa m e a s the *from-tile* va ria ble , it m e a ns a tile wa s se le c te d twic e . T his m ust m e a n
the pla ye r ha s c ha nge d his m ind a nd wa nts to undo his se le c tion. T he re fore , we will se t *from-tile* to nil a nd print a

c a nc e lla tion m e ssa ge .


In a ll othe r c a se s, it m e a ns the pla ye r ha s se le c te d two va lid loc a tions for the sta rt a nd e nd of a n a tta c k. W e c a n now a dva nc e

t h e *cur-game-tree* to point to the a ppropria te ne xt tre e inside the la z y list of a va ila ble m ove s . W e wa nt to print a

m e ssa ge , a llowing the pla ye r to pa ss or m a ke ye t a nothe r a tta c k.


W e ha ve now c om ple te d the c ode our ga m e se rve r will use to inte ra c t with the hum a n pla ye r. Ne xt, le t’s write a func tion to
ha ndle the c om pute r pla ye r.
H andling the Compute r P laye r
Ha ndling the we b inte rfa c e for our c om pute r pla ye r is pre tty sim ple . Afte r a ll, c om pute r pla ye rs don’t ne e d a ny fa nc y use r
inte rfa c e stuff to know wha t’s going on in the ga m e . All the we b stuff tha t ha ppe ns whe n the c om pute r is m a king m ove s is the re
sole ly for the be ne fit of the hum a n pla ye r. He re is the web-handle-computer c ode tha t re nde rs the HT ML in the we b inte rfa c e a s
the AI pla ye r m a ke s a m ove :
(defun web-handle-computer ()

(setf *cur-game-tree* (handle-computer *cur-game-tree*))

(princ "The computer has moved. ")

(tag script ()
(princ
"window.setTimeout('window.location=\"game.html?chosen=NIL\"',5000)")))
All this func tion doe s is c a ll our pre vious handle-computer func tion, whic h will re turn the ne xt bra nc h tha t the c om pute r ha s

se le c te d in the ga m e tre e . W e use this to upda te our *cur-game-tree* va ria ble . Ne xt, we print a m e ssa ge to sta te tha t the

pla ye r ha s m ove d . T he la st pa rt of the func tion is a c le ve r little gim m ic k to spic e up our we b inte rfa c e a bit. It puts a

sm idge n of Ja va Sc ript in the HT ML of the we b pa ge , whic h forc e s the we b browse r to a utom a tic a lly loa d a ne w we b pa ge in
five se c onds. T his m e a ns tha t a s the c om pute r AI pla ye r m a ke s its m ove s, we ge t to se e e ve rything ha ppe n in a c rude a nim a tion!
Dr awing the SVG G ame Boar d fr om W ithin the H TM L
W e ha ve only one m ore func tion to write to c om ple te ve rsion 3 of Dic e of Doom : the draw-dod-page func tion. T his func tion
inte rfa c e s our pa ge ga m e se rve r c ode with the SVG c ode tha t dra ws our boa rd.
(defun draw-dod-page (tree selected-tile)
(svg *board-width*
*board-height*
(draw-board-svg (cadr tree)
selected-tile

(take-all (if selected-tile


(lazy-mapcar

(lambda (move)
(when (eql (caar move)

selected-tile)

(cadar move)))
(caddr tree))

(lazy-mapcar #'caar (caddr tree)))))))


T he m ost c om plic a te d pa rt of this func tion is the c ode tha t de te rm ine s whic h tile s on the boa rd a re le ga l tile s for the pla ye r to

c lic k . If the pla ye r ha s a lre a dy se le c te d a tile , we wa nt to find a ll m ove s whe re the sta rting position for the m ove

m a tc he s the se le c te d tile a nd re turn the de stina tion position for the give n m ove . If the pla ye r ha sn’t se le c te d a tile

ye t, we just wa nt to re turn a ll the le ga l sta rting positions .


W e ha ve now c om ple te d our fully gra phic a l ve rsion of Dic e of Doom . L e t’s pla y!
P laying Ve r sion 3 of Dic e of Doom
First, we ne e d to sta rt up our we b se rve r. Sim ply se rve up our dod-request-handler, a nd we ’re re a dy to go:
> (serve #'dod-request-handler)
Now m ove ove r to Fire fox a nd go to http://loc a lhost:8080/ga m e . htm l. You should se e our ga m e in your browse r:

W he n you c lic k a tile , it is highlighte d:

Now you c a n se le c t a tile to a tta c k. In this e xa m ple , we ’ll c hoose the sta c k of two dic e to the right of the se le c te d sta c k:
Ne xt, le t’s pa ss our turn by c lic king the pass we b link. T his will c a use the re inforc e m e nt dic e to be pla c e d (in this c a se , only a
single a dditiona l die in the uppe r-le ft c orne r):

If you now hit continue, you will se e the ga m e c yc le a utom a tic a lly through the m ove s for the c om pute r pla ye r, in a sim ila r
fa shion. It will ke e p going on like this until the re is a winne r for the ga m e . You c a n a lwa ys sta rt a ne w ga m e by just going ba c k to
the origina l game . html URL .
T his is m uc h nic e r tha n the c rude c onsole inte rfa c e we ’ve be e n using so fa r! But the re a re still a fe w, fina l im prove m e nts we ’re
going to m a ke to pe p up Dic e of Doom . W e ’ll be c ove ring those in the ne xt (a nd fina l c ha pte r) of this book.
W hat You've Le ar ne d
In this c ha pte r, we disc usse d how you c a n ge ne ra te inte ra c tive gra phic s in a we b browse r from a L isp progra m . Along the wa y,
you le a rne d the following:
You c a n c re a te a gra phic a l ve rsion of Dic e Of Doom by re nde ring the boa rd using the SVG form a t.
T he HT ML 5 sta nda rd supports inline SVG im a ge s. You c a n use this to c re a te a n inte ra c tive , we b-ba se d ve rsion of your ga m e .
T he sim ple we b se rve r use d for our e xa m ple ha s se ve ra l lim ita tions. For e xa m ple , our ga m e c a nnot be pla ye d by m ultiple
pla ye rs. Howe ve r, the re que st ha ndle r c ould be e xpa nde d to a llow for m ultiple , pa ra lle l ga m e s.
Chapte r 20. M aking Dic e of Doom M or e F un
It’s now tim e to c re a te a fina l ve rsion of Dic e of Doom . Ve rsion 4 of our ga m e will be m uc h m ore fun to pla y tha n our e a rlie r
ve rsions.
Although you proba bly we re not a wa re of it, we m a de som e m a jor c om prom ise s in the rule s for our ga m e to m a ke it e a sie r to
progra m . In this c ha pte r, we will a llow m ore pla ye rs, a dd rolling of the dic e , a nd im ple m e nt a fe w m ore c ha nge s to m a ke Dic e of
Doom a m uc h m ore inte re sting ga m e .
Inc r e asing the Numbe r of P laye r s
T o be gin, put a ll the c ode from the pre vious c ha pte r in a file na m e d dic e _of_doom_v 3. lisp (a lso a va ila ble from the c om pa nion
we bsite ), a nd the n e xe c ute the following c om m a nd:
> (load "dice_of_doom_v3.lisp")
T he first c ha nge we ’re going to m a ke is to inc re a se the num be r of pla ye rs from two to four. T hre e of the se will be AI oppone nts,
pla ye d by the c om pute r. Be c a use of how we ’ve writte n our c ode so fa r, this re quire s ve ry little e xtra c ode :
(defparameter *num-players* 4)
(defparameter *die-colors* '((255 63 63) (63 63 255) (63 255 63)
(255 63 255)))
First, we sim ply c ha nge our *num-players* va ria ble to 4. T he n we ne e d to indic a te a dditiona l die c olors for our ne w pla ye rs.
T he c olors for the four pla ye rs will be re d, blue , gre e n, a nd purple .
It turns out tha t the AI we ’ve c re a te d so fa r a lre a dy works just fine in a four-pla ye r ga m e .
Our AI ga m e e ngine will use wha t is c a lle d a “ pa ra noid stra te gy. ” T his m e a ns tha t the AI pla ye rs will a lwa ys a ssum e tha t e ve ry
othe r pla ye r (inc luding the hum a n) ha s no othe r goa l but to— how should I put this? — sc re w the m ove r pe rsona lly. T his isn’t a ba d
stra te gy to use ; howe ve r, a ga m e with m ore tha n two pla ye rs ope ns up ne w possibilitie s. For insta nc e , losing pla ye rs c ould ga ng up
on a winning pla ye r to im prove the ir odds. Our ga m e AI isn’t sm a rt e nough to form suc h pa c ks of c oope ra tion, but it’s good e nough.
Now tha t we ’ve a lre a dy twe a ke d som e c onsta nts to inc re a se the num be r of pla ye rs, le t’s twe a k a c ouple m ore :
(defparameter *max-dice* 5)
(defparameter *ai-level* 2)
He re , we ’re inc re a sing the m a xim um num be r of dic e on a he x tile from thre e to five , a nd de c re a sing the le ve l of our AI from
four to two. W ith the ne w rule s de sc ribe d in this c ha pte r, we ’ll ne e d to dum b down our AI a bit to m a ke sure it sta ys z ippy. Sinc e
the re a re now four c om pe ting pla ye rs, the AI a c tua lly doe sn’t ne e d to be so sm a rt to c ha lle nge the hum a n oppone nt.
Rolling the Dic e
I’m sure you’ve proba bly notic e d one obvious fla w in our ga m e so fa r: De spite the fa c t tha t it is c a lle d Dic e of Doom , it a c tua lly
is c om ple te ly de void of a ny ra ndom ne ss! T he dic e a re ne ve r rolle d, a nd the la rge r sta c k will a lwa ys a utom a tic a lly win, whic h
m a ke s for a pre tty la m e dic e ga m e . Now we ’re fina lly going to re c tify this fla w.
In this ve rsion of the ga m e , during a n a tta c k, both pile s of dic e a re rolle d, a nd whoe ve r rolls the highe st num be r wins the ba ttle .
T ie s a re a vic tory for the de fe nde r. If the a tta c ke r lose s, tha t pla ye r m ust surre nde r a ll dic e from the a tta c king he x e xc e pt one .
In the lingo of AI progra m m ing, this m e a ns we will a dd c hanc e node s to our ga m e tre e . T he wa y we ’re going to im ple m e nt this
is pre tty sim ple .
Building Chanc e Node s
E ve ry m ove in our la z y list of m ove s up to now ha s a lwa ys ha d e xa c tly two ite m s in it: a de sc ription of the m ove (a list of the
sourc e a nd de stina tion of the a tta c k, or nil for a pa ssing m ove ) a nd the ne w node of the ga m e tre e for whe n the m ove ha s be e n
ta ke n. Now we ’re sim ply going to a dd a third ite m to a m ove , whic h c onta ins the ga m e tre e for a n unsuc c e ssful a tta c k. T his m e a ns
tha t e a c h m ove in our m ove list will double a s a c ha nc e node , with two possible follow-up node s for the ne xt ga m e tre e , de pe nding
on whe the r a n a tta c k is suc c e ssful.
L e t’s upda te our attacking-moves func tion to a dd this e xtra ite m to the m ove so tha t e a c h m ove a c ts a s a c ha nc e node .
(defun attacking-moves (board cur-player spare-dice)
(labels ((player (pos)
(car (aref board pos)))
(dice (pos)
(cadr (aref board pos))))
(lazy-mapcan (lambda (src)
(if (eq (player src) cur-player)
(lazy-mapcan
(lambda (dst)
(if (and (not (eq (player dst) cur-player))
(> (dice src) 1))
(make-lazy (list (list (list src dst)
(game-tree (board-attack board cur-player src dst (dice src))
cur-player
(+ spare-dice (dice dst))
nil)

(game-tree (board-attack-fail board cur-player src dst (dice src))


cur-player
(+ spare-dice (dice dst))
nil))))
(lazy-nil)))
(make-lazy (neighbors src)))
(lazy-nil)))
(make-lazy (loop for n below *board-hexnum*
collect n)))))

T he only thing ne w in this upda te d ve rsion of attacking-moves is right he re , whe re we a dd a third ite m a s we c re a te a
ne w m ove in the ga m e tre e . T he boa rd in this a lte rna te bra nc h of our c ha nc e node is c onstruc te d by c a lling the func tion board-
attack-fail, whic h we will write ne xt.
T he board-attack-fail func tion doe s e xa c tly wha t you would e xpe c t: It ta ke s a boa rd a nd re turns a boa rd tha t ha s a ll dic e but
one re m ove d from the he x from whic h a fa ile d a tta c k origina te d.
(defun board-attack-fail (board player src dst dice)
(board-array (loop for pos from 0
for hex across board
collect (if (eq pos src)

(list player 1)

hex))))

He re , we sim ply loop ove r the boa rd a nd re turn e a c h he x unm odifie d , unle ss it ha ppe ns to be the sourc e he x for the a tta c k.

In tha t c a se , we re m ove a ll dic e from tha t he x but one .


Doing the Ac tual Dic e Rolling
Ne xt, we ne e d to write som e func tions to a c tua lly roll the dic e . He re is a func tion tha t rolls a pile of dic e :
(defun roll-dice (dice-num)

(let ((total (loop repeat dice-num


sum (1+ (random 6)))))
(fresh-line)

(format t "On ˜a dice rolled ˜a. " dice-num total)

total))
First, it c a lc ula te s a tota l c ount of a pile of rolle d dic e by looping onc e for e a c h die . For e a c h die , it ge ne ra te s a ra ndom

num be r from 1 to 6. T he n it store s the tota l sum in the total va ria ble . Ne xt, the roll-dice func tion prints a de sc riptive

m e ssa ge a bout the roll . Fina lly, it re turns the tota l .


Sinc e we ’re ne ve r going to roll a pile of dic e in isola tion, le t’s c re a te a nothe r func tion tha t pits two pile s of dic e a ga inst e a c h
othe r:
(defun roll-against (src-dice dst-dice)
(> (roll-dice src-dice) (roll-dice dst-dice)))
T his sim ply c a lls roll-dice twic e a nd c om pa re s the tota l of the two rolls. W e ’ll wa nt to use this func tion a s we tra ve l a long our
ga m e tre e to pic k e ithe r the winning or losing m ove a s a turn is c hose n by e ithe r the hum a n or the c om pute r.
Calling the Dic e Rolling Code fr om O ur G ame Engine
In the c onte xt of our ga m e e ngine , rolling dic e sim ply m e a ns pic king e ithe r the winning or losing bra nc h of the c ha nc e node
a fte r the hum a n or c om pute r ha s c hose n a m ove . T his a c tion is pe rform e d by the pick-chance-branch func tion:

(defun pick-chance-branch (board move)


(labels ((dice (pos)
(cadr (aref board pos))))
(let ((path (car move)))

(if (or (null path) (roll-against (dice (car path))


(dice (cadr path))))

(cadr move)

(caddr move)))))

T his func tion ta ke s the c urre nt boa rd a nd a lso the m ove tha t c onta ins the c ha nc e node tha t ne e ds to be re solve d . W he n the
pa th inside the m ove is not null, we c a ll roll-against with a c ount of dic e in the sourc e a nd de stina tion he xe s a long the pa th of

a tta c k . W e c he c k for a null pa th be c a use tha t m e a ns the m ove wa s a “ pa ss, ” whic h doe sn’t re quire a ny dic e rolling.

If the dic e roll for the a tta c k is suc c e ssful, we re m ove the first c hild tre e from the c ha nc e node within the m ove . If the

a tta c k is unsuc c e ssful, we re turn the se c ond c hild of the c ha nc e node .


Now we ne e d to m a ke sure tha t the pick-chance-branch func tion is c a lle d whe n the hum a n or c om pute r c hoose s a m ove . First,
le t’s ta ke c a re of the hum a n:
(defun handle-human (tree)
(fresh-line)
(princ "choose your move:")
(let ((moves (caddr tree)))
(labels ((print-moves (moves n)
(unless (lazy-null moves)
(let* ((move (lazy-car moves))
(action (car move)))
(fresh-line)
(format t "˜a. " n)
(if action
(format t "˜a -> ˜a" (car action) (cadr action))
(princ "end turn")))
(print-moves (lazy-cdr moves) (1+ n)))))
(print-moves moves 1))
(fresh-line)

(pick-chance-branch (cadr tree) (lazy-nth (1- (read)) moves))))


All we ’ve done he re is to a dd a c a ll to pick-chance-branch a t the e nd of our pre vious handle-human func tion, a t the point we

ne e d to re turn the c hild bra nc h of the ga m e tre e tha t holds the ne xt sta te of the ga m e .
W e upda te the handle-computer func tion in the sa m e wa y:
(defun handle-computer (tree)
(let ((ratings (get-ratings (limit-tree-depth tree *ai-level*) (car tree))))
(pick-chance-branch
(cadr tree)

(lazy-nth (position (apply #'max ratings) ratings) (caddr tree)))))

Aga in, we ’ve sim ply a dde d a c a ll to pick-chance-branch a t the e nd of the func tion .
It is now possible to pla y our upda te d Dic e of Doom ga m e . Howe ve r, a t this point, the c om pute r pla ye r will pla y a ve ry poor
ga m e , be c a use the AI doe s not ye t unde rsta nd tha t the c ha nc e node s e xist. It will sim ply a ssum e tha t e ve ry a tta c k will a lwa ys be
suc c e ssful, m a king it m uc h too foolha rdy to pla y a de c e nt ga m e . W e ne e d to im prove our AI so tha t it ta ke s into a c c ount the
rolling of the dic e a s it m a ke s its de c isions.
Updating the AI
For the AI to be a ble to de a l with the dic e rolls tha t a re now im porta nt to our ga m e , it m ust know a little som e thing a bout the
sta tistic s of dic e rolls. T he following ta ble give s it the ne e de d sta tistic a l inform a tion:
(defparameter *dice-odds* #(#(0.84 0.97 1.0 1.0)
#(0.44 0.78 0.94 0.99)
#(0.15 0.45 0.74 0.91)
#(0.04 0.19 0.46 0.72)
#(0.01 0.06 0.22 0.46)))
T his ta ble c onta ins the odds of winning for e a c h possible pa iring of dic e in our ga m e . T he c olum ns re pre se nt the a tta c king dic e ,
sta rting with one die . T he rows re pre se nt the de stina tion dic e , sta rting with two dic e (the m inim um dic e ne e de d for a n a tta c k).
T his ta ble te lls us, for insta nc e , tha t a roll of two a tta c king dic e a ga inst one de fe nding die ha s a n 84 pe rc e nt c ha nc e of winning.
Four a tta c king dic e a ga inst thre e de fe nding dic e ha ve a 74 pe rc e nt c ha nc e of winning.
If you re m e m be r, the c ore func tion in our AI c ode is the get-ratings func tion, whic h give s a point sc ore to the list of possible
follow-up m ove s. W e ne e d to m odify how it c a lc ula te s the sc ore of e a c h possible m ove to ta ke the odds of suc c e ss of the dic e roll
into a c c ount. W e a re now going to m a ke use of our *dice-odds* ta ble , a s we ll a s the point sc ore s of the suc c e ssful or fa ile d
outc om e s of e a c h a tta c k, to inte rpola te a c om bine d sc ore for e a c h a va ila ble m ove :
(defun get-ratings (tree player)
(let ((board (cadr tree)))
(labels ((dice (pos)
(cadr (aref board pos))))
(take-all (lazy-mapcar
(lambda (move)
(let ((path (car move)))
(if path
(let* ((src (car path))
(dst (cadr path))

(odds (aref (aref *dice-odds*


(1- (dice dst)))
(- (dice src) 2))))

(+ (* odds (rate-position (cadr move) player))

(* (- 1 odds) (rate-position (caddr move)


player))))
(rate-position (cadr move) player))))
(caddr tree))))))

In our upda te d get-ratings func tion, we look up the odds of e a c h a tta c k suc c e e ding from our ta ble . T he n we m ultiply the

odds with the ra ting for the winning c hild tre e . Additiona lly, we a dd in the odds of losing the a tta c k (one m inus the odds of

winning) m ultiplie d by the ra ting for the losing boa rd position . W e now ha ve a n upda te d get-ratings func tion tha t
unde rsta nds c ha nc e node s a nd a c c ounts for the m a ppropria te ly whe n ge ne ra ting the sc ore for a m ove .
For our ga m e AI to be fully c om pa tible with c ha nc e node s, we ne e d to m a ke one a dditiona l sm a ll c ha nge . Our tre e -trim m ing
func tion ne e ds to know a bout the two bra nc he s of the c ha nc e node within e a c h m ove , so it c a n prope rly trim both the winning a nd
losing a lte rna tive s for e a c h m ove :
(defun limit-tree-depth (tree depth)
(list (car tree)
(cadr tree)
(if (zerop depth)
(lazy-nil)
(lazy-mapcar (lambda (move)
(cons (car move)

(mapcar (lambda (x)


(limit-tree-depth x (1-depth)))
(cdr move))))
(caddr tree)))))

W e mapcar a c ross the ta il of e a c h m ove , so trim m ing is pe rform e d on both bra nc he s of a ny c ha nc e node s.

Note

Ve rsion 4 of Dic e of Doom will not ha ve a lpha -be ta pruning. Pe rform ing prope r a lpha -be ta pruning in the pre se nc e of c ha nc e
node s is ve ry c om ple x.
Impr oving the Dic e of Doom Re infor c e me nt Rule s
Until now, the num be r of re inforc e m e nts a t the e nd of a pla ye r’s turn a lwa ys e qua ls the num be r of c a pture d oppone nt dic e , m inus
one . T his re inforc e m e nt rule gua ra nte e d tha t the tota l num be r of dic e in a ga m e a lwa ys de c re a se s, so tha t the ga m e wa s c e rta in to
e ve ntua lly te rm ina te , a nd the ga m e tre e wa s a lwa ys finite in siz e .
Howe ve r, sinc e ve rsion 2, our ga m e tre e ha s be e n a la z y tre e , so it is pe rfe c tly fine if the tre e is infinite . Re m e m be r tha t one of
the m a in be ne fits of la z y e va lua tion is tha t you c a n ha ve da ta struc ture s tha t a re infinite in siz e .
T he re fore , we a re now going to a djust our re inforc e m e nt rule s to m a ke our ga m e stra te gic a lly m ore inte re sting.
Ac c ording to our ne w rule s, the num be r of re inforc e m e nt dic e will e qua l the num be r of tile s in the pla ye r’s la rge st c ontiguous
te rritory. T his a dds a lot of stra te gic de pth, be c a use the pla ye rs m ust c onsta ntly de c ide whe the r to risk c onne c ting the ir te rritorie s,
or pe rha ps e ve n to sa c rific e sm a lle r, nonvia ble te rritorie s by se nding the m on suic ide m issions.
In orde r to im ple m e nt this ne w re inforc e m e nt rule , le t’s first de fine the func tion get-connected, whic h re turns a list of tile s tha t
a re owne d by the c urre nt pla ye r a nd a re c onne c te d a s a c luste r of ne ighbors to the ta rge t tile :
(defun get-connected (board player pos)

(labels ((check-pos (pos visited)


(if (and (eq (car (aref board pos)) player)
(not (member pos visited)))
(check-neighbors (neighbors pos) (cons pos visited))
visited))

(check-neighbors (lst visited)


(if lst
(check-neighbors (cdr lst) (check-pos (car lst) visited))
visited)))

(check-pos pos '())))


T his func tion use s the sa m e a lgorithm for finding c onne c te d tile s a s we use d for c a lc ula ting c onne c te dne ss in our Gra nd T he ft
W um pus ga m e in Cha pte r 8. W e tra ve rse through the he xe s a nd the ir ne ighbors re c ursive ly, while m a inta ining a visited list.

T he get-connected func tion a c c om plishe s this by de fining two re c ursive loc a l func tions. T he check-pos func tion c he c ks a

single position a nd a ppe nds a ny ne w ne ighbors a c c e ssible from tha t loc a tion to the visite d list. T he check-neighbors func tion
c he c ks a n e ntire list of ne ighbors, sim ila rly a ppe nding ne w ne ighbors to the visite d list. T he se two func tions c a ll e a c h othe r
re c ursive ly until a ll ne ighbors in a c luste r a re found. T o sta rt off this re c ursive c a lc ula tion, we c a ll the check-pos func tion with

the ta rge t position a nd a n initia lly e m pty visited list .


W e c a n now find c luste rs. Howe ve r, to find the large st c luste r, we ne e d the largest-cluster-size func tion:
(defun largest-cluster-size (board player)

(labels ((f (pos visited best)

(if (< pos *board-hexnum*)


(if (and (eq (car (aref board pos)) player)

(not (member pos visited)))

(let* ((cluster (get-connected board player pos))


(size (length cluster)))

(if (> size best)

(f (1+ pos) (append cluster visited) size)

(f (1+ pos) (append cluster visited) best)))


(f (1+ pos) visited best))
best)))
(f 0 '() 0)))
T his func tion de fine s a loc a l func tion f, whic h we ’ll use to c he c k e ve ry position on the boa rd, while m a inta ining both a list of

pre viously visite d node s a nd the siz e of the la rge st, be st c luste r found so fa r .

As long a s the c urre nt position num be r is le ss tha n the tota l num be r of spots on the boa rd , we c ontinue to c he c k tile s. If the

c urre nt tile to be c he c ke d be longs to the pla ye r a nd a lso ha s not ye t be e n visite d , we ’ll c a ll get-connected to re trie ve the

c luste r of he xe s re a c ha ble from this spot . T he n, if the siz e of the c luste r is la rge r tha n the be st found so fa r , we m a ke

this the ne w be st siz e in our re c ursive c a ll . Othe rwise , we proc e e d by c a lling f while ke e ping the pre vious be st siz e .
(T he be st va ria ble a t this point will hold the be st va lue found so fa r from pre vious ite ra tions. ) No m a tte r wha t ha ppe ns, howe ve r,
the pos va ria ble is inc re m e nte d with e ve ry re c ursive c a ll to f, so tha t we e ve ntua lly c ove r the whole boa rd.
Fina lly, we ne e d to upda te add-new-dice to m a ke use of our ne w rule for c hoosing the num be r of re inforc e m e nts:

(defun add-new-dice (board player spare-dice)


(labels ((f (lst n)
(cond ((zerop n) lst)
((null lst) nil)
(t (let ((cur-player (caar lst))
(cur-dice (cadar lst)))
(if (and (eq cur-player player) (< cur-dice *max-dice*))
(cons (list cur-player (1+ cur-dice))
(f (cdr lst) (1-n)))
(cons (car lst) (f (cdr lst) n))))))))
(board-array (f (coerce board 'list)

(largest-cluster-size board player)))))

As you c a n se e , the add-new-dice func tion still re c e ive s spare-dice a s a n a rgum e nt for c om pa tibility with our old c ode ,
but now this a rgum e nt is sim ply ignore d. Inste a d, the num be r of re inforc e m e nts a dde d to the boa rd de pe nds on the siz e of the

la rge st c luste r . Othe rwise , the add-new-dice is ide ntic a l to our pre vious ve rsion.
T his is a ll the c ode we ne e d to e na ble the ne w re inforc e m e nt rule s. Note tha t, due to the de sign of our c ode , the AI pla ye r ha s
full a c c e ss to the ga m e tre e . Sinc e the ga m e tre e now c onta ins a ll of this ne w re inforc e m e nt da ta , the AI will a utom a tic a lly a da pt
its pla ying stra te gy to ta ke into a c c ount the ne w re inforc e m e nt rule s!
Conc lusion
W e ’ve gone through quite a long trip a s we ’ve c re a te d the Dic e of Doom ga m e , e m ploying a n im m e nse num be r of diffe re nt
progra m m ing te c hnique s a long the wa y. W e ’ve ta ke n e ve n m ore trips with a ll the othe r ga m e s in this book. T ha nks for ta king this
journe y with m e through the world of L isp progra m m ing!
I sugge st tha t you ta ke a m om e nt to e njoy the fruits of your la bor a nd pla y a fe w ga m e s of the fourth a nd fina l ve rsion of Dic e of
Doom . Aga in, a ll you ne e d to do is se rve up the Dic e of Doom re que st ha ndle r through our we b se rve r:
> (serve #'dod-request-handler)
Now you c a n pla y Dic e of Doom in Fire fox (a ga in, a t the a ddre ss loc alhost:8080/game . html) a s it is m e a nt to be pla ye d, with four
pla ye rs a nd a ll the ne w rule s we ’ve a dde d in this c ha pte r.

Good luc k with a ll your Dic e of Doom ba ttle s a nd a ll your future L isp progra m m ing!
Appe ndix A. Epilogue
Now tha t you’ve worke d your wa y through this book, he re is one fina l re wa rd: A story a bout the te c hnologie s be hind the e ntire
L isp fa m ily of progra m m ing la ngua ge s, se t in the not-too-dista nt future . . .
F unc tional G uild Cr uise r
Lisp Diale c t
Com m on L isp
Synopsis
Func tiona l progra m m ing is a m a the m a tic a l a pproa c h to progra m m ing tha t wa s pione e re d by the c re a tors of L isp. Func tiona l
progra m m ing pla c e s c e rta in re stric tions on the progra m m e r, but it c a n le a d to ve ry e le ga nt c ode . W he n using func tiona l
progra m m ing, e ve ry va ria ble tha t is use d by a give n func tion m ust be one of the following:

A pa ra m e te r pa sse d into tha t func tion


A loc a l va ria ble c re a te d within tha t func tion
A c onsta nt

Also, func tiona l progra m m ing doe sn’t a llow a func tion to ha ve side e ffe c ts. T his m e a ns a func tion c a n’t write to the disk, print
m e ssa ge s on the sc re e n, or do a nything othe r tha n re turn a re sult. T he goa l is to write m ost of a progra m using “ func tiona l c ode , ”
while re ta ining a te e nsy bit of c ode tha t doe s a ny dirty, nonfunc tiona l stuff tha t is still ne e de d.
H ow It K ills Bugs
W riting c ode in a func tiona l style gua ra nte e s tha t a func tion doe s only one thing (re turns a va lue ) a nd is de pe nde nt on one only
thing (the pa ra m e te rs pa sse d to it). T his m a ke s it ve ry e a sy to de bug. No m a tte r how m a ny tim e s you run a func tion, a s long a s
you’re pa ssing it the sa m e da ta , you will a lwa ys ge t the sa m e re sult.
E xa m ple A-1. E xa m ple

(defun unique-letters (name)


(concatenate 'string
"Hello "
(coerce (remove-duplicates name) 'string)))

(defun ask-and-respond ()
(princ "What is your name?")
(princ (unique-letters (read-line))))
Explanation
If you e nte r this c ode into the L isp RE PL a nd e xe c ute (ask-and-respond), you will be a ske d for your na m e , a nd the n gre e te d by
your na m e but with a ll duplic a te le tte rs re m ove d. All the ha rd work in this func tion is ha ndle d by unique-letters, whic h is

writte n in a func tiona l style . T he dirty work of inte ra c ting with the use r, whic h c a n’t be writte n in a pure ly func tiona l wa y,

is ha ndle d by ask-and-respond .
W e akne ss
T he m a in we a kne ss of func tiona l progra m m ing is tha t som e side e ffe c ts a re a lm ost a lwa ys ne c e ssa ry for a progra m to a c tua lly do
som e thing. T his m e a ns you c a n’t write a use ful progra m tha t ha s the e ntire ty of its c ode writte n in the func tiona l style . At le a st a
sm a ll a m ount of c ode will be nonfunc tiona l.
Func tiona l progra m m ing is disc usse d in Cha pte r 14.
M ac r o G uild M e le e F ighte r s
Lisp Diale c t
Com m on L isp
Synopsis
True mac ros a re one of L isp’s m ost unique a nd a m a z ing fe a ture s. In fa c t, the re a son L ispe rs put up with a ll those a nnoying
pa re nthe se s in the ir c ode is tha t those pa re nthe se s e na ble the a we som e L isp m a c ro syste m .

T rue m a c ros a llow you to a dd ne w func tiona lity to L isp in a ve ry funda m e nta l wa y. E xpe rie nc e d L ispe rs c a n use m a c ros to m a ke
the ir L isp c om pile r/inte rpre te r do the ir bidding c le a nly a nd e le ga ntly.
H ow It K ills Bugs
By using m a c ros, a n e xpe rie nc e d L ispe r c a n m inim iz e c ode duplic a tion, a nd be tte r ta ilor the unde rlying la ngua ge to the
proble m a t ha nd. T his le a ds to c le a ne r c ode a nd fe we r bugs.
E xa m ple A-2. E xa m ple
(defmacro three-way-if (expr a b &rest c)
(let ((val (gensym)))
`(let ((,val ,expr))

(cond ((and (numberp ,val) (zerop ,val)) ,a)

(,val ,@c)

(t ,b)))))
Explanation
L isp m a c ros a re so powe rful tha t you c a n a c tua lly write your own if-the n c om m a nd! T he c ode shown he re c re a te s a m a c ro c a lle d

three-way-if tha t ha s thre e bra nc he s: one for a nil va lue , one for a num e ric a l z e ro va lue , a nd one for e ve rything e lse

. For m ost purpose s, a func tion like this m ight se e m stupid, but if you e ve r wa nt to write a progra m tha t c onsta ntly ne e ds to
distinguish z e ros from nils (or ne e ds to ha ndle som e othe r dom a in-spe c ific he a da c he ), you’ll m a ke your life m uc h e a sie r by writing
a m a c ro.
W e akne ss
Sinc e L isp m a c ros a re so powe rful, the re is a lwa ys the da nge r of progra m m e rs a busing the m . Ove ruse of m a c ros c a n m a ke it ha rd
for othe r progra m m e rs to unde rsta nd your c ode .
Ma c ros a re disc usse d in Cha pte r 16.
Re star t G uild Ar mor e d F ighte r
Lisp Diale c t
Com m on L isp
Synopsis
Prope r e xc e ption ha ndling is e xtre m e ly diffic ult. T he re a re re a lly only two good a pproa c he s: Don’t ha ndle e xc e ptions a t a ll a nd
just le t your progra m die whe n one oc c urs, or ha ndle e ve ry single e xc e ption in the m ost dire c t a nd spe c ific wa y possible . But is it
truly possible to ha ndle e ve ry pote ntia l e xc e ption in your c ode ? If your write Com m on L isp c ode , it’s possible to ge t e xtre m e ly
c lose to this ide a l goa l.

For e xa m ple , suppose you write a func tion tha t ra ise s the pric e s on a list of widge ts. But the n, while the func tion is proc e ssing
one of the widge ts in the list, the re ’s a m e m ory a lloc a tion e rror. You c a n’t pre pa re for this type of e rror a he a d of tim e , sinc e it
c ould ha ppe n a nywhe re in a progra m . T his m a ke s it im possible to a ddre ss using tra ditiona l e xc e ption ha ndling m e thods.
E ve n if a func tion lowe r in the c a ll sta c k c a tc he s a nd re solve s the sourc e of the e xc e ption, the progra m still fa c e s a n unsolva ble
proble m : Som e of the widge t pric e s ha ve be e n ra ise d, while othe rs ha ve not. Com m on L isp, howe ve r, ha s a m e c ha nism for
a ddre ssing this proble m , c a lle d re starts.
In a la ngua ge tha t supports re sta rts, the func tion tha t ra ise s the widge t pric e s c a n m a ke the proc la m a tion, “ He y e ve rybody! If
som e thing ba d ha ppe ns while I’m working on m y widge ts, just use m y re sta rt (c a lle d try-again) whe n it’s sa fe for m e to finish m y
work!” Anothe r func tion, lowe r in the c a ll tre e , c a n now ha ndle the e rror, a nd the n c a ll try-again to e nsure tha t the widge t pric e s
won’t be c om e c orrupt. T his a llows the func tion to finish ra ising widge t pric e s a t the e xa c t point of fa ilure .
In fa c t, if you ha ve a progra m tha t c a n’t a fford to shut down (a we b se rve r, for e xa m ple ), you c a n still ha ndle a surprising
num be r of e xtre m e e xc e ptions in Com m on L isp without e nding the progra m . E ve n if the progra m e nc ounte rs a truly e xc e ptiona l
e xc e ption, it c a n sim ply dive rt c ontrol ba c k to the RE PL . T he progra m m e r c a n the n fix the c a use of the e xc e ption, a c c e ss a list of
a va ila ble re sta rts, a nd c ontinue running the progra m on the spot.
H ow It K ills Bugs
By using re sta rts a nd the L isp RE PL , a bug c a n be fixe d in a running progra m , a llowing you to “ hot sc ript” long-running
a pplic a tions with only a ne gligible inte rruption.
E xa m ple A-3. E xa m ple
(defun raise-widget-prices (widgets)
(when widgets

(loop (restart-case (progn (raise-price (car widgets))

(return))

(try-again () (princ "trying again"))))

(raise-widget-prices (cdr widgets))))


Explanation
T his is a n im ple m e nta tion of a func tion tha t ra ise s pric e s on a list of widge ts. T he a c tua l work of ra ising the pric e of a single

widge t is done by the raise-price func tion . T he c a ll to this func tion is prote c te d by wra pping it in a loop a nd the restart-

case c om m a nd, whic h de c la re s a re sta rt c a lle d try-again . If the pric e c a n be ra ise d without proble m s, the raise-price

func tion will c om ple te norm a lly, the loop is inte rrupte d with a return , a nd the ne xt ite m in the list of widge ts is proc e sse d.
On the othe r ha nd, if a n e rror oc c urs while ra ising the pric e on a widge t, a nothe r func tion (or the progra m m e r) c a n a tte m pt to fix

the proble m a nd c a ll the try-again re sta rt to re try the widge t a t the point of fa ilure , whic h le a ds to a nothe r c yc le through

the loop . T he func tion c a n the n c ontinue down the re st of the list, ra ising the pric e s on the re m a ining widge ts .
By using re sta rts, your c ode c a n offe r m ultiple a lte rna tive follow-up options for c oping with a n e xc e ption, so tha t e ve n the m ost
e xc e ptiona l e xc e ptions c a n be ha ndle d a ppropria te ly.
W e akne ss
E ve n though Com m on L isp ha s one of the m ost a dva nc e d e xc e ption ha ndling syste m s in e xiste nc e , it is still diffic ult to ha ndle
e ve ry e xc e ption a ppropria te ly in your c ode . Howe ve r, re sta rts give you the unique a bility to fix a running progra m a nd a llow it to
c ontinue ope ra ting, whic h is usua lly not possible in othe r la ngua ge s.
Re sta rts a re disc usse d in Cha pte r 14.
G e ne r ic Se tte r G uild Supply Ship
Lisp Diale c t
Com m on L isp
Synopsis
T o m odify the va lue of a va ria ble in Com m on L isp, you use setf. Howe ve r, this c om m a nd a lso ha s a n a m a z ing spe c ia l powe r:
Inste a d of a va ria ble na m e , you c a n pa ss it a c om ple x L isp e xpre ssion tha t re trie ve s a va lue . It c a n the n turn tha t e xpre ssion
“ inside out” a nd use it to m odify tha t va lue , ra the r tha n sim ply re trie ve it. T he se type s of e xpre ssions a re c a lle d ge ne ric se tte rs.

Ma ny c om m a nds be side s setf a lso support ge ne ric se tte rs. Using this fe a ture , m ost type s of da ta struc ture s c a n ge t by without a ny
spe c ific “ se tting” func tions of the ir own.
H ow It K ills Bugs
W he n you ha ve a c om plic a te d, ne ste d da ta struc ture , it’s ofte n e a sie r to unde rsta nd c ode tha t re trie ve s da ta from a spe c ific
loc a tion tha n it is to unde rsta nd c ode tha t se ts a va lue a t the sa m e loc a tion. If you wa nt to se t a va lue a t a spe c ific loc a tion in a
c om plic a te d struc ture , you usua lly ne e d to work ba c kwa rd through the struc ture to figure out how to c ha nge it. But with ge ne ric
se tte rs, you c a n le t L isp ha ndle the ha rd c ode for you. Ha ving sim ple r c ode is a gre a t wa y to fight bugs.
E xa m ple A-4. E xa m ple

(defparameter foo (list 1 (make-hash-table) 3))

(setf (gethash 'my-key (nth foo 1)) 77)


Explanation

T he e xa m ple c re a te s a va ria ble na m e d foo, whic h holds a list of thre e ite m s . T he se c ond ite m in the list is a n e m pty ha sh
ta ble . T he n it a dds a ke y na m e d my-key with a va lue of 77 to the ta ble inside foo a ll a t onc e , by putting a c om ple x e xpre ssion

into setf tha t “ ge ts a t” this loc a tion .


W e akne ss
By m uta ting a n e xisting da ta struc ture , ge ne ric se tte rs c a use a side e ffe c t, whic h viola te s one of the te ne ts of func tiona l
progra m m ing. T his m e a ns the y c a n’t be use d whe n progra m m ing in a pure ly func tiona l style .
Ge ne ric se tte rs a re disc usse d in Cha pte r 9.
DSL G uild H ot Rods
Lisp Diale c t
Com m on L isp
Synopsis
Be c a use L isp ha s suc h a sim ple synta x (e ve rything is de lim ite d with pa re nthe se s), it is e a sy to use it to build your own c ustom
progra m m ing la ngua ge , de signe d for a spe c ific dom a in. Suc h domain-spe c ific language s (DSLs) te nd to m a ke he a vy use of the L isp
m a c ro syste m . T he y re pre se nt a n e xtre m e form of m a c ro progra m m ing, tra nsform ing L isp into a c om ple te ly ne w progra m m ing
la ngua ge .
Explanation
T his is a n e xa m ple of c ode tha t use s a DSL to build a n HT ML pa ge . In this c a se , the pa ge displa ys “ He llo W or ld” in a browse r,
with the se c ond word re nde re d in bold. T he html a nd body c om m a nds (m a c ros c re a te d for the HT ML libra ry in Cha pte r 16)

ge ne ra te ope ning a nd c losing ta gs tha t will c onta in the body of the pa ge . T he n it c a lls the re gula r L isp func tion princ to

ge ne ra te the te xt. T he se c ond word is wra ppe d in a nothe r c ustom DSL c om m a nd, bold , whic h ge ne ra te s ope ning a nd c losing
bold ta gs a round the spe c ifie d te xt.
E xa m ple A-5. E xa m ple

(html (body (princ "Hello ")

(bold (princ "World!"))))


W e akne ss
Sinc e DSL s a re progra m m ing la ngua ge s you c re a te a ll by yourse lf, you c a n de finite ly shoot yourse lf in the foot if you a re n’t
c a re ful. It’s e a sy to c re a te c ode in a la ngua ge tha t is im possible for othe rs (a nd pe rha ps e ve n you) to unde rsta nd.
Cha pte r 17 disc usse s DSL s, inc luding the DSL tha t a llows you to write HT ML dire c tly inside your L isp c ode , a s shown in this
e xa m ple .
CLO S G uild Battle ship
Lisp Diale c t
Com m on L isp
Synopsis
Com m on L isp ha s the m ost sophistic a te d obje c t-orie nte d progra m m ing fra m e work of a ny m a jor progra m m ing la ngua ge , c a lle d
the Common Lisp Obje c t Sy ste m (CLOS). It is c ustom iz a ble a t a funda m e nta l le ve l using the Me taobje c t P rotoc ol (MOP ). T he re ’s
re a lly nothing like it a nywhe re e lse in progra m m ing. It le ts you c re a te inc re dibly c om ple x softwa re without losing c ontrol ove r the
c ode .
H ow It K ills Bugs
Obje c t-orie nte d programing (OOP ) is a c om m only use d te c hnique for ke e ping bugs unde r c ontrol. By writing c ode in a n obje c t-
orie nte d style , you c a n de c ouple diffe re nt pa rts of your c ode . W he n you de c ouple c ode , you bre a k your c ode into logic a l
c om pone nts, whic h c a n be te ste d inde pe nde ntly.
E xa m ple A-6. E xa m ple 1: W ra pping Code Around Me thods

(defclass widget ()
((color :accessor widget-color
:initarg :color)))

(defmethod describe-widget ((w widget))


(format t "this is a ˜a widget" (widget-color w)))

(defmethod describe-widget :before ((w widget))


(add-to-log "Somebody is checking on a widget"))
T he ba sic c onc e pts be hind obje c t-orie nte d progra m m ing in Com m on L isp a re disc usse d in Cha pte r 9. For de ta ile d inform a tion on
the de sign of CL OS, I re c om m e nd re a ding the CL OS pa pe rs c om pile d a t http://www. dre a m songs. c om /CL OS. htm l.
Explanation
For this e xa m ple , im a gine we run a c om pa ny tha t se lls widge ts, a nd we ne e d som e obje c t-orie nte d L isp c ode to he lp ke e p tra c k

of the m . First, we ne e d to c re a te a ne w CL OS c la ss (c a lle d widget) with defclass . It ha s one prope rty (or slot, in L isp

lingo) de sc ribing the widge t’s c olor. Ne xt, we de c la re a describe-widget, whic h prints out a de sc ription of the widge t . By
c onve ntion, a func tion de signe d to ope ra te on a spe c ific type of obje c t is c a lle d a me thod. In this c a se , the describe-widget is
c onside re d a m e thod of the widget obje c t.
Now suppose we wa nt to write a n e ntry to a log file e ve ry tim e a use r c he c ks on a widge t. Using the CL OS, we c a n de c la re one

or m ore be fore me thods tha t will a utom a tic a lly be c a lle d be fore the m a in describe-widget m e thod is e xe c ute d .
If we didn’t ha ve be fore m e thods a va ila ble , we would ne e d to dirty up our m a in widge t c ode to a dd logging, like so:

(defmethod describe-widget ((w widget))

(add-to-log "Somebody is checking on a widget")


(format t "this is a ˜a widget" (widget-color w)))

He re , we ’ve a dde d the c om m a nd for logging right in the m iddle of the describe-widget m e thod . T his c ode is a lot
uglie r, be c a use writing to logs ha s nothing intrinsic a lly to do with de sc ribing a widge t. T he logging in this ve rsion is a lso tightly
c ouple d to the m a in c ode , whic h m e a ns we c a n no longe r te st the widge t c ode inde pe nde ntly from the de bugging c ode . Using the
be fore m e thod le a ds to c le a ne r, m ore de c ouple d c ode .
Explanation
T his e xa m ple de m ontra te s multiple dispatc h, a powe rful te c hnique for writing m e thods tha t a re c hose n ba se d on the type s of the ir
pa ra m e te rs.
E xa m ple A-7. E xa m ple 2: Multiple Dispa tc h

(defclass color () ())

(defclass red (color) ())


(defclass blue (color) ())
(defclass yellow (color) ())

(defmethod mix ((c1 color) (c2 color))


"I don't know what color that makes")

(defmethod mix ((c1 blue) (c2 yellow))


"you made green!")

(defmethod mix ((c1 yellow) (c2 red))


"you made orange!")

T he e xa m ple be gins by c re a ting a color c la ss a nd a lso de fine s thre e de rive d c la sse s: red, green, a nd blue . T he n we
de c la re a mix m e thod, whic h will te ll us wha t ha ppe ns if we m ix a ny two c olors. By de fa ult, whe n we m ix two c olors, it just sa ys,

“ I don’t know wha t c olor tha t m a ke s” . Howe ve r, using m ultiple dispa tc h, we c an de fine more v e rsions of the mix m e thod.

For insta nc e , we c a n de c la re a ve rsion tha t m ixe s blue a nd ye llow , a nd a nothe r ve rsion for ye llow a nd re d . He re ’s wha t
ha ppe ns whe n we c a ll the se m e thods with diffe re nt c olors:
> (mix (make-instance 'red) (make-instance 'blue))
"I don't know what color that makes"
> (mix (make-instance 'yellow) (make-instance 'red))
"you made orange!"
T he im porta nt thing to note a bout the e xa m ple is tha t in orde r to figure out whic h m ix m e thod to c a ll in a give n situa tion, the
CL OS ne e ds to ta ke into a c c ount both of the obje c ts pa sse d into the m e thod. It is dispatc hing to a spe c ific im ple m e nta tion of the
m e thod ba se d on the type s of multiple obje c ts. T his is a fe a ture tha t is not a va ila ble in tra ditiona l obje c t-orie nte d la ngua ge s, suc h
a s Ja va or C++.
W e akne ss
Opinions va ry wide ly in the L isp c om m unity a s to how la rge a role obje c t-orie nte d te c hnique s should pla y in progra m m ing. T he
c ritic s of this style c om pla in tha t obje c t-orie nte d te c hnique s forc e da ta to be hidde n a wa y in lot of dispa ra te pla c e s by re quiring
the m to live inside m a ny diffe re nt obje c ts. Ha ving da ta loc a te d in dispa ra te pla c e s c a n m a ke progra m s diffic ult to unde rsta nd,
e spe c ia lly if tha t da ta c ha nge s ove r tim e . T he re fore , m a ny L ispe rs pre fe r to use func tiona l te c hnique s ove r obje c t-orie nte d
te c hnique s, though the two c a n ofte n be use d toge the r with som e c a re . None the le ss, the re a re still m a ny dom a ins in whic h obje c t-
orie nte d te c hnique s a re inva lua ble , suc h a s in use r inte rfa c e progra m m ing or sim ula tion progra m m ing.
The Continuation G uild Roc ke t P ods
Lisp Diale c t
Sc he m e (lim ite d support in Com m on L isp with c ontinuation-passing sty le , or through the use of spe c ia l libra rie s)
Synopsis
In the 1970s, a spe c ia l dia le c t of L isp wa s c re a te d tha t fe a ture d a pa rtic ula rly powe rful progra m m ing fe a ture c a lle d
c ontinuations. Ba sic a lly, c ontinua tions le t you put “ tim e tra ve l” into your c ode . T his a llows you to do things like run progra m s
ba c kwa rd, side wa ys, or in othe r c ra z y wa ys. For insta nc e , it’s gre a t for im ple m e nting a dva nc e d progra m m ing te c hnique s, suc h a s
nonde te rministic programming. In nonde te rm inistic progra m m ing, you write c ode tha t offe rs the c om pute r m ultiple c hoic e s for wha t
to do ne xt. If one c hoic e isn’t sa tisfa c tory, the c om pute r c a n “ roll ba c k tim e ” with c ontinua tions to try a diffe re nt pa th.
E xa m ple A-8. E xa m ple
(define continuation null)

(define (foo n)

(* (call-with-current-continuation
(lambda (c)

(set! continuation c)
(+ n 1)))

2))

Note

T his e xa m ple is in the Sc he m e L isp dia le c t a nd won’t run in Com m on L isp.


H ow It K ills Bugs
T he re a re m a ny situa tions whe re ha ving tim e tra ve l in your c ode c a n m a ke the c ode e a sie r to unde rsta nd. T he c la ssic e xa m ple is
in a we b se rve r. Ofte n, a pe rson m ust visit se ve ra l pa ge s on a we b pa ge in orde r to pe rform a single a c tion. W ith a c ontinua tion-
a wa re we b se rve r, you c a n write c ode tha t pre te nds the se pa ge s we re visite d a ll a t the sa m e tim e , m a king your c ode a lot le ss
buggy. L a te r on, the we b se rve r use s c ontinua tions to bre a k your c ode into se ve ra l pa rts (by using the tim e -tra ve l a bilitie s of
c ontinua tions), ta king c a re of a ll the ugly de ta ils of ha ndling a m ultipa ge we b a c tion.
Explanation

In the e xa m ple , we c re a te a sim ple func tion c a lle d foo , whic h a dds one to a num be r, a nd the n double s it. For insta nc e ,

running (foo 7) will re turn 16. Howe ve r, inside the func tion, the re is a c a ll to call-with-current-continuation , whic h

c a pture s the sta te of the func tion be fore the doubling ste p. It sa ve s this “ m om e nt in tim e ” in the va ria ble continuation .

T he c urre nt sta te of the running progra m is c a pture d a t this line . E ve rything tha t ha ppe ns afte r the c ontinua tion wa s c a pture d
will the n be e xe c ute d if we c a ll the c a pture d c ontinua tion. T he only pa rt of the foo c om m a nd tha t ha ppe ns a fte r the c ontinua tion

wa s c a pture d is the m ultiplic a tion by two . Conse que ntly, the va ria ble continuation is now a tim e m a c hine tha t we c a n use
to jum p into this pa st m om e nt to switc h out the num be r we wa nt to double with a nothe r one . So, if we we re to now c a ll
(continuation 100), it would re turn 200 (whic h is 100 double d). W e ha ve tra ve le d ba c kwa rd in tim e !
W e akne ss
Continua tions a re suc h a n a we som e fe a ture tha t the y don’t re a lly ha ve a downside . T he only re a l proble m the y pre se nt is for
c re a tors of progra m m ing la ngua ge s. T rue c ontinua tions a re te c hnic a lly diffic ult to put into a progra m m ing la ngua ge , so fe w
la ngua ge s support the m . Sc he m e ha ppe ns to be one of the m . T o le a rn m ore a bout c ontinua tion-ba se d we b se rve rs, se e
“ Im ple m e nta tion a nd Use of the PL T Sc he m e W e b Se rve r” by Shrira m Krishna m urthi, e t a l.
Br e vity G uild M ic r o F ighte r
Lisp Diale c t
Arc L isp (indire c tly a va ila ble in Com m on L isp using c ustom m a c ros)
Synopsis
L isp a llows you to write c ode tha t is inc re dibly c onc ise but doe sn’t look like your c a t wa lke d ove r your ke yboa rd. (I’m looking a t
you, Pe rl!) T his is possible be c a use of the va rious fe a ture s we ’ve a lre a dy m e ntione d, suc h a s m a c ros, func tiona l progra m m ing, a nd
L isp’s dyna m ic typing syste m .
T he re is one L isp dia le c t, howe ve r, tha t ta ke s this ide a to the e xtre m e : Arc . In fa c t, c ode bre vity is the prim a ry de sign goa l for
this la ngua ge . Pa ul Gra ha m , the de signe r of Arc , a na lyz e d la rge a m ounts of c om pute r c ode in a n a tte m pt to figure out whic h
prim itive c om m a nds a re ne e de d to write c ode tha t is a s c onc ise a s possible , while ke e ping the c ode re a da ble .
H ow It K ills Bugs
W ith Arc , the goa l is to write progra m s tha t a re short. It is de signe d to le t you sa y wha t you wa nt to sa y in the m ost c onc ise wa y
possible , le a ving no pla c e for bugs to hide .
E xa m ple A-9. E xa m ple

(accum a

(for n 1 1000

(unless (some [is 0 (mod n _)] (range 2 (- n 1)))

a.n)))

Note

T his e xa m ple is in the Arc L isp dia le c t a nd won’t run in Com m on L isp.
Explanation
T his e xa m ple c re a te s a list of a ll prim e num be rs be twe e n 1 a nd 1000, using the na ïve m e thod of c he c king for sm a lle r num be rs
tha t divide e ve nly into the c urre nt loop va lue .

T he accum func tion c re a te s a loc a l func tion na m e d a, whic h is use d to c olle c t a ny prim e s tha t a re found . W e ite ra te

through the inte ge rs with a for loop , c he c king for sm a lle r num be rs tha t divide e ve nly into the c urre nt va lue of i . If

none a re a re found, i is a dde d to the list of prim e s , by c a lling the func tion a with this ne w num be r. T he bra c ke ts, [ ], a re a
shortc ut for c re a ting a la m bda func tion with one pa ra m e te r, whic h is a c c e sse d with the unde rsc ore c ha ra c te r.
W e akne ss
Finding a n optim a lly c onc ise se t of c om m a nds is diffic ult. W ith too m a ny c om m a nds a va ila ble , your c ode c a n be c om e ha rd to
unde rsta nd, sinc e it’s diffic ult to re m e m be r wha t e a c h func tion doe s. W ith too fe w c om m a nds, progra m s c a n ge t too bulky. Arc
L isp trie s to find a ha ppy m e dium , a lthough the re ’s still room for a lte rna tive la ngua ge de signs optim iz e d for c ode bre vity.
Cha pte r 16 de m onstra te s how to use m a c ros to m a ke your c ode c onc ise , a nd m a ny othe r e xa m ple s of L isp’s powe rs of bre vity a re
shown in the c ha pte rs following tha t disc ussion.
M ultic or e G uild F or mation F ighte r s
Lisp Diale c t
Clojure L isp (a va ila ble in Com m on L isp with the CL -ST M e xte nsion)
Synopsis
Now tha t m ost c om pute rs ha ve m ultiple c ore s, the re is a lot of inte re st in finding e le ga nt wa ys to write m ultic ore /m ultithre a de d
c ode . One popula r a pproa c h is to use func tiona l da ta struc ture s a long with a software transac tional me mory syste m .
Using softwa re tra nsa c tiona l m e m ory, you c a n sha re c om ple x da ta struc ture s be twe e n se ve ra l thre a ds, with a gua ra nte e tha t no
thre a d will se e inc onsiste nt inform a tion in the da ta , e ve n if it trie s to re a d sha re d da ta while a nothe r thre a d is a tte m pting to write
to it.
H ow It F ights Bugs
Multithre a de d c ode te nds to be ve ry buggy. By using softwa re tra nsa c tiona l m e m ory, you c a n gre a tly inc re a se your odds of
writing bug-fre e m ultithre a de d softwa re .
Explanation

In this e xa m ple , we de fine two ba nk a c c ounts c a lle d checking a nd savings , with a tota l a m ount of $300 be twe e n the m .
W e the n de fine a transfer-to-savings func tion, whic h c a n be c a lle d to m ove m one y from the checking a c c ount to the savings

a c c ount .
E xa m ple A-10. E xa m ple

(def checking (ref 100))


(def savings (ref 200))

(defn transfer-to-savings [n]

(dosync (alter checking - n)


(alter savings + n)))

Note

T his e xa m ple is in the Clojure L isp dia le c t a nd won’t run in Com m on L isp.

Be c a use this func tion c onta ins a dosync bloc k, Clojure will m a ke sure the se two alter ope ra tions ha ppe n a t the sa m e
m om e nt in tim e . Of c ourse , both va lue s a re n’t re a lly a lte re d a t the e xa c t sa m e point in tim e , but the la ngua ge m a ke s sure it will
a ppe a r to ha ppe n sim ulta ne ously. If a nothe r thre a d we re to re a d the se two a c c ounts a t the sa m e tim e , a lso within a dosync bloc k,
it would se e e xa c tly $300 in the c om bine d a c c ounts, no m a tte r how m a ny tim e s e ithe r thre a d c he c ks the se va lue s.
W e akne ss
Softwa re tra nsa c tiona l m e m ory c a rrie s a pe rform a nc e pe na lty tha t c a nc e ls out som e of the pe rform a nc e ga ins tha t c om e with
using m ultiple CPU c ore s. Howe ve r, a s the num be r of CPU c ore s inc re a se s, this pe na lty is le ss of a n issue .
The Laz y G uild F r igate
Lisp Diale c t
Clojure (a va ila ble in Com m on L isp with the Se rie s libra ry, CL AZ Y libra ry, or c ustom m a c ros)
Synopsis
A la z y progra m m ing la ngua ge will pe rform a c a lc ula tion only if the c om pile r de te rm ine s it is a bsolute ly ne c e ssa ry to produc e a
visible re sult. Clojure is the m ost popula r L isp dia le c t to inc lude la z y progra m m ing a s a prim a ry fe a ture . Howe ve r, lim ite d form s
of la z y progra m m ing a re c om m on in a ll L isp dia le c ts.
H ow It K ills Bugs
L a z y la ngua ge s le t you c re a te infinite ly big da ta struc ture s (a s long a s you don’t try to use all of the da ta ), whic h a llows m ore of
your c ode to be form ula te d a s tra nsform a tions of la rge da ta struc ture s. In ge ne ra l, it is e a sie r to de bug da ta struc ture s tha n it is to
de bug a lgorithm s. Algorithm s involve ste ps tha t unfold ove r tim e , a nd to unde rsta nd the m , you usua lly ne e d to wa tc h the m a s the y
e xe c ute . Da ta , on the othe r ha nd, e xists inde pe nde ntly of tim e , whic h m e a ns you c a n find bugs in a da ta struc ture just by looking
a t it.
E xa m ple A-11. E xa m ple

(take 20 (filter even? (iterate inc 0) ))

Note

T his e xa m ple is in the Clojure L isp dia le c t a nd won’t run in Com m on L isp.
Explanation

T his c ode re turns the first 20 e ve n positive inte ge rs. T o do this, it first c re a te s a n infinite list of a ll positive inte ge rs ,

using the iterate func tion to c re a te a list of inte ge rs sta rting a t z e ro. T he n it filte rs out the e ve n num be rs . Fina lly, it ta ke s

the first 20 num be rs from tha t re sult . Until the fina l take c om m a nd, the da ta struc ture s be ing ope ra te d on a re the ore tic a lly
infinite . Howe ve r, sinc e Clojure is a la z y la ngua ge , it insta ntia te s the se da ta struc ture s only on a n a s-ne e de d ba sis. T his m e a ns tha t
only the first 20 suc h num be rs a re e ve r ge ne ra te d. (And e ve n the n, the y a re ge ne ra te d only if we a c tua lly use the fina l va lue
som e how, suc h a s printing it to the sc re e n. )
W e akne ss
Sinc e a la z y progra m m ing la ngua ge c hoose s the orde r in whic h your c ode is run, it c a n le a d to de bugging he a da c he s if you try to
tra c e your c ode a s it is running.
Cha pte r 18 disc usse s la z y progra m m ing.
Inde x

A note on the digital inde x

A link in a n inde x e ntry is displa ye d a s the se c tion title in whic h tha t e ntry a ppe a rs. Be c a use som e se c tions ha ve m ultiple inde x
m a rke rs, it is not unusua l for a n e ntry to ha ve se ve ra l links to the sa m e se c tion. Clic king on a ny link will ta ke you dire c tly to the
pla c e in the te xt in whic h the m a rke r a ppe a rs.

Symbols

#S pre fix, for struc ture s, W orking with Struc ture s, W orking with Struc ture s
#\ne wline , Sta rting with print a nd re a d, Sta rting with print a nd re a d
#\spa c e , Sta rting with print a nd re a d, Sta rting with print a nd re a d
#\ta b, Sta rting with print a nd re a d, Sta rting with print a nd re a d
& body ke yword, How Ma c ros Are T ra nsform e d, How Ma c ros Are T ra nsform e d
() pa re nthe se s, T he Gue ss-My-Num be r Ga m e , An Alte rna tive Globa l Va ria ble De finition Func tion , An Alte rna tive Globa l
Va ria ble De finition Func tion, Ba sic L isp E tique tte , De fining the sta rt-ove r Func tion, T he Building Bloc ks of L isp Synta x, Ma king
De c isions with Conditions
e m pty lists, Ba sic L isp E tique tte , Ma king De c isions with Conditions
sym m e try of nil a nd, Ma king De c isions with Conditions
for c a lling c om m a nds a nd func tions, T he Gue ss-My-Num be r Ga m e , An Alte rna tive Globa l Va ria ble De finition Func tion
for list of de c la re d va ria ble s in le t, De fining the sta rt-ove r Func tion
for orga niz ing c ode into lists, T he Building Bloc ks of L isp Synta x
*boa rd-sc a le * va ria ble , Dra wing a T ile , Dra wing a T ile
*dic e -sc a le * va ria ble , Dra wing the Ga m e Boa rd Using the SVG Form a t, Dra wing the Ga m e Boa rd Using the SVG Form a t
*from -tile * va ria ble , Ha ndling the Hum a n Pla ye r, Ha ndling the Hum a n Pla ye r
*num -pla ye rs* va ria ble , Inc re a sing the Num be r of Pla ye rs, Inc re a sing the Num be r of Pla ye rs
*print-c irc le * va ria ble , Circ ula r L ists, Circ ula r L ists
*sta nda rd-output* va ria ble , Building a More Com plic a te d SVG E xa m ple , Building a More Com plic a te d SVG E xa m ple
*top-offse t* va ria ble , Dra wing the Ga m e Boa rd Using the SVG Form a t, Dra wing the Ga m e Boa rd Using the SVG Form a t
404 e rror pa ge , Building a Dyna m ic W e bsite
:@ fla g, for c olum ns in ta ble s, Justifying Output
:if-e xists ke yword pa ra m e te r, W orking with File s, W orking with File s
:initia l-va lue ke yword pa ra m e te r, Se que nc e Func tions for Ite ra ting Ac ross a Se que nc e , Se que nc e Func tions for Ite ra ting Ac ross a
Se que nc e
:junk-a llowe d pa ra m e te r, De c oding the Va lue s of Re que st Pa ra m e te rs, De c oding the Va lue s of Re que st Pa ra m e te rs
:pre tty pa ra m e te r, Conve rting Node Ide ntifie rs, Conve rting Node Ide ntifie rs
:ra dix pa ra m e te r, De c oding the Va lue s of Re que st Pa ra m e te rs, De c oding the Va lue s of Re que st Pa ra m e te rs
:te st ke yword pa ra m e te r, T he e dge s-to-a list Func tion, T he e dge s-to-a list Func tion, Growing Pla nts in Our W orld, Growing Pla nts
in Our W orld
to use e qua l, Growing Pla nts in Our W orld, Growing Pla nts in Our W orld
˜$ c ontrol se que nc e , T he Control String Pa ra m e te r, Control Se que nc e s for Form a tting Floa ting-Point Num be rs
˜% c ontrol se que nc e , Printing Multiple L ine s of Output
˜& c ontrol se que nc e , Printing Multiple L ine s of Output
˜:; c ontrol se que nc e , Ite ra ting T hrough L ists Using Control Se que nc e s
˜< c ontrol se que nc e , Justifying Output
˜> c ontrol se que nc e , Justifying Output
˜a c ontrol se que nc e , T he Control String Pa ra m e te r, T he Control String Pa ra m e te r
˜b c ontrol se que nc e , Control Se que nc e s for Form a tting Num be rs, Control Se que nc e s for Form a tting Num be rs
˜d c ontrol se que nc e , Control Se que nc e s for Form a tting Num be rs, Control Se que nc e s for Form a tting Num be rs
˜f c ontrol se que nc e , Control Se que nc e s for Form a tting Floa ting-Point Num be rs, Control Se que nc e s for Form a tting Floa ting-Point
Num be rs
˜t c ontrol se que nc e , Printing Multiple L ine s of Output, Printing Multiple L ine s of Output
˜x c ontrol se que nc e , Control Se que nc e s for Form a tting Num be rs, Control Se que nc e s for Form a tting Num be rs
˜{ c ontrol se que nc e , Justifying Output
˜} c ontrol se que nc e , Justifying Output

a b-ge t-ra tings-m a x func tion, Alpha Be ta Pruning


a b-ge t-ra tings-m in func tion, Alpha Be ta Pruning
a b-ra te -position func tion, Alpha Be ta Pruning
a c a de m ic re se a rc h, W he re Did L isp Com e From ?
a c c um func tion, Bre vity Guild Mic ro Fighte r
a c c um ula tor, T a il Ca ll Optim iz a tion
a c ross in loop m a c ro, Using Multiple for Cla use s, De te rm ining the W inne r
a dd func tion, pre dic a te s in, Cre a ting Your Own Ge ne ric Func tions with T ype Pre dic a te s
a dd-c ops func tion, Building the Fina l E dge s for Conge stion City, T he e dge s-to-a list Func tion
a dd-ne w-dic e func tion, Finding the Ne ighbors, T a il Ca ll Optim iz a tion, Im proving the Dic e of Doom Re inforc e m e nt Rule s
a dd-pa ssing-m ove func tion, Ge ne ra ting a Ga m e T re e , Ma pping a nd Se a rc hing Ac ross L a z y L ists
a dd-pla nts func tion, Growing Pla nts in Our W orld, Ha ndling Anim a l Re