2013-08-18 27 views
9

http://www.aiai.ed.ac.uk/~jeff/lisp/cl-pitfalls tiểu bang này là một trong những cạm bẫy Common LispLàm thế nào để xem ra cho thực tế là NREVERSE có thể sửa đổi CAR thay

chức năng phá hủy mà bạn nghĩ rằng sẽ sửa đổi CDR có thể sửa đổi CAR để thay thế. (Ví dụ: NREVERSE.)

Tôi không chắc chắn những biện pháp phòng ngừa mà tôi phải thực hiện. Biện pháp phòng ngừa thông thường Tôi có thể thực hiện NREVERSE có thể sửa đổi CDR chỉ sử dụng NREVERSE khi danh sách (đối số) không chia sẻ đuôi với bất kỳ danh sách nào khác mà biến của tôi có thể tham khảo sau (ngoại trừ biến tôi lưu giá trị trả về) đến). Tôi nên thực hiện biện pháp phòng ngừa nào từ thực tế là NREVERSE có thể sửa đổi CAR? Làm thế nào là điều này để xem ra cho?

Trả lời

12

Nếu không có bất kỳ ngữ cảnh nào thì điều này rất khó hiểu.

Ví dụ:

(setq list1 (list 1 2 3 4)) 

Bây giờ chúng ta có một danh sách bốn số. Biến số list1 trỏ đến điểm yếu đầu tiên.

Nếu chúng ta nhìn vào một đảo ngược phá hoại, chúng ta đang nói về một hoạt động có thể thay đổi các tế bào khuyết điểm. Có nhiều cách khác nhau như thế nào danh sách này có thể được đảo ngược.

Ví dụ: chúng tôi có thể lấy các ô khuyết điểm và đảo ngược các ô đó. Ô khuyết điểm đầu tiên là ô cuối cùng. Cdr của ô đó sau đó phải được thay đổi thành NIL.

CL-USER 52 > (setq list1 (list 1 2 3 4)) 
(1 2 3 4) 

CL-USER 53 > (nreverse list1) 
(4 3 2 1) 

Bây giờ chúng tôi biến list1 vẫn trỏ tới các tế bào khuyết điểm tương tự, nhưng cdr của nó đã được thay đổi:

CL-USER 54 > list1 
(1) 

Để đảm bảo rằng các điểm biến vào một danh sách đảo ngược, các lập trình viên sau đó có nhiệm vụ cập nhật biến và đặt nó thành kết quả của hoạt động nreverse. Người ta cũng có thể bị cám dỗ để khai thác kết quả quan sát được rằng list1 điểm đến điểm cuối cùng.

Trên đây là những gì nhà phát triển Lisp thường mong đợi. Hầu hết việc thực hiện ngược lại dường như hoạt động theo cách đó. Nhưng nó không được chỉ định trong tiêu chuẩn ANSI CL như thế nào nreverse phải được thực hiện.

Vậy thay vào đó, thay đổi CAR là gì?

Hãy nhìn vào một thực hiện thay thế nreverse:

(defun nreverse1 (list) 
    (loop for e across (reverse (coerce list 'vector)) 
     for a on list do 
     (setf (car a) e)) 
    list) 

Trên chức năng cho phép của chuỗi tế bào khuyết điểm còn nguyên vẹn, nhưng thay đổi xe.

CL-USER 56 > (setq list1 (list 1 2 3 4)) 
(1 2 3 4) 

Bây giờ, hãy sử dụng phiên bản mới, nreverse1.

CL-USER 57 > (nreverse1 list1) 
(4 3 2 1) 

CL-USER 58 > list1 
(4 3 2 1) 

Bây giờ bạn thấy sự khác biệt: list1 vẫn trỏ vào toàn bộ danh sách.

Tóm tắt: bạn cần lưu ý rằng có thể triển khai khác nhau nreverse. Không khai thác các hành vi thông thường, nơi mà một biến sau đó sẽ trỏ đến khuyết điểm cuối cùng. Chỉ cần sử dụng kết quả của nreverse và mọi thứ đều ổn.

Lưu ý bên: phiên bản thứ hai có thể được sử dụng ở đâu?

Một số triển khai Lisp trên Máy Lisp cho phép một đại diện giống như vectơ nhỏ gọn của danh sách. Nếu trên một thực hiện Lisp như vậy sẽ nreverse một danh sách như vậy, những người triển khai có thể cung cấp một nreverse giống như vector hiệu quả.

+0

Tôi +1 câu trả lời của bạn vì tôi thích cách tiếp cận chi tiết bạn mất, nhưng tôi không chắc chắn nếu bạn trả lời câu hỏi của OP, đó là làm thế nào để đối phó với thực tế là cả 'car' và' cdr' đều có khả năng bị đột biến khi gọi 'nreverse'. –

+6

@ Chris Jester-Young: không cần phải * đối phó * với nó. Chỉ cần không khai thác một hiệu ứng phụ cụ thể của một triển khai cụ thể. Chỉ cần sử dụng kết quả trả về của 'nreverse'. –

+1

Tôi đồng ý, _if_ danh sách bạn đang truy cập không có bí danh. Nếu không, bạn chạy vào tất cả các loại rắc rối. Đề án, đặc biệt, có rất nhiều thủ tục danh sách trả về kết quả được chia sẻ đuôi, đó là một dạng răng cưa. Ví dụ, kết quả của 'append' và' append! '(' Nconc') chia sẻ đuôi với danh sách cuối cùng được đưa ra, do đó, gọi 'reverse!' ('Nreverse') trên đó có thể là vấn đề (nếu danh sách có đuôi -sharing đang được sử dụng ở nơi khác). –

3

Trong mọi trường hợp, có thể là CAR hoặc CDR của ô được điều chỉnh, bạn không nên sử dụng NREVERSE nếu bất kỳ ô khuyết điểm nào (bao gồm ô khuyết đầu tiên) của danh sách được chuyển có thể được chia sẻ với một danh sách khác. Thay vào đó hãy sử dụng REVERSE.

BTW, CLISP thực sự làm thay đổi CAR:

> (let ((a (list 1 2 3 4 5 6 7 8 9 0))) 
    (nreverse a) 
    a) 
(0 9 8 7 6 5 4 3 2 1) 
Các vấn đề liên quan