From: Johan Kullstam
Subject: loop collect into
Date: 
Message-ID: <m23cuakr1y.fsf@euler.axel.nom>
I am working with some convolutional codes and wanted to group the
table by the third element in each row.  I figured I'd put them into
an array indexed by this element since they are all numbers from 0
through 3.

Here is the table

(defvar table '((0 0 0 0)
		(0 1 2 5)
		(0 2 1 2)
		(0 3 3 7)
		(1 0 3 4)
		(1 1 1 1)
		(1 2 2 6)
		(1 3 0 3)
		(2 0 1 4)
		(2 1 3 1)
		(2 2 0 6)
		(2 3 2 3)
		(3 0 2 0)
		(3 1 0 5)
		(3 2 3 2)
		(3 3 1 7)))

This happens to work

(defvar a (loop :with code = (make-array 4 :initial-element nil)
		:for row :in table
		:for q = (third row)
		:do (push row (aref code q))
		:finally :return code))

I figured I'd be fancy but this won't work.  Can I have an array
reference instead of a variable as the argument of the into?

(defvar b (loop :with code = (make-array 4 :initial-element nil)
		:for row :in table
		:for q = (third row)
		:collect row :into (aref code q)
		:finally :return code))

In my particular case, the order doesn't matter so I am using the
first form.  If preserving order did matter, how do people go about
collecting into an array?

-- 
Johan KULLSTAM


-- 
Johan KULLSTAM

From: Jochen Schmidt
Subject: Re: loop collect into
Date: 
Message-ID: <ahktp0$gv1$1@rznews2.rrze.uni-erlangen.de>
Johan Kullstam wrote:

> 
> I am working with some convolutional codes and wanted to group the
> table by the third element in each row.  I figured I'd put them into
> an array indexed by this element since they are all numbers from 0
> through 3.
> 
> Here is the table
> 
> (defvar table '((0 0 0 0)
> (0 1 2 5)
> (0 2 1 2)
> (0 3 3 7)
> (1 0 3 4)
> (1 1 1 1)
> (1 2 2 6)
> (1 3 0 3)
> (2 0 1 4)
> (2 1 3 1)
> (2 2 0 6)
> (2 3 2 3)
> (3 0 2 0)
> (3 1 0 5)
> (3 2 3 2)
> (3 3 1 7)))
> 
> This happens to work
> 
> (defvar a (loop :with code = (make-array 4 :initial-element nil)
> :for row :in table
> :for q = (third row)
> :do (push row (aref code q))
> :finally :return code))

  :finally :return code 

since finally expects a "compound form" after it. You can use

  finally (return code)

instead

> I figured I'd be fancy but this won't work.  Can I have an array
> reference instead of a variable as the argument of the into?
> 
> (defvar b (loop :with code = (make-array 4 :initial-element nil)
> :for row :in table
> :for q = (third row)
> :collect row :into (aref code q)
> :finally :return code))

No it expects a symbol.

> In my particular case, the order doesn't matter so I am using the
> first form.  If preserving order did matter, how do people go about
> collecting into an array?

You could use an array and vector-push-extend:

(defun index-1 (table)
  (let ((code (make-array 4 :initial-element nil)))
    (flet ((collect (key item)
             (unless (aref code key)
               (setf (aref code key) (make-array 1 :fill-pointer 0
                                                 :adjustable t)))
             (vector-push-extend item (aref code key))))
     (dolist (row table) (collect (third row) row))
      code)))

Or you could simulate what loops collect does:

(defun index-2 (table)
  (let ((code (make-array 4 :initial-element nil)))
    (flet ((collect (key item)
             (if (aref code key) 
                 (let ((cell (aref code key)))
                      (setf (cdr cell) (setf (cddr cell) (list item))))
                 (let ((head (list item)))
                        (setf (aref code key) (cons head head))))))
     (dolist (row table) (collect (third row) row)) 
      (map-into code #'car code))))

You can test that both indices are equivalent with:

(every #'(lambda (a b) (every #'eq a b)) (index-1 *table*) (index-2 
*table*))

(defmacro with-indexer ((&key (test 'eql)) &body body)
  `(let ((index (make-hash-table :test ,test)))
       (flet ((collect (key item)
             (if (aref code key) 
                 (let ((cell (aref code key)))
                      (setf (cdr cell) (setf (cddr cell) (list item))))
                 (let ((head (list item)))
                        (setf (aref code key) (cons head head))))))
            ,@body)
       (maphash (lambda (key value) (setf (gethash key index) value)) 
index)))

Another interesting thing would be to use an hash-table instead of an array 
as index representation. The following macro does that:

(defmacro with-indexer ((&key (test 'eql)) &body body)
  (let ((index (gensym)))
  `(let ((,index (make-hash-table :test ',test)))
       (flet ((collect (key item)
             (if (gethash key ,index) 
                 (let ((cell (gethash key ,index)))
                      (setf (cdr cell) (setf (cddr cell) (list item))))
                 (let ((head (list item)))
                        (setf (gethash key ,index) (cons head head))))))
            ,@body)
       (maphash (lambda (key value) (setf (gethash key ,index) (car value))) 
,index)
       ,index)))

You could then write your indexer this way:

(defun index-3 (table)
  (with-indexer ()
     (dolist (row table) (collect (third row) row))))

ciao,
Jochen

--
http://www.dataheaven.de
From: Jochen Schmidt
Subject: Re: loop collect into
Date: 
Message-ID: <ahku4q$h63$1@rznews2.rrze.uni-erlangen.de>
Sorry for the mangled last post...

Jochen Schmidt wrote:

>> (defvar a (loop :with code = (make-array 4 :initial-element nil)
>> :for row :in table
>> :for q = (third row)
>> :do (push row (aref code q))
>> :finally :return code))
> 
>   :finally :return code
> 
> since finally expects a "compound form" after it. You can use
> 
>   finally (return code)
> 
> instead

I wanted to say: :finally return code
is actually illegal loop syntax (at least it represents not what you meant 
it to be). You should use :finally (return code) to get what you wanted.

--
http://www.dataheaven.de
From: Johan Kullstam
Subject: Re: loop collect into
Date: 
Message-ID: <m2y9c2j5eo.fsf@euler.axel.nom>
Jochen Schmidt <···@dataheaven.de> writes:

> Sorry for the mangled last post...
> 
> Jochen Schmidt wrote:
> 
> >> (defvar a (loop :with code = (make-array 4 :initial-element nil)
> >> :for row :in table
> >> :for q = (third row)
> >> :do (push row (aref code q))
> >> :finally :return code))
> > 
> >   :finally :return code
> > 
> > since finally expects a "compound form" after it. You can use
> > 
> >   finally (return code)
> > 
> > instead
> 
> I wanted to say: :finally return code
> is actually illegal loop syntax (at least it represents not what you meant 
> it to be). You should use :finally (return code) to get what you
> wanted.

thanks for your help.  funny but :finally :return code does end up
pretty much giving me what i wanted.  i have consulted the aluminium
book (hyperspec is painful to use to learn loop -- imho too many tiny
sections, poor linking between them) and see that :finally (return
code) would be better.

-- 
Johan KULLSTAM
From: Jochen Schmidt
Subject: Re: loop collect into
Date: 
Message-ID: <ahl14d$ih9$1@rznews2.rrze.uni-erlangen.de>
Johan Kullstam wrote:

> Jochen Schmidt <···@dataheaven.de> writes:
> 
>> Sorry for the mangled last post...
>> 
>> Jochen Schmidt wrote:
>> 
>> >> (defvar a (loop :with code = (make-array 4 :initial-element nil)
>> >> :for row :in table
>> >> :for q = (third row)
>> >> :do (push row (aref code q))
>> >> :finally :return code))
>> > 
>> >   :finally :return code
>> > 
>> > since finally expects a "compound form" after it. You can use
>> > 
>> >   finally (return code)
>> > 
>> > instead
>> 
>> I wanted to say: :finally return code
>> is actually illegal loop syntax (at least it represents not what you
>> meant it to be). You should use :finally (return code) to get what you
>> wanted.
> 
> thanks for your help.  funny but :finally :return code does end up
> pretty much giving me what i wanted.  i have consulted the aluminium
> book (hyperspec is painful to use to learn loop -- imho too many tiny
> sections, poor linking between them) and see that :finally (return
> code) would be better.

As far as I know it seems to work in some of the LOOP implementations I know 
of  (It works in CLISP and Lispworks but it does _not_ work in CMUCL).

Regardless of that it is against of what is specified in the ANSI standard 
and is therefore unreliable behaviour if you count on it.

ciao,
Jochen

--
http://www.dataheaven.de
From: Thomas F. Burdick
Subject: Re: loop collect into
Date: 
Message-ID: <xcv3cu90zb6.fsf@conquest.OCF.Berkeley.EDU>
Johan Kullstam <··········@attbi.com> writes:

> (defvar b (loop :with code = (make-array 4 :initial-element nil)
> 		:for row :in table
> 		:for q = (third row)
> 		:collect row :into (aref code q)
> 		:finally :return code))
> 
> In my particular case, the order doesn't matter so I am using the
> first form.  If preserving order did matter, how do people go about
> collecting into an array?

Generally for collcting when I want to return a vector, I'll do:

  (loop ...
        collect ... into results
        ...
        finally (return (coerce results 'vector)))

However, what you mean by collect in the above pseudo-LOOP isn't
really collect at all.  You're not wanting to collect a list, you want
to set a certain entry in an array.  So the fourth line should be

  do (setf (aref code q) row)

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Jochen Schmidt
Subject: Re: loop collect into
Date: 
Message-ID: <ahmrdh$q03$1@rznews2.rrze.uni-erlangen.de>
Thomas F. Burdick wrote:

> Johan Kullstam <··········@attbi.com> writes:
> 
>> (defvar b (loop :with code = (make-array 4 :initial-element nil)
>> :for row :in table
>> :for q = (third row)
>> :collect row :into (aref code q)
>> :finally :return code))
>> 
>> In my particular case, the order doesn't matter so I am using the
>> first form.  If preserving order did matter, how do people go about
>> collecting into an array?
> 
> Generally for collcting when I want to return a vector, I'll do:
> 
>   (loop ...
>         collect ... into results
>         ...
>         finally (return (coerce results 'vector)))
> 
> However, what you mean by collect in the above pseudo-LOOP isn't
> really collect at all.  You're not wanting to collect a list, you want
> to set a certain entry in an array.  So the fourth line should be
> 
>   do (setf (aref code q) row)

He actually wanted to "collect" all rows into lists which are accessible 
through the array. But since collect only works on variables denoted as a 
symbol (and not for example (aref code q) ) this would not work.

See my other post on how one can do it.

ciao,
Jochen

--
http://www.dataheaven.de