7

In SICP exercise 2.26, this Scheme code is given:

(define x (list 1 2 3))
(define y (list 4 5 6))

Then this cons call is given:

(cons x y)

I expected a pair of lists would result, ((1 2 3) (4 5 6)) but the interpreter gives, ((1 2 3) 4 5 6) ...a list with 4 elements, the first being a list. Why is y treated differently? I've tried looking up other SICP answers for an explanation, but couldn't find something satisfactory. So could any Scheme/Lisp experts please shed some light on this aspect of cons? Thanks in advance for any insight.

jjnguy
  • 136,852
  • 53
  • 295
  • 323
limist
  • 1,288
  • 2
  • 16
  • 29
  • 1
    Thank you to all responders to my question, particularly Nathan and tonio. This newbie in Scheme/Lisp now groks the language a bit better because of your detailed answers. – limist May 28 '10 at 14:18
  • Also see [Recursive range in Lisp adds a period?](http://stackoverflow.com/q/16379657/1281433) for a similar explanation. – Joshua Taylor Dec 14 '13 at 15:28

6 Answers6

14

'((1 2 3) 4 5 6) is actually a pair of lists. Here's another way to write it:

'((1 2 3) . (4 5 6))

However, the printer avoids dotted pair notation whenever it can, so you get the first representation instead. The rule is:

'(x . (xs ...))
=>
'(x xs ...)

For any x and xs. Here, your x = '(1 2 3) and xs = '(4 5 6), so you get ((1 2 3) 4 5 6).


To see how cons and dotted-pair notation is related, let's shorten the problem to just '(1) and '(6). The lowest level way to build a pair of them is this:

(cons (cons 1 '()) (cons 6 '()))

Here, '() is nil, or the empty list. If we translate this literally to dotted-pair notation, we get this:

'((1 . ()) . (6 . ()))

But because the printer collapses dotted-pair notation whenever possible, you get this instead:

'((1 . ()) . (6 . ()))
=>
'((1) . (6))    ; <-- x=1, xs=nothing; x=6, xs=nothing
=>
'((1) 6) ; <-- x=1, xs=6
Nathan Shively-Sanders
  • 18,329
  • 4
  • 46
  • 56
  • Thank you for the detailed explanation; I now see that it's a pair of lists, per the collapse of dotted pair notation, and since (cdr (cons x y)) gives the second list. But why does (length (cons x y)) answer 4? Shouldn't it be either 6 (count all elements of both lists), or 2 (count two lists)? – limist May 28 '10 at 14:08
  • Ah, never mind my comment; I see it's a consequence of the (length) function they give in the book that doesn't deal with nested lists. – limist May 28 '10 at 14:13
  • 1
    @limist: Well, sort of. Your problem (I think) is thinking of List as an opaque, separate data type. It's really just a convention of how to use cons pairs (see tonio's answer). So, to get length 6, you'd write `nested-list`, although this would really be called `tree-size` because it uses the tree convention for conses. On the other hand, to get 2 from `(length (cons '(1 2 3) '(4 5 6))`, you'd have to get 2 for *any* list. That method ignores the list convention that says that, eg, '(1) is actually `(cons 1 '())`. Its length is 1, not 2, because of the way lists are constructed. – Nathan Shively-Sanders May 28 '10 at 16:19
  • you're correct in your diagnosis, thank you. :) I realized there was vagueness in my mind about how cons and list are defined when I worked through another SICP problem, and also, some "legacy" mental model from how lists work in python. Thanks again for taking the time to explain things clearly, I appreciate it. – limist Jun 01 '10 at 19:04
10

cons uses the first argument as head of the list, and the second as tail.

You give it a first list (1 2 3), which will constitute the head of the resulting list and a second list (4 5 6), to be used as tail of the list. Thus, you end with ((1 2 3) 4 5 6).

Thing of lists as left-to-right combs, ending with empty list (represented as o here), and see how they combine.

 X=      Y=
 /\      /\
1 /\  + 4 /\    
 2 /\    5 /\  
  3  o    6  o

You then build:

 /\
X  Y

Obtaining:

  /\
 /\ \
1 /\ \
 2 /\ \
  3  o/\
     4 /\
      5 /\
       6  o

which is ((1 2 3) 4 5 6 when represented with parenthesis. And this is a pair of lists.

tonio
  • 10,355
  • 2
  • 46
  • 60
2

hey, i think you could think of it in this way;

whenever there is a nil, there must be a pair of parenthesis, as follow:

(cons 1 (cons 2 nil))--> (list 1 2)

(let ((x (list 1 2 3)) (y (list 4 5 6))))

1.(cons x y)--> (cons (cons 1 (cons 2 (cons 3 nil))) (cons 4 (cons 5 (cons 6 nil)))) here, the first nil stands for an end of a pair which could be expressed by parenthesis; whereas the second nil stands for the end of the whole pair which use another pair of parenthesis; so, ((1 2 3) 4 5 6)

2.(list x y)-> (cons x (cons y nil); as we know the x contain a nil, so it must be (1 2 3); the second part contains two nils, so ((1 2 3) (4 5 6));

the inner most nil means the outer most parenthesis;

Hope it can help.

Peng
  • 21
  • 1
1

I found the diagrams in the Emacs Lisp tutorial particularly helpful when learning Lisp.

Ken
  • 2,886
  • 20
  • 11
0

Read this: http://en.wikipedia.org/wiki/Cons

Ori Pessach
  • 6,777
  • 6
  • 36
  • 51
0

try (list x y) I'm sure it works on common lisp, I don't know about Scheme

rabidmachine9
  • 7,775
  • 11
  • 46
  • 59