arrayslispcommon-lispsbclcons

Two cons point to same memory when mapping over array in Common Lisp


I have the following function:

(defun transform-matrix (matrix)
   (let ((res (map 'vector
                   (lambda (x)
                       (map 'vector
                            (lambda (ix)
                                (if ix
                                    '(t 0) ; --> Problem happens here
                                    0))
                            x))
                   matrix)))
    res))

This function will accept a 2d matrix, in which each element can either be t or nil. Then it will transform t -> '(t 0) and nil -> 0.

The result array has one problem is every (t 0) cons now point to the same memory location. For example if i save the result array in res variable and do this:

(eq (aref (aref res 0) 0)
    (aref (aref res 1) 1))

*Assume that res[0][0] and res[1][1] is the '(t, 0) nodes.

This will result in T. But do like this is result in nil:

(eq '(t 0) '(t 0))

Can i ask what happen with the transform-matrix that make created cons to point to the same memory location.

I test these codes on SBCL 2.0.0 Windows 64 Bit.

Thank you.


Solution

  • One way to see the problem here is to change your function to this:

    (defun transform-matrix (matrix)
      (let ((non-nil-value '(t 0))
            (nil-value 0))
        (map 'vector
             (lambda (x)
               (map 'vector
                    (lambda (ix)
                      (if ix non-nil-value nil-value))
                    x))
             matrix)))
    

    It should be clear that this code is functionally identical: both functions have a single occurrence of '(t 0): this one just gives it a name.

    But now let's gut this function and consider this:

    (defun ts ()
      (let ((non-nil-value '(t 0)))
        (eq non-nil-value non-nil-value)))
    

    Well, certainly I would expect the result of calling this function to be t.

    And that's why every element in your resulting nested vectors which isn't 0 is the same: because you only ever constructed one object.

    If you want all of the objects in the returned value to be different objects (ie not to be identical), you need to construct a new object each time, for instance by:

    (defun transform-matrix (matrix)
      (let ((non-nil-template '(t 0)))
        (map 'vector
             (lambda (x)
               (map 'vector
                    (lambda (ix)
                      (if ix (copy-list non-nil-template) 0))
                    x))
             matrix)))
    

    This will ensure that each non-zero element of the resulting object

    Neither of these were previously true.

    For the case of (eq '(t 0) '(t 0)) you might expect that this must return nil. That is (I think definitely) the case in interpreted code. But for compiled code the answer is not so clear. At least for the file compiler, it's fairly clear that in fact this may return t. Section 3.2.4.4 of the spec says, in part

    If two literal objects appearing in the source code for a single file processed with the file compiler are the identical, the corresponding objects in the compiled code must also be the identical. With the exception of symbols and packages, any two literal objects in code being processed by the file compiler may be coalesced if and only if they are similar; if they are either both symbols or both packages, they may only be coalesced if and only if they are identical.

    And in (eq '(t 0) '(t 0)), there are two literal lists, which are similar, and which therefore may be coalesced by the file compiler.

    As a general rule, if you want a mutable object, or an object which you need to be certain is not identical to any other object, you should construct it explicitly: it is never safe (or even legal) to mutate literal objects, and the rules on which objects can be coalesced are complex enough that it is generally just safer to construct the objects so you know what is happening.


    As an aside is there a reason you are using nested vectors rather than a two-dimensional matrix?