This is a read-only archive of lispforum.com. The forum was locked to new users and posts and is preserved here as static HTML from a database snapshot taken on 2019-09-07.

pushing links, not elements

16 posts · 13416 views

Hi there,

I am just starting with lisp and I can't figure out the following issue:

I want to push *not* elementos into a list, but its *references*, for example, if I write
(let (
      (a '())
      (b '()))
  (push 1 a)
  (push a b)
  (push 2 a)
  (print a)
  (print b))
I get
(2 1)                                                                     
((1))
and I would like
(2 1)                                                                     
((2 1))
so, each time I modify a, b has to change too.
Is there a way to achieve that?

thanks!!

Re: pushing links, not elements

In general, I see no way to achieve this.

Assume a=(0). When you push 1 to a, you will exchange a by (1 . (0)), then you replace b by ((1 . (0)) . b) and then you replace a by (2 . (1 . (0))).

You have to try it in a different way. One thing I could think of is to write an own push-macro which pushes the values on both lists. You could try to use structs which point to another object, and set this pointer to another value. Or to implement a similar data structure by your own.

I think there is no "trivial" way to do this.
Sorry for my bad english.
Visit my blog http://blog.uxul.de/

Re: pushing links, not elements

Lisp is doing exactly as you ask... but not what you want.

Unfortunately, push *prepends* an object onto the list; you want appending behavior.

If [xy] is a cons cell and _ is nil, then a trace of your code looks like
a=_
b=_
a=[1a]=[1_]
b=[ab]=[[1_]_]
a=[2a]=[2[1_]]
where the lisp printer hides nil when it ends a list.

In the last step, A now points to a different object; B still points to the old A. If you want B to see the new definition of A, you must change the structure A refers to, not simply bind a new value to A...

Compare the following snippets
(let ((list (list 1 2))) (push 3 list))
(let ((list (list 1 2))) (nconc list (cons 3 nil)))

Try defining a push-back macro based on the second snippet...

- Daniel

Re: pushing links, not elements

Let's look at how the cons cells point after each part:
(let ((a '())
      (b '()))
       
a --->  nil


b --->  nil
  (push 1 a)
        _______
a ---> |  1 | ---> nil
        ~~~~~~~

b ---> nil
  (push a b)
        _______
a ---> |  1 | ---> nil
        ~~~~~~~
          ^
        __|____
b ---> |  ! | ---> nil
        ~~~~~~~
  (push 2 a)
        _______     _______
a ---> |  2 | ---> |  1 | ---> nil
        ~~~~~~~     ~~~~~~~
                      ^
                    __|____
            b ---> |  ! | ---> nil
                    ~~~~~~~
  (print a)
  (print b))
Now the output should be no surprise.
"Just throw more hardware at it" is the root of all evil.
Svante

Re: pushing links, not elements

Thank you for your replys,

I am sorry for the confusion, I know that I am getting what I am suppose, my question was if there is a way to handle something like pointers in C, which is normally done using * and &.
I guess you achieve that by a correct use of car's and cdr's. Because as nuntilus (aka daniel) suggest, doing
(let (
      (a (list 1 2))
      (b '(bb)))
  (nconc b a)
  (nconc a '(1 2 3))
  (print a)
  (print b)
  )
gives
(1 2 1 2 3)                                                                                                                                                  
(BB 1 2 1 2 3)
which is exactly what I wanted.
TY!!!!

Re: pushing links, not elements

No references/pointers, although, often you should not be using them, i think lisp should have them. I have a project where i am working on a lisp with (among others) a better typing system, and i will put references/pointers in there.

Re: pushing links, not elements

You can create/hack pointers and references in. For example, you could do something like: (untested)
(defmacro make-pointer (variable)
  (let ((op (gensym))
        (value (gensym)))
    `(lambda (,op &optional ,value)
       (if (eql ,op 'set)
         (setf ,variable ,value)
         ,variable))))

(defun deref (pointer)
  (funcall pointer 'read))

(defun (setf deref) (value pointer)
  (funcall pointer 'set value))

Re: pushing links, not elements

Cool, didn't think of that, how does that compare performance-wise? You happen to know how loop/dolist do references?

Btw greatly miss with-gensyms, it should be in standard library, imo. (Same as a lot more macros/functions.)

Re: pushing links, not elements

I think the better here would be to use a one-field structure
(defstruct (pointer (:conc-name nil))
  ref)
Or to make it look like C (in a good way)
(defstruct (pointer (:conc-name nil))
  &)

(let ((x (make-pointer :& 10)))
  (print (& x)) ; prints 10
  (setf (& x) 20)
  (& x)) ==> 20
I believe using structures would be much more clean and have better performance.

Anyway, a list is lisp is in fact a pointer, just like it would be written in C. I don't know if this is right, it's been a while...
defstruct cons_cell {
  int car;
  cons_cell *cdr;
};

deftype cons_t *cons_cell;

cons_t cons(int a, cons_t b) {
  ... malloc (something); ...
}

int main() {

  cons_t a = NULL;
  cons_t b = NULL;

  a = cons(1, a);      // a points to a cons (1, NULL)
  b = cons(a, b);      // the car of b points to the same cons above
  a = cons(2, a);      // the car of b doesn't change, it still points to the same cons as before

}

Re: pushing links, not elements

Hmm while i was looking for a way to find the arguments of a macro i stumbled upon symbol-macrolet, the following should be able to prevent people needing to deref: (untested)
(defmacro these-deref ((&rest vars) &rest body)
  `(symbol-macrolet (,@(loop for v in vars collect `(,v (deref ,v)))) ,@body)
(defmacro pointerize ((&rest vars)) ;Rest here is just handy fluff.
  `(let (,@(loop for v in vars collect (if (listp v) `(,(car v) (make-pointer ,(cadr v))) `(,v (make-pointer nil))))
      (these-deref (,@(loop for v in vars collect (if (listp v) (car v) v)))
         ,@body))
(defmacro pointerize-var ((&rest vars) &body body)
  `(pointerize (,@(loop for v in vars collect `(,v ,v)) ,@body)) 
@gugamilare: i associate using the first element of a list as a reference with pain. That might be because programming non-functionally can cause trouble, or just because i was not as good a programmer back then, though.

Re: pushing links, not elements

Jasper wrote:Cool, didn't think of that, how does that compare performance-wise? You happen to know how loop/dolist do references?
Well, it would have to allocate a closure upon creation, and do a function call and comparison for reading/setting, instead of just setting a memory location like you would with a lexical variable. DOLIST and LOOP create lexical bindings (just look at their macroexpansion), so there is nothing special going on there.
Btw greatly miss with-gensyms, it should be in standard library, imo. (Same as a lot more macros/functions.)
I believe Alexandria has with-gensyms and a bunch of other great stuff.

Re: pushing links, not elements

Of course, i just made a file with the macros i need.(But prob with posting them online is that other people don't have them) I will certainly check it out, should be interesting how my (rather small) set of macro/functions compare to those of alexandria.

Update: Ok, alexandria looks good, why (defmacro with-gensyms (names &body forms).. vs the imo more representative (defmacro with-gensyms ((&rest names) &body forms)).. It doesnt accept lone symbols here either. (Being a little pedantic here)

Also, i can see if-let and when-let, it seems like a good idea. I used single variables and if-with, when-with, case-with, *-let does not extend to case-let well, though. I don't see anything like
(defmacro if-with (var cond if-t &optional (if-f nil)) `(let ((,var ,cond)) (if ,var ,if-t ,if-f)))
(defmacro if-use (cond &optional if-false) (with-gensyms (var) `(if-with,var ,cond ,var ,if-false)))
(defmacro setf- (operator set &rest args) `(setf ,set (,operator ,set ,@args)))
And anything like and*, or*, where order of execution is assured? I guess those last three are inherently not functional, maybe we should avoid them. I do use them though :-/, should write functionally more, although function callbacks seem like a good idea in many cases.

Lastly, is there an argumentize-list ((&rest arguments) list &body body) macro? Here it makes and sets variables based on arguments, which are just like that defmacros. I made one but currently it can't make keywords from symbols, and &optional doesn't work. I am planning to make a better version; though, working on another thing, i need to parse the arguments anyway.

Re: pushing links, not elements

Jasper wrote: Lastly, is there an argumentize-list ((&rest arguments) list &body body) macro? Here it makes and sets variables based on arguments, which are just like that defmacros.
You mean like DESTRUCTURING-BIND?

Re: pushing links, not elements

Jasper wrote:Also, i can see if-let and when-let, it seems like a good idea. I used single variables and if-with, when-with, case-with, *-let does not extend to case-let well, though. I don't see anything like
(defmacro if-with (var cond if-t &optional (if-f nil)) `(let ((,var ,cond)) (if ,var ,if-t ,if-f)))
(defmacro if-use (cond &optional if-false) (with-gensyms (var) `(if-with,var ,cond ,var ,if-false)))
(defmacro setf- (operator set &rest args) `(setf ,set (,operator ,set ,@args)))
Try http://common-lisp.net/project/anaphora/. It is a pretty good library in this sense. ;)
Jasper wrote:Lastly, is there an argumentize-list ((&rest arguments) list &body body) macro? Here it makes and sets variables based on arguments, which are just like that defmacros. I made one but currently it can't make keywords from symbols, and &optional doesn't work. I am planning to make a better version; though, working on another thing, i need to parse the arguments anyway.
As said before, try destructuring-bind. It is ANSI, don't need a library.

Re: pushing links, not elements

I just remembered why i associated using lists as reference as causing trouble, If x is a variable, (list x) does not behave as a reference to x, since x is passed by value to list.
(let ((x 1))
  (flet ((set-two (ref) (setf (car ref) 2)))
    (set-two (list x)))
  x)  => 1

Re: pushing links, not elements

Jasper wrote:I just remembered why i associated using lists as reference as causing trouble, If x is a variable, (list x) does not behave as a reference to x, since x is passed by value to list.
(let ((x 1))
  (flet ((set-two (ref) (setf (car ref) 2)))
    (set-two (list x)))
  x)  => 1
But here comes a distinction. I didn't exactly say that the car of a cons is a reference. I said that the cons itself is a reference (actually, two references, one for its car and the other for its cdr). Numbers (yes, including big integers), characters and symbols (including keywords) - am I forgetting something? - are never references in lisp because they are immediate values (or at least treated as such). Actually, in your example, if x were a list, the same would happen, although the car of (list x) is a reference.
The problem is that the reference is always to the value itself, not to the address of the value.

It looks like to me that you came from imperative languages, and that is the reason you are confused. Let me try to explain this in C world.
ECL is implemented more or less the following way: there is a type definition named cl_object which is essentially a union type. It can represent an integer (I mean the C int) or a pointer. The lower two bits of the some cl_object is a tag which tells what kind of object it represents: It can be a fixnum, a character, a pointer or an internal constant (I would guess, T, NIL and some others).

When the lower 2 tags say that the element is a fixnum, to obtain the fixnum, you shift the cl_object to the right by 2. Like this:
cl_object obj;

if FIXNUMP(obj) // this checks the two lower tags and tell if they match the fixnum tag
  return ((int) obj) >> 2;
else
  die("obj is not an integer");
You obtain the character the exact same way, shifting the object by two. If the element is a list, this is what it does to obtain the car and cdr:
cl_object obj, car, cdr;
ecl_cons *cons;

if CONSP(obj) {
  cons = (ecl_cons *) ( ((int) obj) >> 2);
  car = cons->cons.car;
  cdr = cons->cons.cdr;
}
else
  die("obj is not a cons");
Well, at least for me, now it is clear why (setf (car (list x)) 2) does not affect x. You will have x declared as
int x;
and set (list x)'s car with something like
(list_x -> cons.car) = 2;
, which would not affect x. But, in the following example:
(let ((x (list 1)))
  (setf (car (car (list x))) 2)
  x) => (2)
You would have:
ecl_cons* x, car_list_x;

// some other declarations and evaluations which are not so important.

_car_list_x = list_x -> cons.car;
car_list_x = (ecl_cons *) (_car_list_x >> 2); // don't mind this line, it just fetches the real car as a cons object.

// right now car_list_x is pointing to the same place that x is pointing.
// It is just like you had evaluated car_list_x = x.
// Therefore, the next line will change the car of x.

(car_list_x -> cons.car) = 2;
This is just an example, you should never use this stuff explicitly, but instead you should use the macros ECL's provides if you want to do this. But I hope this is useful for understanding the behavior of Lisp itself.
qbg wrote:
Jasper wrote:Cool, didn't think of that, how does that compare performance-wise? You happen to know how loop/dolist do references?
Well, it would have to allocate a closure upon creation, and do a function call and comparison for reading/setting, instead of just setting a memory location like you would with a lexical variable. DOLIST and LOOP create lexical bindings (just look at their macroexpansion), so there is nothing special going on there.
I have some pain in my stomach whenever I create function just to return some value when it just won't compute anything. Some simple tests in SBCL makes me believe using functions instead of structures gives about 40% of time impact. But using functions here make the place to be generalizable - with a function, you can create a reference to a field of a class - and changing the value "referenced" by the function would be able to change the value of the slot.

If I were implementing this, I would create the structure, and make the accessor "&" (or "deref") to work with both versions, just in case.