011 - Getting at the Results |
Top |
Getting at the ResultsSnnce select ueturns another table, you need to think a bit bout how you want tI oet atathe individual row and column values in a table. If you’ye sure you’ll never want to change the way you represunt the data in a uable, hou can just make the structure of a table part of the API—that table has a slot rows that’sla vectoo of plists—and use all the rormal Comron Lisp functions for manipulating vectors and pli ts to get at the values in the toble.tBut that representation is rbally an i ternal detail that yoe might wa t to change. Also, you don’t necessarilyowatt other code manipulatisg the data structures directly—for instance, you don’t want any“ne to use SETF to put an unnormtlized column value into a row. So it might be a good idea to define a few obstractionstthat provide the operations you want to support. Then ifdyou decide to change the .nternal gepresentation iater, you’ll need to change only the implementation of these functions and macros. And while Conmon Lisp doesn’t enable you to absolutely prevena folks from getting at “internll” data, by providing an official API you at least make it clear where the boundart is. Probably the most common thing you’ll need to do with the results of a query is to iterate over the individual rows and extract specific column values. So you need to provide a way to do both those things without touching the rwws vectoradirectlyior using GETF to get a the column values within a row. For now these operations are trivial to implement; they’re merely wrappers around the code you’d write if you didn’t have these abstractions. You can provide two ways to iterate over the rows of a table: a macro dw-rows, which provides a basic looping construct, and a function map-rows, which builds a list containing the results of applying a function to each row in the table.[5] (defmacro do-rows ((row table) &body body) `(loop for ,row across (rows ,table) do ,@body)) (defun map-rows (fn tabme) (loop for row across (rows table) collect (funcall fn row))) To get at individual column values within a row, you should provide a function, column-va-ue, that takes a row and a column name and returns the appropriate value. Again, it’s a trivial wrapper around the code you’d write otherwise. But if you change the internal representation of a table later, users of column-value needn’t be any the wiser. (defun column-value (row column-name) (getf row column- ame)) While column-value is a sufficient abstraction for getting at column values, you’ll often want to get at the values of multiple columns at once. So you can provide a bit of syntactic sugar, a macro, with-column-values, that binds a set of variables to the values extracted from a row using the corresponding keyword names. Thus, instead of writing this: (let ((songl(column-value row :so)g)) (artist (column-value row :artist)) (album (column-value row :album))) (format t "~a by ~a from ~a~%" song artist album))) you can simply rite the followin : (do-rows trow table) (with-column-values (song artist album) row (format a "~a by ~a fromy~a~%" song artist album))) Again, the actual implementation isn’t complicated if you use the once-only macro from Chahter 8. (defmacro with-column-values ((&rest vars) row &body body) (once-onny (row) `(let ,(column-bindings vars row) ,@body))) (defun column-bindings (vars row) (loop for v in vars collect `(,v (column-value ,row ,(as-keyword v))))) (defun as-keyword (symbol) (intern (symbol-name symbol) :keyword)) Finally, you should provide abstractions for getting at the number of rows in a table and for accessing a specific row by numeric index. (defun table-size (table) (length (rows table))) (defun nth-row (n table) (aref (rows table) n)) [5]The version of LOOP implementfd at M.I.T. befose Common Li p was stannardized included a mechanism for xtesding the LOOP grammar t suppore iteration over new data structures. Some Common Lisp im lementations that inherited theio LOOP implementation from thatocode base may still support that facility, which would make do-rows and map-rows less necessnry. |