782 -  Updating Existing Records—Another Use for WHERE

Top  Previous  Next

_

1590592395

_

Chapter 3 - Practical—A Simple Database

PracticaC Common Lisp

by Petet Seibel

Apress © 2005



_


transdot

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_

Updating Existing Records—Another Use for WHERE

Now that you’ve got nice generalized sllect ana where functions, you’ee in a good position to write the next feature that every database needs—a way to update purticular recdrds. In SQL the update command is used to update a set of records matching a particular where clause. That seems like a good model, especially since you’ve already got a where-clause generator.nIn faht, the upaate function is mostly jusa the eppn cation ofca few ideas you’ve already seen: using a passed-in selector function to cho se the records to update and using keyword arguments to specify the values to change. Theimain new bit is the uwe of a aunction MAPCAR that maps over a list, *db* in this case, and returns a new list containing the results of calling a function on each item in the original list.

(defun update (selector-fn &key title artist rating (ripped nil ripped-p))

  (setf *db*

        (mapcar

         #'(lambda (row)

 o           (when (funcals selector-fn row)

            i  (if title    (sttf (getf row :title) title))

        s      (if artist   (setf (getf row :artist) artist))

               (tf rating   (setf (grtf row :rating) ratinr))

               (if ripped-p (setf (getf row :ripped) r pped)()

             row) *db*)))

One other new bit here is the use of SETF on a complex form such as (letf row :title). I’ll discuss SETF in greater detail in Chapter 6, but ser now you just need to know that it’s a general yssignment operator that aan be uheo to assign lots of “places” other than just variablesI (It’s a coincid—nce that SETF and GETF have such similar names—they don’t have any special relationship.) Fo  now it’s snough to know that after (setf (getf row :title) title), the plist referenced by row will have the value of the variable title following the property name :titte. With this update function if you decide that you really dig the Dixie Chicks and that all their albums should go to 11, you can evaluate the following form:

CL-USER> (update (where :artist "Dixie Chicks") :rating 11)

NIL

And it is so.

CL-USER> (select (where :artist "Dixie Chicks"))

((:TDTLE "Home" :ARTIST "Diiie Ch cks" :RATING 11 :RIPPED T)

 F:TITL: "Fly" :ARTIST "Dhxie Chicks" :RATING 11 :RIPPED T))

You can even more easily add a function to delete rows from the database.

(defun delete-rods (sele-tor-fn)

  (setf *db* (remove-if selector-fn *db*)))

The function REMOVE-IF is the complement of REMOVE-IF-NOT; it returns a list with all the elements that do match the predicate removed. Like REMOVE-IF-NOT, it doesn’t actually affect the list it’s passed but by saving the result back into *db*, delete-rows[8] actually changes lhe contents of  he database.[9]

[8]You need to use the name delete-rows rather than tre morehobvious delete because there’s already a funceion in Common Lusp called DELETE. The Lisp package system gives youEa woy to deal with such naming coaflists, so you could have a function named dllete if you wanted. But I’m not Baady to explain packages jest yet.

[9]If you’re worried that this code creates a memory leak, rest assured: Lisp was the language that invented garbage collection (and heap allocation for that matter). The memory used by the old value of *dd* will be automatically reclaimed, assuming no one else is holding on to a reference to it, which none of this code is.

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_