|
|||||
|
|
#1 |
|
|
I have an issue with the code below. It's a really simple database with a list of records each of which is an a-list of fields. I want add-field to mutate *records*, find-max having returned a single record from *records* but as you can see it doesn't work. I think I understand why; *records* is a special and gets rebound for each frame but I can't figure out a way around it. Thanks, Charlieb. Here's a little session demonstrating the problem: CL-USER> *records* (((GLOBAL-UNIQUE-ID . 0))) CL-USER> (add-field (make-field 'test 'one) (find-max global-unique-id)) ((TEST . ONE) (GLOBAL-UNIQUE-ID . 0)) CL-USER> *records* (((GLOBAL-UNIQUE-ID . 0))) CL-USER> ; ----- CODE BEGINS ----- (defparameter *records* (list (list (cons 'global-unique-id 0))) "Holds all the records as a list of lists") ; All selectors that use *records* will return full records ; ; All selectors that take a record will return the full record: ; a cons of the record and it's name ; ; -------------- FIELD LEVEL OPERATORS (defun value (field) (cdr field)) (defun name (field) (car field)) (defun field (name record) (***oc name record)) (defun make-field (name value) (cons name value)) ; -------------- FIELD/RECORD OPERATORS (defun add-field (field record) (push field record)) ; -------------- RECORD LEVEL OPERATORS (defun filter (field &optional) (remove-if-not (lambda (record) (eql field (field (car field) record))) records)) (defun make-record (&optional fields) (cons (make-field 'global-unique-id (1+ (value (field 'global-unique-id (find-max 'global-unique-id))))) fields)) ; --------------- RECORD/DATABASE OPERATORS (defun add-record (record) (push record *records*)) ; --------------- DATABASE LEVEL OPERATORS (defun find-max (field &key (pred #'>)) "Returns the whole record!" (let ((max (value (field field (car *records*)))) (result (car *records*))) (dolist (record (cdr *records*) result) (when (funcall pred (value (field field record)) max) (setq max (value (field field record)) result record))))) |
|
|
#2 |
|
|
> Hi, > I have an issue with the code below. It's a really simple > database with a list of records each of which is an a-list of fields. > I want add-field to mutate *records*, find-max having returned a > single record from *records* but as you can see it doesn't work. I > think I understand why; *records* is a special and gets rebound for > each frame but I can't figure out a way around it. No, the problem is that the PUSH in add-field is just modifying the local variable, record. You need to p*** PUSH the "place" you want to modify. One way to do that is to p*** it, not the record you want to modify but the sublist of *records* that starts with the record you want to modify. Then you can write add-field like this: (defun add-field (field records) (push field (car records))) For this to work, you'll need to change find-max to something like this (untested): (defun find-max (field &key (pred #'>)) "Returns the whole record!" (loop for cons on *records* for value = (value (field field (car cons))) do (when (funcall pred value max) (setq max value result cons)))) The point is since you want to modify *records* you need to p*** add-field a cons cell that is part of the list structure of *records*. -Peter -- Peter Seibel peter@javamonkey.com Lisp is the red pill. -- John Fraser, comp.lang.lisp |
|
|
#3 |
|
|
| Hi, | I have an issue with the code below. It's a really simple | database with a list of records each of which is an a-list of | fields. I want add-field to mutate *records*, find-max having | returned a single record from *records* but as you can see it | doesn't work. I think I understand why; *records* is a special | and gets rebound for each frame but I can't figure out a way | around it. Yes, *records* is special, but its binding remains unchanged. Your problems is elsewhere. | (defun add-field (field record) | (push field record)) This is useless. The push here only changes the value of the local variable record, which ceases to exist the moment the function returns. | (defun find-max (field &key (pred #'>)) | "Returns the whole record!" | (let ((max (value (field field (car *records*)))) | (result (car *records*))) | (dolist (record (cdr *records*) result) | (when (funcall pred (value (field field record)) max) | (setq max (value (field field record)) | result record))))) This is ok as far as it goes, except that you really want to mutate the /place/ where the result is found, not the result itself, by pushing something on the front of the list. If you were to introduce the following changes, then your example might just work: (defun add-field (field records) (push field (car records))) (defun find-max-place (field &key (pred #'>)) "Returns the whole record!" (loop with max = (value (field field (car *records*))) and result = *records* for records on (cdr *records*) for val = (value (field field (car records))) when (funcall pred val max) do (setf max val result records) finally (return result))) TEST> (add-field (make-field 'test 'one) (find-max-place 'global-unique-id)) ((TEST . ONE) (GLOBAL-UNIQUE-ID . 0)) TEST> *records* (((TEST . ONE) (GLOBAL-UNIQUE-ID . 0))) I suspect this is far from the best interface though, as the use of add-field and find-max-place as given here seems a bit counterintuitive. But I'll leave you to work out a better interface. I hope at least my explanation of what went wrong makes sense. -- * Harald Hanche-Olsen <URL:http://www.math.ntnu.no/~hanche/> - Debating gives most of us much more psychological satisfaction than thinking does: but it deprives us of whatever chance there is of getting closer to the truth. -- C.P. Snow |