3

I understand that (eq '(3 . d) '(3 . d)) returns nil, and (eq 3 3) returns t. But I do not understand why delq is deleting one (3 . d) in the second example each loop when y is (3 . d). Is mapc placing some type of invisible symbol on one of the (3 . d) but not the others such that only one (1) of them in the sequence is eq?

(let* ((original '(0 1 2 3 3 3 3 4 5 6))
       (copy (copy-list original)))
  (mapc (lambda (x)
          (mapc (lambda (y)
                  (message "y: %s | copy: %s" y copy)
                  (setq copy (delq y copy)))
                copy))
        original))

In the second example, one (3 . d) is deleted each loop when y is (3 . d).

(let* ((original '((0 . a) (1 . b) (2 . c) (3 . d) (3 . d)
                   (3 . d) (3 . d) (4 . e) (5 . f) (6 . g)))
       (copy (copy-list original)))
  (mapc (lambda (x)
          (mapc (lambda (y)
                  (message "y: %s | copy: %s" y copy)
                  (setq copy (delq y copy)))
                copy))
        original))

Note: copy-sequence is an alternative to copy-list a.k.a. cl-copy-list (defined in cl.el).

lawlist
  • 18,826
  • 5
  • 37
  • 118
  • `C-h S copy-sequence` detailedly explains the "copy" behavior, though unlike `copy-list`, it doesn't work for dotted list. – xuchunyang Jan 03 '17 at 19:39

1 Answers1

2

I think you basically explained what's going on when you explained the eq business: In the first loop, the first time you hit a 3, delq removes all the 3s, because they are eq to each other. In the second loop, each time you hit a (3 . d), the call to delq removes that particular (3 . d) and not the other (3 . d) because they are not eq to each other.

I guess you want to know how Emacs implements cons cells and eq to make different occurrences of (3 . d) not be eq to each other. Every time you evaluate a (3 . d), Emacs allocates some fresh, unused portion of memory for a cons cell and sets the car to 3 and the cdr to d, for cons cells eq tests whether or not they are stored in the same location in memory (if you are familiar with pointers, as they occur in C, for example, you'd say that for cons cells eq performs a pointer equality test). So there's no "extra symbol" required to make eq work that way.

(Strictly speaking to explain the results you also need to know that copy-list allocates new space for the list it returns, but reuses exactly the same contents as the input list.)

Omar
  • 4,732
  • 1
  • 17
  • 32
  • Would it be accurate for me state?: "The active element during each `mapc` loop will always be `eq` to the corresponding element of the SEQUENCE, even if the `eq` test would otherwise fail outside of a `mapc` context. The active element may be `eq` to more than one element if they would otherwise be `eq`." – lawlist Jan 03 '17 at 19:40
  • 3
    This has nothing to do with `mapc`. The expression `(eq '(d . 3) '(d . 3))` *may* evaluate to true because the Lisp-reader *may* create only one copy for multiple equal cells. You can see this with something like `(progn (setq x '(a . 0) y '(a . 0)) (setcar x 'b) y)`. – politza Jan 03 '17 at 21:09
  • See politza's comment answering yours, @lawlist (I think he or she forgot to ping you). – Omar Jan 04 '17 at 04:23
  • @politza In CL `(eq '(d . 3) '(d . 3))` can return `t`. It is detailed in the CL specification that equal constant lists can be identified. Whether that is the case for Elisp is currently an open question. See https://emacs.stackexchange.com/q/45820/2370. For me it looks like that is unspecified for Elisp. Note that the evaluation of your example `(progn (setq x '(a . 0) y '(a . 0)) (setcar x 'b) y)` in an Org Elisp-source code block returns `(a . 0)`. – Tobias Nov 27 '18 at 17:34