2

I want to save a reference (pointer) to a part of some Data I saved in another variable:

(let ((a (list 1 2 3)))
  (let ((b (car (cdr a)))) ;here I want to set b to 2, but it is set to a copy of 2
    (setf b 4))
  a) ;evaluates to (1 2 3) instead of (1 4 2)

I could use macros, but then there would ever be much code to be executed if I want to change some Data in the middle of a list and I am not very flexible:

(defparameter *list* (create-some-list-of-arrays))
(macrolet ((a () '(nth 1000 *list*)))
  (macrolet ((b () `(aref 100 ,(a))))
    ;; I would like to change the macro a here if it were possible
    ;; but then b would mean something different
    (setf (b) "Hello")))

Is it possible, to create a variable as a reference and not as a copy?

PuercoPop
  • 6,707
  • 4
  • 30
  • 40
porky11
  • 604
  • 5
  • 16
  • 3
    What is wrong with (let ((a (list 1 2 3))) (setf (second a) 4) a) ? – Baggers Apr 03 '14 at 11:47
  • 1
    I highly recommend reading this http://www.gigamonkeys.com/book/variables.html and focus on the details of values, variables and places. Sorry if you are not a newbie to the language, but if you are then do remember that mapping techniques from other languages wont necessarily give you good or fast code. – Baggers Apr 03 '14 at 11:53
  • 2
    You'll probably be interested in the answer to [How do I globally change a variable value within function in lisp](http://stackoverflow.com/q/19491275/1281433) which discusses C pointers, indirection through structures (including cons cells), generalized references, and simulating C-style pointers. The question isn't quite the same, but I think the answer might be what you need. – Joshua Taylor Apr 03 '14 at 13:48
  • 2
    The first code snippet there is undefined behaviour. lists defined using quote are constants and the effect of modifying a constant is undefined. If you want to modify the list create it with list – PuercoPop Apr 03 '14 at 19:13

3 Answers3

8
cl-user> (let ((a '(1 2 3)))
           (let ((b (car (cdr a))))
             (setf b 4))
           a)
;Compiler warnings :
;   In an anonymous lambda form: Unused lexical variable B
(1 2 3)

A cons cell is a pair of pointers. car dereferences the first, and cdr dereferences the second. Your list is effectively

  a -> [ | ] -> [ | ] -> [ | ] -> NIL
        |        |        |
        1        2        3

Up top where you're defining b, (cdr a) gets you that second arrow. Taking the car of that dereferences the first pointer of that second cell and hands you its value. In this case, 2. If you want to change the value of that pointer, you need to setf it rather than its value.

cl-user> (let ((a '(1 2 3)))
           (let ((b (cdr a)))
             (setf (car b) 4))
           a)
(1 4 3)
Inaimathi
  • 13,853
  • 9
  • 49
  • 93
4

If all you need is some syntactic sugar, try symbol-macrolet:

(let ((a (list 1 2 3 4)))
  (symbol-macrolet ((b (car (cdr a))))
    (format t "~&Old: ~S~%" b)
    (setf b 'hello)
    (format t "~&New: ~S~%" b)))

Note, that this is strictly a compile-time thing. Anywhere (in the scope of the symbol-macrolet), where b is used as variable, it is expanded into (car (cdr a)) at compile time. As Sylwester already stated, there are no "references" in Common Lisp.

I wouldn't recommend this practice for general use, though.

And by the way: never change quoted data. Using (setf (car ...) ...) (and similar) on a constant list literal like '(1 2 3) will have undefined consequences.

Dirk
  • 30,623
  • 8
  • 82
  • 102
1

Building on what Baggers suggested. Not exactly what you are looking for but you can define setf-expanders to create 'accessors'. So lets say your list contains information about people in the for of (first-name last-name martial-status) and when someone marries you can update it as:

(defun marital-status (person)
  (third person))

(defun (setf marital-status) (value person)
  (setf (third person) value))

(let ((person (list "John" "Doe" "Single")))
  (setf (marital-status person) "Married")
  person)
;; => ("John" "Doe" "Married")
PuercoPop
  • 6,707
  • 4
  • 30
  • 40