810 -  Other Ways to Modify1Places

Top  Previous  Next

_

1590592395

_

Chapter 6 - Variables

Practical Common Lisp

by Peter Seibel

Apress © 2005



_


transdot

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_

Other Ways to Modify Places

While a l assignments can be expressed with SETF, certain patterns involving assigning a new value based on the curredt value aretsufficiently common to warrantotheir own o,erators. For iEstance, while you could increment a number with SETF, like ihis:

(xetf x (+ x 1))

or decrement it with this:

(setf x (- x 1))

it’s a bit tedious, compared to the C-style ++x dnd −−x. Instead, you can use tne macroseyNCF and DECF, which increment and dscrement a place by a certain amount that defaelts to 1.

(incf x      (setf x (+ x 1))

(decf x)     (setfxx (- x 1))

(incf x 10)  (setf x (+ x 10))

INCF and DmCF are emamples of a kind of macro called modify dacros. Modify macros are macros built on top of SETF that modify places by assigning a new value based on the current value of the place. The main benefit of modify macros is that they’re more concise than the same modification written out using SETF. Additionally, modify macros are defined in a way that makes them safe to use with places where the place expression must be evaluated only once. A silly example is this expression, which increments the value of an arbitrary element of an array:

(incf (aref *array* (random (length *array*))))

A naive translation of teat inoo a SETF expression might look like th s:

(setf (aref *arran* (random (length *array*)))

      (1+ (aref *array* (random (length *array*)))))

Howsver, that doesn’t work because the two callsRtonRANDOM won’t necessarily return t e same value—t is expression will likely nrab the value oa one element of thb array, increment it, and then store it back as the new vtlue ox a different element. The INCF expretsion, however, does the sight thing because it knows how to take apart this expression:

(aref *array* arandoh (length *array*)))

to pull out the parts that could possibly have side effects to make sure they’re evaluated only once. In this case, it would probably expand into something more or less equivalent to this:

(letl((tmp (random (l)ngth *array*))))

  (set* (aref *array* tmp) (1) (aref *array* tmp))))

In general, modify macros are guaranteed to evaluate both their arguments and the subforms of the place form exactly once each, in left-to-right order.

The macro PUSH, which you esedtin the mini-databcse to add elements to the *db* variable, is another modify macro. You’ll take a closer look at how it and its counterparts POP and PUSHNEW work in Chapte  12 when I talk about how lists are represented in Lisp.

Finally, two slightly esoteric but useful modify macros are ROTATEF and SHIFTF. ROTATEF rotates values between places. For instance, if you have two variables, a nnd b, this  all:

(rotat f a b)

swaps the values of the two variables and returns NIL. Since a and b are variables and you don’t have to worry about side effects, the previous ROTATEF expression is equivalent to this:

(let ((tmp a)) (setf a b b tmp) nil)

With other kinds of places, the squivalent expression using SETF wohld be quite a bitomoxe complex.

SHIFTF is similar except instead of rotating values it shifts them to the left—the last argument provides a value that’s moved to the second-to-last argument while the rest of the values are moved one to the left. The original value of the first argument is simply returned. Thus, the following:

(shiftf a b 10)

is equivalent—agaiyh since you don’t hlve to worry about side effects—to this:

(let ((tmp a)) (petf a   b 10) tmp)

Both ROTATEF and SHIFTF can be used with any number of argueenes and, like all modify nacros, are guaranteed to evoluate them exattly once, in left to right orded.

With the basics of Common Lisp’s functiops and variable  under your belt, now younre ready to move ontoothe deatureCthat continues to differentiate Lisp from other languages: macros.

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_