From: Ola Rinta-Koski
Subject: Setting variables from list
Date: 
Message-ID: <x5zo5hlmxv.fsf@arenal.cyberell.com>
  What should the definition of SET-FROM-LIST be if I want the
  following behaviour:

  (let ((foo (list 1 2 3)) bar baz bat)
    (set-from-list foo bar baz bat)
    (values bar baz bat))
  ==> 1; 2; 3

  This works:

  (defmacro set-from-list (list &rest vars)
    `(eval `(setf ,@(mapcan (lambda (x) (list x (pop ,list))) ',vars))))

  but I have a strong feeling there's a more elegant way to do this.
  Any ideas? (The list can be used up or left as is, doesn't matter.)
-- 
        Ola Rinta-Koski                                 ···@cyberell.com
        Cyberell Oy                                     +358 41 467 2502
        Rauhankatu 8 C, FIN-00170 Helsinki, FINLAND	www.cyberell.com

From: Dr. Edmund Weitz
Subject: Re: Setting variables from list
Date: 
Message-ID: <m3lmh17kpz.fsf@duke.agharta.de>
Ola Rinta-Koski <···@cyberell.com> writes:

>   What should the definition of SET-FROM-LIST be if I want the
>   following behaviour:
>
>   (let ((foo (list 1 2 3)) bar baz bat)
>     (set-from-list foo bar baz bat)
>     (values bar baz bat))
>   ==> 1; 2; 3
>
>   This works:
>
>   (defmacro set-from-list (list &rest vars)
>     `(eval `(setf ,@(mapcan (lambda (x) (list x (pop ,list))) ',vars))))
>
>   but I have a strong feeling there's a more elegant way to do this.
>   Any ideas? (The list can be used up or left as is, doesn't matter.)
> -- 
>         Ola Rinta-Koski                                 ···@cyberell.com
>         Cyberell Oy                                     +358 41 467 2502
>         Rauhankatu 8 C, FIN-00170 Helsinki, FINLAND	www.cyberell.com

There has been a thread about exactly this problem (with reversed
argument order) lately. It can be found at

  <http://groups.google.com/groups?hl=en&threadm=m3n137u8lu.fsf%40bird.agharta.de>

As I was the original poster, I have assembled a list of all proposals
that came in during this thread. See below.

HTH,
Edi.


;;; my (Edi Weitz <···@agharta.de>) first solution
;;; posted to comp.lang.lisp on October 4, 2001, as
;;; <···················@bird.agharta.de>
;; doesn't work correctly because EVAL works
;; in a null lexical environment and thus
;; this macro can't be placed inside of a LET form
(defmacro set-from-list (places-list value-list)
  `(progn
     ,@(mapcar #'(lambda (place value)
                   `(setf ,place ,value))
               places-list (eval value-list))))

;;; solution by Dr. Shlomo Argamon <·······@sunlightNOSPAM.cs.biu.ac.il>
;;; posted to comp.lang.lisp on October 4, 2001, as
;;; <····················@sunlight.i-have-a-misconfigured-system-so-shoot-me>
;; won't work with places, just with symbols.
;; also doesn't work inside of LET forms because
;; SET assigns the SYMBOL-VALUE not the lexical value.
(defmacro set-from-list (places-list value-list)
  (let ((var (gensym)))
    `(let ((,var ,value-list))
       (mapcar #'(lambda (place value)
                   (set place value))
                 (quote ,places-list) ,var))))

;;; solution by Kalle Olavi Niemitalo <···@iki.fi>
;;; posted to comp.lang.lisp on October 4, 2001, as
;;; <····················@stekt34.oulu.fi>
(defmacro set-from-list (places values-form)
  (let ((=values= (gensym)))
    `(let ((,=values= ,values-form))
       ,@(loop for place in places
               collect `(setf ,place (pop ,=values=))))))

;;; solution by Paul Foley
;;; sent to me by private email on October 4, 2001
(defmacro set-from-list (places-list values-list)
  `(mapc #'funcall (list ,@(mapcar (lambda (place)
                                     `(lambda (value) (setf ,place value)))
                                   places-list))
         ,values-list))

;;; solution by Kent M Pitman <······@world.std.com>
;;; posted to comp.lang.lisp on October 4, 2001, as
;;; <····················@world.std.com>
(defmacro set-from-list (places-list value-list)
  (let ((temps (mapcar #'(lambda (place) 
                           (declare (ignore place))
                           (gensym))
                       places-list)))
    `(destructuring-bind ,temps ,value-list
       (setf ,@(mapcan #'list places-list temps)))))

;;; Christopher Vogt <····@computer.org>
;;; suggested to look at PROGV in an article
;;; posted to comp.lang.lisp on October 4, 2001, as
;;; <······················@computer.org>

;;; solution by Kent M Pitman <······@world.std.com>
;;; posted to comp.lang.lisp on October 4, 2001, as
;;; <····················@world.std.com>
(defmacro set-from-list (places-list value-list)
  (let ((temps (mapcar #'(lambda (place) 
                           (declare (ignore place))
                           (gensym))
                       places-list)))
    `(destructuring-bind ,temps ,value-list
       (setf ,@(mapcan #'list places-list temps)))))

;;; solution by Kaz Kylheku <···@ashi.footprints.net>
;;; posted to comp.lang.lisp on October 5, 2001, as
;;; <···························@news1.rdc1.bc.home.com>
(defmacro set-from-list (places-list value-list)
 (if (and (listp value-list) (eq (first value-list) 'quote))
  `(setf ,@(mapcan #'(lambda (place value) 
                    `(,place ',value)) places-list (second value-list)))
  (let ((iterator (gensym)))
   `(let ((,iterator ,value-list))
     ,@(mapcan #'(lambda (place) 
                  `((setf ,place (car ,iterator))
                    (setf ,iterator (cdr ,iterator))))
       places-list)))))

;;; solution by Sam Steingold <···@gnu.org>
;;; posted to comp.lang.lisp on October 5, 2001, as
;;; <··················@xchange.com>
(defmacro set-from-list (places values)
  (let ((gens (mapcar (lambda (x) (declare (ignore x)) (gensym)) places)))
    `(destructuring-bind ,gens ,values
       (setf ,@(mapcan #'list places gens)))))


;;; Kalle Olavi Niemitalo <···@iki.fi> observed that none
;;; of the solutions above preserves the standard left-to-right
;;; evaluation order and provided a counter-example
;;; posted to comp.lang.lisp on October 4, 2001, as
;;; <···················@Astalo.y2000.kon.iki.fi>
;; all solutions following below try to overcome this problem
(let ((x '()))
  (set-from-list ((cdr (push 42 x))) (copy-list x))
  ;; which means: (setf (cdr (push 42 x)) (first x))
  x)  ; should be (42 . 42)

;;; solution by Kent M Pitman <······@world.std.com>
;;; posted to comp.lang.lisp on October 5, 2001, as
;;; <····················@world.std.com>
(defmacro set-from-list (places values)
  (let ((expansion-data-list (mapcar #'(lambda (place)
                                         (multiple-value-list 
                                          (get-setf-expansion place)))
                                     places)))
    `(let ,(mapcan #'(lambda (expansion-data)
                       (mapcar #'list
                               (first  expansion-data)
                               (second expansion-data)))
                   expansion-data-list)
       (destructuring-bind ,(mapcar #'(lambda (place expansion-data)
                                        (let ((vars (third expansion-data)))
                                          (unless (= (length vars) 1)
                                            (error "Can't SET-FROM-LIST ~S."
                                                    place))
                                          (first vars)))
                                    places expansion-data-list)
           ,values
         ,@(mapcar #'fourth expansion-data-list)))))

;;; my (Edi Weitz <···@agharta.de>) next trial
;;; posted to comp.lang.lisp on October 5, 2001, as
;;; <···················@bird.agharta.de>
(defmacro set-from-list (places values)
  (let ((=list-val= (gensym)))
    (let (vars forms var set access)
      (loop for place in places
            do (multiple-value-setq (vars forms var set access)
                   (get-setf-method place))
            append (mapcar #'list vars forms) into place-bindings
            collect `(,(car var) (pop ,=list-val=)) into temp-bindings
            collect set into assignments
            finally (progn
                      (return `(let* ,place-bindings
                                 (let ((,=list-val= ,values))
                                   (let* ,temp-bindings
                                     ,@assignments)))))))))

;;; another version of mine,
;;; also from <···················@bird.agharta.de>
;; tries to behave reasonably if PLACES and VALUES
;; have different length
(defmacro set-from-list (places values 
                                &key 
                                (fill-form nil supplied-fill-form-p))
  (let ((=list-val= (gensym))
        (=list-len= (gensym))
        (=excess-len= (gensym)))
    (let (vars forms var set access)
      (loop for place in places
            do (multiple-value-setq (vars forms var set access)
                   (get-setf-method place))
            append (mapcar #'list vars forms) into place-bindings
            collect `(,(car var) (pop ,=list-val=)) into temp-bindings
            collect `(when (>= (decf ,=list-len=) 0) ,set) 
                    into assignments
            finally (progn
                      (return `(let* ,(append place-bindings
                                              `((,=list-val= ,values)
                                                (,=list-len= 
                                                 (length ,=list-val=))))
                                 ,(if supplied-fill-form-p
                                      `(let ((,=excess-len= 
                                              (- ,(length places)
                                                 ,=list-len=)))
                                         (when (> ,=excess-len= 0)
                                           (setq ,=list-val=
                                                 (nconc ,=list-val=
                                                        (make-list 
                                                         ,=excess-len=
                                                         :initial-element
                                                         ,fill-form)))
                                           (incf ,=list-len=
                                                 ,=excess-len=))))
                                 (let* ,temp-bindings
                                   ,@assignments)
                                 ,=list-val=)))))))

;;; another solution by Kent M Pitman <······@world.std.com>
;;; posted to comp.lang.lisp on October 5, 2001, as
;;; <····················@world.std.com>
;; may be affected by MULTIPLE-VALUES-LIMIT
(defmacro set-from-list (places values)
  (let ((temps (mapcar #'(lambda (x) (declare (ignore x)) (gensym)) places)))
    ;; KLUDGE ALERTS:
    ;; (1) Tricks MULTIPLE-VALUE-SETQ into being MULTIPLE-VALUE-SETF
    ;; (2) Makes use of its special knowledge that each place will be used
    ;;     only once and in the right order, so intermediate temps are not
    ;;     needed explicitly to preserve left-to-right order nor single
    ;;     in the symbol-macrolet expansion.  (MULTIPLE-VALUE-SETQ has a 
    ;;     similar issue to solve again for itself which has to be solved
    ;;     differently.)
    `(symbol-macrolet ,(mapcar #'list temps places)
       (multiple-value-setq ,temps (values-list ,values)))))

;;; solution by Tim Moore <·····@bricoworks.com>
;;; posted to comp.lang.lisp on October 5, 2001, as
;;; <·················@216.39.145.192>
;; may also be affected by MULTIPLE-VALUES-LIMIT
;; but is otherwise the coolest solution IMHO
(defmacro set-from-list (places values)
  `(setf (values ,@places) (values-list ,values)))
From: Ola Rinta-Koski
Subject: Re: Setting variables from list
Date: 
Message-ID: <x5vgg5lkgz.fsf@arenal.cyberell.com>
···@agharta.de (Dr. Edmund Weitz) writes:
> Ola Rinta-Koski <···@cyberell.com> writes:
> >   What should the definition of SET-FROM-LIST be if I want the
> >   following behaviour:
> >
> >   (let ((foo (list 1 2 3)) bar baz bat)
> >     (set-from-list foo bar baz bat)
> >     (values bar baz bat))
> >   ==> 1; 2; 3

> There has been a thread about exactly this problem (with reversed
> argument order) lately.

  Thanks a lot. I decided to use this one:

> ;;; solution by Tim Moore <·····@bricoworks.com>
> ;;; posted to comp.lang.lisp on October 5, 2001, as
> ;;; <·················@216.39.145.192>
> ;; may also be affected by MULTIPLE-VALUES-LIMIT
> ;; but is otherwise the coolest solution IMHO
> (defmacro set-from-list (places values)
>   `(setf (values ,@places) (values-list ,values)))
-- 
        Ola Rinta-Koski                                 ···@cyberell.com
        Cyberell Oy                                     +358 41 467 2502
        Rauhankatu 8 C, FIN-00170 Helsinki, FINLAND	www.cyberell.com
From: Takehiko Abe
Subject: Re: Setting variables from list
Date: 
Message-ID: <keke-2011012356210001@solg4.keke.org>
In article <··············@arenal.cyberell.com>, Ola Rinta-Koski <···@cyberell.com> wrote:

> > >   What should the definition of SET-FROM-LIST be if I want the
> > >   following behaviour:
> > >
> > >   (let ((foo (list 1 2 3)) bar baz bat)
> > >     (set-from-list foo bar baz bat)
> > >     (values bar baz bat))
> > >   ==> 1; 2; 3
> 
> > There has been a thread about exactly this problem (with reversed
> > argument order) lately.
> 
>   Thanks a lot. I decided to use this one:
> 
> > ;;; solution by Tim Moore <·····@bricoworks.com>
> > ;;; posted to comp.lang.lisp on October 5, 2001, as
> > ;;; <·················@216.39.145.192>
> > ;; may also be affected by MULTIPLE-VALUES-LIMIT
> > ;; but is otherwise the coolest solution IMHO
> > (defmacro set-from-list (places values)
> >   `(setf (values ,@places) (values-list ,values)))

I'm not sure how you're going to use the macro, but if you use it
where destruturing-bind would suffice, e.g:

  (destructuring-bind (bar baz bat)
                      (list 1 2 3)
     ...    
     )

I would hate that.

abe

-- 
<keke at mac com>
From: Ola Rinta-Koski
Subject: Re: Setting variables from list
Date: 
Message-ID: <x5r8qtlaxz.fsf@arenal.cyberell.com>
····@ma.ccom (Takehiko Abe) writes:
> In article <··············@arenal.cyberell.com>, Ola Rinta-Koski <···@cyberell.com> wrote:
> > > >   (let ((foo (list 1 2 3)) bar baz bat)
> > > >     (set-from-list foo bar baz bat)
> > > >     (values bar baz bat))
> > > >   ==> 1; 2; 3

> I'm not sure how you're going to use the macro, but if you use it
> where destruturing-bind would suffice, e.g:
> 
>   (destructuring-bind (bar baz bat)
>                       (list 1 2 3)
>      ...    
>      )
> 
> I would hate that.

  No need to worry, I won't. The variables are already bound with a LET
  since I'm not doing the SET-FROM-LIST in all cases.
-- 
        Ola Rinta-Koski                                 ···@cyberell.com
        Cyberell Oy                                     +358 41 467 2502
        Rauhankatu 8 C, FIN-00170 Helsinki, FINLAND	www.cyberell.com