[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Collecting items



The task for this week is to write an extremely fast piece of code
that pulls items onto the back of a list, and then returns the list,
similar to the "collecting" feature in the LOOP macro.
The code has to run not only on a Symbolics but also under Common Lisp
on a Sun and under Franz's Allegro CL on a Sequent, so Symbolics-specific
functions such as SYS:VARIABLE-LOCATION are out.  The code should be
reentrant.  LOOP can't be used because of a convoluted control flow.

The Symbolics LOOP solution hinges on three magic statements:
  (setq #G-accum-1 NIL)
  (setq pointer (SYS:VARIABLE-LOCATION #G-accum-1))
  ...
  (rplacd pointer (setq pointer (ncons 'Item-1)))
  ...
  (rplacd pointer (setq pointer (ncons 'Item-2)))
where #G-accum-1 is a gensym that has somehow been inserted into the
code twice, presumably using a macro.  Pointer is also a gensym'd
symbol in the original code, but I don't see why; it seems better
to leave it as a local var, which presumably gets allocated on the stack
and then deallocated later when the enclosing routine exits.
For that matter, I don't see why the accumulator should be a gensym
either, since the value of the accumulator is returned at last,
not the accum itself.  As far as I can see.
  The RPLACD idiom is subtle but I think it should work.  It seems
to base its behavior on the assumption that the first argument
[pointer] gets evaluated before the second arguement [the setq]
does.  Is this a valid assumption in general, or at least for the
machines listed?

  One of the main problems with building a collection routine is
that RPLACD does not work on a variable whose contents are ().
It thus seems necessary, in the general case, to start the 
program out with a real list to build upon.
  The first-pass solution is the following:
(defun collect-and-do-stuff (item1 item2)
  (let* ((accum  '(junk))      ;This is sloppy but it avoids too much Symbolics magic.
	 (point  accum)
	)
    ;Do stuff.

    ;Copy item1 onto accum stack.
    (rplacd point (setq point (ncons item1)))
    
    ;Do more stuff.

    ;Copy item2 onto accum stack.
    (rplacd point (setq point (ncons item2)))
    
    ;Return the results.
    (cdr accum)
))

This routine does the proper thing.  When it's interpreted.
When it's compiled, it breaks with the following error message:

Error: Attempt to RPLACD a list that is embedded in a structure and
       therefore cannot be RPLACD'ed.  The list is (JUNK)

This is just plain WEIRD.  I can't understand how a list can be
"embedded" in a structure, and how that could possibly prevent me
from replacing its CDR cell.  I don't understand what structure
it is talking about, and why this could possibly work differently
when it's interpreted vs. when it's compiled.

How do I make pieces of portable code that perform collecting behavior?

John Myers      myers@atr-la.atr.co.jp