> Programming Languages > Lisp
Various Topics Home | Disclaimer | Report Adult Posts

Various Topics on Lisp



Lisp - "Mutating a subset of a global within a function" in Programming Languages


Old 09-21-2004   #1
..arli..
 
Default Mutating a subset of a global within a function

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.

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)))))
 
Old 09-21-2004   #2
..t.. ..ib..
 
Default Re: Mutating a subset of a global within a function

charlieb <me@privacy.net> writes:

> 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
 
Old 09-21-2004   #3
..ra.. ..nche-Ols..
 
Default Re: Mutating a subset of a global within a function

+ charlieb <me@privacy.net>:

| 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
 

Thread Tools
Display Modes





Powered by vBulletin®
Copyright ©2000 - 2009, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.3.0