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.
Code: Select all
(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:
Code: Select all
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:
Code: Select all
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
and set (list x)'s car with something like
, which would not affect x. But, in the following example:
Code: Select all
(let ((x (list 1)))
(setf (car (car (list x))) 2)
x) => (2)
You would have:
Code: Select all
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.