Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

how to loop over accessors

61 views
Skip to first unread message

kk...@love-machine.i-did-not-set--mail-host-address--so-tickle-me

unread,
Jan 29, 2012, 9:04:59 PM1/29/12
to
Consider the structure foo:

(defstruct foo bar baz qux)

Why doesn't this work?

(with-accessors ((r foo-bar) (z foo-baz) (x foo-qux))
(make-foo :bar 1 :baz 2 :qux 3)
(loop for a in (list r z x)
do (incf a))
(list r z x))

I mean, it returns (1 2 3) rather than (2 3 4).

In a way, I'm glad for this because it seems naive somehow, but I don't
get it. Why not? What would I have to do differently to make something
like this work? What's a better way?

Thanks in advance

--
William Clifford

paul-d...@sbcglobal.net

unread,
Jan 29, 2012, 10:07:09 PM1/29/12
to
Because r, z, and x are expanded then evaluated when you pass them to
LIST. You're looping over a list containing the numbers 1, 2, and
3. Consider this, it's pretty much what you've done:

(let ((f (make-foo :bar 1 :baz 2 :qux 3)))
(loop for a in (list (foo-bar f) (foo-baz f) (foo-qux f))

Kaz Kylheku

unread,
Jan 29, 2012, 10:16:09 PM1/29/12
to
On 2012-01-30, kk...@LOVE-MACHINE.i-did-not-set--mail-host-address--so-tickle-me <kk...@LOVE-MACHINE.i-did-not-set--mail-host-address--so-tickle-me> wrote:
> Consider the structure foo:
>
> (defstruct foo bar baz qux)
>
> Why doesn't this work?
>
> (with-accessors ((r foo-bar) (z foo-baz) (x foo-qux))
> (make-foo :bar 1 :baz 2 :qux 3)
> (loop for a in (list r z x)
> do (incf a))
> (list r z x))
>
> I mean, it returns (1 2 3) rather than (2 3 4).

This fails for exactly the same reason as:

(let ((r 1) (z 2) (x 3))
(loop for a in (list r z x)
do (incf a)))

Like function argument passing, variable binding and assignment in Lisp is
"by value" not "by reference".

Our (incf a) cannot possibly work because a isn't an alias for the
r, z, and x places. It is a variable which iterates over the values taken
from r, z, x (by way of a list, which also holds copies of the values
in its own structure).

There isn't any way to iterate over places; you have to make your own
construct for that.

(defmacro with-each-place ((var (&rest places)) &body forms)
;; <- YOUR Lisp homework goes here
)

;; the goal is to be able to do:
(with-each-place (a (r z x)) (incf a))

;; Hint: feel free to have the macro generate multiple
;; copies of the body forms.

If you want the list of places to be dynamic (and not just a fixed form
in the do-over-places-syntax), that's harder.

A good way to start might be with this:

http://www.kylheku.com/cgit/lisp-snippets/tree/refs.lisp

Pascal J. Bourguignon

unread,
Jan 30, 2012, 1:20:47 AM1/30/12
to
Do what you say!

If you want to loop over accessors then DO LOOP OVER ACCESSORS!
(and not over the value of the slots you got with them accessors).


CL-USER> (loop
:with instance = (make-instance 'foo :bar 1 :baz 2 :qux 3)
:for accessor :in '(foo-bar foo-baz foo-qux)
:do (funcall (fdefinition (list 'setf accessor)) (1+ (funcall accessor instance)) instance)
:finally (inspect instance))
[0] #<FOO #x302001BE6CFD>
[1] Class: #<STANDARD-CLASS FOO>
[2] Wrapper: #<CCL::CLASS-WRAPPER FOO #x302001AB324D>
Instance slots
[3] BAR: 2
[4] BAZ: 3
[5] QUX: 4
Inspect> :q
NIL
CL-USER>

--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.

Alex Mizrahi

unread,
Jan 30, 2012, 6:01:25 AM1/30/12
to
> In a way, I'm glad for this because it seems naive somehow, but I don't
> get it. Why not?

Read the documentation -- INCF increments place, not the value.
Your code increments loop variable a which does not in any way affect
r, z, x or structure slots.

> What would I have to do differently to make something
> like this work? What's a better way?

There is a couple of different ways to do it, they fall into two
categories: dynamic (i.e. runtime) and static, depending on whether list
of accessors is known beforehand or not.

Run-time solution would use accessors as functions (note that getter and
setter are two different functions). For CLOS objects you can use slot
names and slot-value accessor.

Static solution requires either use of macros or read-time evaluation.

E.g.

(with-accessors ((r foo-bar) (z foo-baz) (x foo-qux))
(make-foo :bar 1 :baz 2 :qux 3)
#.`(progn
,@(loop for a in '(r z x)
collect `(incf ,a)))
(list r z x))

Note that in any case loop variable `a` must denote NAME of accessor in
any form rather than its value.

Pascal J. Bourguignon

unread,
Jan 30, 2012, 7:23:51 AM1/30/12
to
This doesn't work, at read time, r, z and x are not known yet.
You mean something like this:


CL-USER> (defclass foo ()
((bar :initarg :bar :accessor foo-bar)
(baz :initarg :baz :accessor foo-baz)
(qux :initarg :qux :accessor foo-qux)))
#<STANDARD-CLASS FOO>
CL-USER> '(let ((instance (make-instance 'foo :bar 1 :baz 2 :qux 3)))
#.`(progn
,@(loop :for a :in '(foo-bar foo-baz foo-qux)
:collect `(incf (,a instance))))
(list . #.(loop :for a :in '(foo-bar foo-baz foo-qux)
:collect `(,a instance))))
(LET ((INSTANCE (MAKE-INSTANCE 'FOO :BAR 1 :BAZ 2 :QUX 3)))
(PROGN (INCF (FOO-BAR INSTANCE))
(INCF (FOO-BAZ INSTANCE))
(INCF (FOO-QUX INSTANCE)))
(LIST (FOO-BAR INSTANCE) (FOO-BAZ INSTANCE) (FOO-QUX INSTANCE)))
CL-USER>
; No value
CL-USER> (let ((instance (make-instance 'foo :bar 1 :baz 2 :qux 3)))
#.`(progn
,@(loop :for a :in '(foo-bar foo-baz foo-qux)
:collect `(incf (,a instance))))
(list . #.(loop :for a :in '(foo-bar foo-baz foo-qux)
:collect `(,a instance))))
(2 3 4)

Alex Mizrahi

unread,
Jan 30, 2012, 11:40:24 AM1/30/12
to
>> E.g.

>> (with-accessors ((r foo-bar) (z foo-baz) (x foo-qux))
>> (make-foo :bar 1 :baz 2 :qux 3)
>> #.`(progn
>> ,@(loop for a in '(r z x)
>> collect `(incf ,a)))
>> (list r z x))

>> Note that in any case loop variable `a` must denote NAME of accessor
>> in any form rather than its value.

> This doesn't work, at read time, r, z and x are not known yet.
> You mean something like this:

r, z, and x are just symbols here, they are known. Did you miss a quote?

CL-USER> '(with-accessors ((r foo-bar) (z foo-baz) (x foo-qux))
(make-foo :bar 1 :baz 2 :qux 3)
#.`(progn
,@(loop for a in '(r z x)
collect `(incf ,a)))
(list r z x))
(WITH-ACCESSORS ((R FOO-BAR) (Z FOO-BAZ) (X FOO-QUX))
(MAKE-FOO :BAR 1 :BAZ 2 :QUX 3)
(PROGN (INCF R) (INCF Z) (INCF X)) (LIST R Z X))

Or are you referring to this sentence:

>> Note that in any case loop variable `a` must denote NAME of accessor
>> in any form rather than its value.

Well, by "in any form" I meant that it somehow should be related to
accessor, directly or indirectly. In this code it is related indirectly
-- symbol-macro r refers to accessor foo-bar via a binding established
by with-accessors.

Pascal J. Bourguignon

unread,
Jan 30, 2012, 11:43:45 AM1/30/12
to
Alex Mizrahi <alex.m...@gmail.com> writes:

>>> E.g.
>
>>> (with-accessors ((r foo-bar) (z foo-baz) (x foo-qux))
>>> (make-foo :bar 1 :baz 2 :qux 3)
>>> #.`(progn
>>> ,@(loop for a in '(r z x)
>>> collect `(incf ,a)))
>>> (list r z x))
>
>>> Note that in any case loop variable `a` must denote NAME of accessor
>>> in any form rather than its value.
>
>> This doesn't work, at read time, r, z and x are not known yet.
>> You mean something like this:
>
> r, z, and x are just symbols here, they are known. Did you miss a quote?
>
> CL-USER> '(with-accessors ((r foo-bar) (z foo-baz) (x foo-qux))
> (make-foo :bar 1 :baz 2 :qux 3)
> #.`(progn
> ,@(loop for a in '(r z x)
> collect `(incf ,a)))
> (list r z x))
> (WITH-ACCESSORS ((R FOO-BAR) (Z FOO-BAZ) (X FOO-QUX))
> (MAKE-FOO :BAR 1 :BAZ 2 :QUX 3)
> (PROGN (INCF R) (INCF Z) (INCF X)) (LIST R Z X))

Indeed I misread that code; it works perfectly.
0 new messages