R: "Look, in the sky! The Lambda Signal! A citizen is in trouble Lambda Man!"
LM: "I see it! And I've got just the box art they need."
In Lisps, lists are singly-linked data structures, comprised of elements called cons cells. Each of these cells is a structure that consists of
- a pointer to a value
- a pointer to the next cell
These are called the car and cdr respectively for historical reasons. Here's the traditional box art representing a 3-element list:
Structure: (car . cdr -)--->(car . cdr -)--->(car . cdr)
| | | |
v v v v
Values: 1 2 3 nil
The car
and cdr
functions allow you to work with lists from this low level of abstraction, and return the values of the respective cells. Thus, car
returns the 'value' of the cell, and cdr
dereferences to the remainder of the list. nthcdr
is a generalisation on top of cdr
for convenience.
The value returned by cdr
is a reference to a raw data structure, which is mutable at this level. If you change the value of a cons-cell's cdr, you are changing the underlying structure of the list.
Given:
let A = '(1 2) ~= (1 . -)-->(2 . nil)
let B = '(3 4) ~= (3 . -)-->(4 . nil)
Setting the cdr of (cdr A)
to B will destructively concatenate A and B such that A is now the structure below:
A B
(1 . -)-->(2 . -)-->(3 . -)-->(4 . nil)
As we've shown, a nil
value in a cell's cdr represents the end of the list - there's nothing more that can be traversed. If we set the cdr of A to nil
, we lobotomise the list, such that A is now
A
(1 . nil) <- [Not pointing to anything - the rest of the list shall go wanting]
This is pretty much what you've done - you've mutated the underlying data structures using low-level functions. :) By setting one of the cell's cdrs to nil
, you've trimmed the end off your list.