LISP very simple list question

2019-05-15 04:15发布

Im learning lisp and im pretty new at this so i was wondering...

if i do this:

(defparameter *list-1* (list 1 2))
(defparameter *list-2* (list 2 3))
(defparameter *list-3* (append *list-1* *list-2*))

And then

(setf (first *list-2*) 1)
*list-3*

I will get (1 2 1 4)

I know this is because the append is going to "save resources" and create a new list for the first chunk, but will actually just point to the second chunk, coz if i do:

(setf (first *list-1*) 0)
*list-3*

I will get (1 2 1 4) instade of the more logical (0 2 1 4)

So my question is, what other cases are like this in lisp, how do you black belt lispers know how to deal with this stuff that is not intuitive or consistent?

标签: list lisp
6条回答
唯我独甜
2楼-- · 2019-05-15 04:50

Um, primarily we learn how it works, so that what we imagine isn't consistent makes sense.

What you need to do is find some of the old-fashioned block and pointer diagrams, which I can't easily draw, but let's figure it out.

After the first defparameter, you've got list-1, which is

(1 . 2 . nil)

in dot notation; list-2 is

(2 . 3 . nil) 
查看更多
3楼-- · 2019-05-15 04:53

quote:

So my question is, what other cases are like this in lisp, how do you black belt lispers know how to deal with this stuff that is not intuitive or consistent?

I think that you are a bit harsh here with a subject that is quite a bit larger than you imagine. Lists are a rather elaborate concept in Lisp, and you need to understand that this is not some simple array. The standard provides a lot of functions to manipulate lists in every way. What you want in this case is:

(concatenate 'list *list-1* *list-2*)

So, why is there also append? Well, if you can omit copying the last list, and all symbols involved still return the correct data, this can be a significant performance boost in both calculating time and memory footprint. append is especially useful in a functional programming style which doesn't use side effects.

In lieu of further explanation about cons cells, destructive vs. nondestructive functions etc., I'll point you to a nice introduction: Practical Common Lisp, Ch. 12, and for a complete reference, the Common Lisp Hyperspec, look at Chapters 14 and 17.

查看更多
我欲成王,谁敢阻挡
4楼-- · 2019-05-15 04:54

So my question is, what other cases are like this in lisp, how do you black belt lispers know how to deal with this stuff that is not intuitive or consistent?

By reading the fine manual? Hyperpsec explicitly states:

[...] the list structure of each of lists except the last is copied. The last argument is not copied; it becomes the cdr of the final dotted pair of the concatenation of the preceding lists, or is returned directly if there are no preceding non-empty lists.

查看更多
我命由我不由天
5楼-- · 2019-05-15 04:58

The append function has to make a copy of its first argument, to avoid modifying existing data structures. As a result, you now have two list segments that look like (1 2 ...), but they're part of different lists.

In general, any list can be the tail of any other list, but you can't have a single list object that serves as the head of multiple lists.

查看更多
Juvenile、少年°
6楼-- · 2019-05-15 05:08

You have to think of lists in terms of cons cells. When you define list 1 and list 2, it is like:

(defparameter *list-1* (cons 1 (cons 2 nil)))
(defparameter *list-2* (cons 2 (cons 3 nil)))

Then, when you append:

(defparameter *list-3* (cons 1 (cons 2 *list-2*)))

Basically, a cons cell consists of two parts; a value (the car), and a pointer (the cdr). Append is defined to not change the first list, so that is copied, but then the last cdr (normally nil) is changed to point at the second list, not a copy of the second list. If you were willing to destroy the first list, you would use nconc.

Try this:

(defparameter *list-3* (nconc *list-1* *list-2*))

Then observe the value of *list-1*, it is (1 2 2 3), just like *list-3*.

The general rule is that the non-destructive functions (append) won't destroy existing data, while the destructive functions (nconc) will. What a future destructive function does ((setf cdr)), though, is not the responsibility of the first non-destructive function.

查看更多
在下西门庆
7楼-- · 2019-05-15 05:10

One defensive tactic is to avoid sharing structure.

(defparameter *list-3* (append *list-1* *list-2* '()))

or

(defparameter *list-3* (append *list-1* (copy-list *list-2*)))

Now the structure of the new *list-3* is all new, and modifications to *list-3* won't affect *list-2* and vice versa.

查看更多
登录 后发表回答