unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
From: Pascal Bourguignon <pjb@informatimago.com>
To: help-gnu-emacs@gnu.org
Subject: Re: Effective use of destructive functions
Date: Thu, 12 Apr 2007 09:31:07 +0200	[thread overview]
Message-ID: <877isikq10.fsf@voyager.informatimago.com> (raw)
In-Reply-To: 1176334962.373893.13990@w1g2000hsg.googlegroups.com

"achambers.home@googlemail.com" <achambers.home@googlemail.com> writes:

> Here's some code....
>
> (setq doc '(root (child 1 2) (child 3 4)))

If you plan to modify these conses,  you shouldn't let the reader
create them with quote.  Explicitely build these lists!

(setq doc (list 'root (list 'child 1 2) (list 'child 3 4)))


> (setq editable-child (car (cdr doc)))
> ;; (setq editable-child (mapcan (lambda (x)
> ;; 		       (cons x nil)) editable-child))
>
> (setcdr editable-child '(10 11))

Same with this one, if you plan to do a (setcar (cdr editable-child) 12),
use list:

  (setcdr editable-child (list 10 11))


> editable-child
> doc
>
> It seems that in the commented sexp, altering the symbol
> editable-child using setcdr has no effect on doc because the contents
> of editable-child is a copy of the contents of doc rather than a
> direct reference to a particular `place' in doc.
>
> This is a stripped down version of my real problem which is to
> creating a number of widgets out of the data in doc.  I want to make
> a :notify function for each widget that updates doc when an element of
> the tree is edited.
>
> My question is...How can you run the contents of editable-child
> through a mapping function, and keep the structure so that any changes
> to the return value carry on up to doc?

Indeed, mapcar builds a new list with the results of the function.

(let* ((a (list 1 2 3))
       (b (mapcar (lambda (x) (* 2 x)) a)))
  (list a b))
--> ((1 2 3) (2 4 6))

You wouldn't want to modify the list a when you build the list b...



In your case, you can modify each element of your list, without
touching the cdr of the cons cells of the list, by using map-into.

So instead of writing 

  (setq editable-child (mapcan (lambda (x) (cons x nil)) editable-child))

you'd write:

doc
--> (root (child 1 2) (child 3 4))

editable-child 
--> (child 1 2)

(map-into editable-child (function list) editable-child)
--> ((child) (1) (2))

doc
--> (root ((child) (1) (2)) (child 3 4))

editable-child
--> ((child) (1) (2))



Well, it's not in emacs AFAIK, so here is an emacs-cl implementation
of map-into; see  http://www.lispworks.com/documentation/HyperSpec/Body/f_map_in.htm    
for the specifications.


(require 'cl)
(require 'eieio)

(defclass iterator () ())
(defclass iterator-list   (iterator) ((head   :initarg :sequence :type list)))
(defclass iterator-vector (iterator) ((vector :initarg :sequence :type VECTOR)
                                      (index :initform 0)))


(defmethod end-of-sequence-p ((self iterator-list))
  (null (slot-value self 'head)))


(defmethod end-of-sequence-p ((self iterator-vector))
  (>= (slot-value self 'index) (length (slot-value self 'vector))))


(defmethod current-item ((self iterator-list))
  (car (slot-value self 'head)))


(defmethod current-item ((self iterator-vector))
  (aref (slot-value self 'vector) (slot-value self 'index)))


(defmethod set-current-item ((self iterator-list) value)
  (setf (car (slot-value self 'head)) value))


(defmethod set-current-item ((self iterator-vector) value)
  (setf (aref (slot-value self 'vector) (slot-value self 'index)) value))


(defmethod advance ((self iterator-list))
  (setf (slot-value self 'head) (cdr (slot-value self 'head))))


(defmethod advance ((self iterator-vector))
  (incf (slot-value self 'index)))


(defun map-into (result-sequence function &rest sequences)
  (cond
    ((every (function listp) sequences)
     (cond
       ((listp result-sequence)
        (do ((sequences sequences (mapcar (function cdr) sequences))
             (target result-sequence (cdr target)))
            ((or (null target) (some (function null) sequences)) result-sequence)
          (setf (car target) (apply function
                                    (mapcar (function car) sequences)))))
       ((vectorp* result-sequence)
        (do ((sequences sequences (mapcar (function cdr) sequences))
             (target 0 (1+ target)))
            ((or (>= target (length result-sequence))
                 (some (function null) sequences)) result-sequence)
          (setf (aref result-sequence target)
                (apply function (mapcar (function car) sequences)))))
       (t (error "RESULT-SEQUENCE is neither a LIST or a VECTOR."))))
    ((every (function vectorp*) sequences)
     (cond
       ((listp result-sequence)
        (do ((source 0 (1+ source))
             (min (apply (function min) (mapcar (function length) sequences)))
             (target result-sequence (cdr target)))
            ((or (null target) (>= source min)) result-sequence)
          (setf (car target) 
                (apply function (mapcar (lambda (seq) (aref seq source))
                                        sequences)))))
       ((vectorp* result-sequence)
        (do ((index 0 (1+ index))
             (min (apply (function min) (length result-sequence)
                         (mapcar (function length) sequences))))
            ((>= index min) result-sequence)
          (setf (aref result-sequence index)
                (apply function (mapcar (lambda (seq) (aref seq index))
                                        sequences)))))
       (t (error "RESULT-SEQUENCE is neither a LIST or a VECTOR."))))
    (t
     (do ((res
           (make-instance
               (cond 
                 ((listp    result-sequence) 'iterator-list)
                 ((vectorp* result-sequence) 'iterator-vector)
                 (t (error "RESULT-SEQUENCE is neither a LIST or a VECTOR.")))
             :sequence result-sequence))
          (sequences
           (mapcar
            (lambda (seq)
              (make-instance
                  (cond 
                    ((listp    seq) 'iterator-list)
                    ((vectorp* seq) 'iterator-vector)
                    (t (error "A SEQUENCE is neither a LIST or a VECTOR.")))
                :sequence seq)) sequences)))
         ((some (function end-of-sequence-p) (cons res sequences))
          result-sequence)
       (set-current-item res (apply function
                                    (mapcar (function current-item) sequences)))
       (dolist (seq (cons res sequences)) (advance seq))))))



(let ((result (make-list 10 0)))
   (print (map-into result (function +) '(1 2 3 4 5 6)
                                        '(100 200 300 400 500 600 700 800)))
   (print (map-into result (function *) result '(2 2 2 3 3 3)))
   (map-into result (lambda (x) (+ 1000 x)) result))

(101 202 303 404 505 606 0 0 0 0)

(202 404 606 1212 1515 1818 0 0 0 0)
(1202 1404 1606 2212 2515 2818 1000 1000 1000 1000)


-- 
__Pascal Bourguignon__
http://www.informatimago.com
http://pjb.ogamita.org

  reply	other threads:[~2007-04-12  7:31 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-04-11 23:42 Effective use of destructive functions achambers.home
2007-04-12  7:31 ` Pascal Bourguignon [this message]
2007-04-12 15:27   ` achambers.home

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=877isikq10.fsf@voyager.informatimago.com \
    --to=pjb@informatimago.com \
    --cc=help-gnu-emacs@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).