Tái hiện đúng của một danh sách? Điều gì đang xảy ra dưới mui xe?


8

Tôi đang dạy bản thân một số điều cần thiết hơn và đã gặp phải vấn đề sau:

Nếu tôi muốn đặt lại một biến danh sách, nó sẽ không được cập nhật sau lần đánh giá đầu tiên. Dưới đây là một số mã ví dụ:

(defun initilize ()
  (setq example '(3)))

(defun modify ()
  (initilize)
  (message "%S" example)
  (setcar example 2))

; M-x eval-buffer RET
(modify) ; message --> (3)
(modify) ; message --> (2)
(modify) ; message --> (2)

Tôi quan tâm đến hai điều. Đầu tiên là tìm hiểu thêm về những gì đang xảy ra "dưới mui xe", vậy tại sao nó hoạt động lần đầu tiên và thất bại trong các cuộc gọi tiếp theo?

Câu hỏi thứ hai và thiết thực hơn là làm thế nào để sắp xếp lại danh sách đúng cách hay có một cách phổ biến khác để làm một cái gì đó như thế?

Một cách giải quyết tôi thấy mình là sử dụng một danh sách được trích dẫn và đánh giá nội dung như thế này:

(setq example `(,3)) 

2
Tóm tắt: Đỗ không mong đợi '(some list)để được eqđến '(some list)- từng .There nói chung là không có bảo đảm trong Lisp rằng mã mà rõ ràng trích dẫn một danh sách lợi nhuận mới cấu trúc danh sách mỗi lần. Trong một số triển khai Lisp, nó có thể, hoặc đôi khi có thể. Trong những người khác, nó không bao giờ làm. Mã của bạn dù sao cũng không nên phụ thuộc vào bất kỳ hành vi nào như vậy từ việc triển khai. Nếu bạn muốn cấu trúc mới danh sách, sử dụng listhoặc conshoặc tương đương.
vẽ

(Tôi đoán rằng câu hỏi này là một bản sao, nhưng tôi không biết bản sao đó ở đâu.)
Drew

1
Tôi nghĩ rằng vấn đề ở đây là examplechưa bao giờ được khai báo là một biến, vì vậy setqphải hành động như thể nó khai báo một biến mới, nhưng sau đó khi bạn gọi initializelại một biến mới đang được tạo, trong khi modifynhớ lại biến cũ ... trong mọi trường hợp, đây không phải là một hành vi dự kiến, tuy nhiên, việc sử dụng setqvới một thứ chưa được giới thiệu trước đó như một biến cũng có thể không được xác định.
wvxvw

3
OK, tôi đã tìm ra những gì xảy ra. '(3)được coi là một giá trị theo nghĩa đen, vì vậy một khi bạn (setcar '(3) 2), bất cứ khi nào bạn làm (defvar foo '(3))hoặc (let ((foo '(3)))như vậy, bạn có thể sẽ nhận được một giá trị foobằng '(2). Tôi nói "có khả năng" bởi vì hành vi này không được đảm bảo, đó là một loại tối ưu hóa mà người phiên dịch thực hiện bất cứ khi nào nó cảm thấy, một cái gì đó được gọi là loại bỏ hằng số phụ (một trường hợp cụ thể). Vì vậy, những gì abo-abo viết không chính xác là lý do. Nó giống như sửa đổi một chuỗi ký tự bằng chữ C (thường tạo cảnh báo).
wvxvw

Câu trả lời:


5

Có lẽ điều này sẽ làm sáng tỏ một số nhầm lẫn:

  • Hàm của bạn initilizekhông khởi tạo biến example. Nó đặt nó vào một ô khuyết cụ thể - cùng một ô khuyết mỗi lần nó được gọi. Lần đầu tiên initilizeđược gọi, setqgán examplecho một ô khuyết điểm mới, là kết quả của việc đánh giá '(3). Các cuộc gọi tiếp theo initilizechỉ cần gán lại examplecho cùng một ô.

  • initilizechỉ cần gán lại cùng một ô khuyết điểm example, modifychỉ cần đặt ô tô của cùng một ô đó cho 2mỗi lần nó được gọi.

  • Để khởi tạo một danh sách, hãy sử dụng listhoặc cons(hoặc một backpote sex tương đương, chẳng hạn như `(,(+ 2 1))hoặc `(,3)). Ví dụ, sử dụng (list 3).

Chìa khóa để hiểu điều này là để biết rằng một ô khuyết điểm được trích dẫn chỉ được đánh giá một lần và sau đó cùng một ô khuyết điểm được trả về. Đây không nhất thiết là cách mà tất cả các Lisps cư xử, nhưng đó là cách Emacs Lisp cư xử.

Tổng quát hơn, hành vi đánh giá một đối tượng có thể thay đổi được trích dẫn là phụ thuộc vào việc thực hiện, nếu không phụ thuộc vào ngôn ngữ. Ví dụ, trong Common Lisp, tôi khá chắc chắn rằng không có gì trong định nghĩa (thông số) của ngôn ngữ xác định hành vi trong vấn đề này - nó phụ thuộc vào việc thực hiện.

Tóm tắt: Đừng mong đợi '(một số danh sách) sẽ là eq thành' (một số danh sách) - ever. Nói chung, không có gì đảm bảo trong Lisp rằng mã trích dẫn rõ ràng một danh sách trả về cấu trúc danh sách mới mỗi lần. Trong một số triển khai Lisp, nó có thể, hoặc đôi khi có thể. Trong những người khác, nó không bao giờ làm. Mã của bạn dù sao cũng không nên phụ thuộc vào bất kỳ hành vi nào như vậy từ việc triển khai. Nếu bạn muốn cấu trúc mới danh sách, sử dụng listhoặc conshoặc tương đương.


1
Là giai điệu hạ thấp trong hai dòng đầu tiên của câu trả lời của bạn cần thiết? Nếu không thì một câu trả lời khai sáng.
asjo

@asjo: Ý định không từ bỏ bằng mọi cách; lấy làm tiếc. Hy vọng rằng nó rõ ràng hơn bây giờ.
vẽ

Nhờ làm rõ mọi thứ lên một chút. Với thuật ngữ "đối số", tôi có nghĩa là đối số `(, 3) cho hàm setq, mặc dù tôi hiểu nó hơi không rõ ràng vì tôi thực sự đánh giá 3 trong danh sách được trích dẫn. Tôi sẽ chỉnh sửa nó.
clemera

@Drew Tuyệt vời! Bây giờ đọc nó thân thiện hơn.
asjo

5

Bạn có thể sử dụng (setq example (list 3))để tránh lỗi này.

Chuyện gì xảy ra là initngười được chỉ định một đối tượng mà ban đầu chứa (3)tới example. Nó đặt giá trị của đối tượng chỉ một lần. Sau đó, bạn sửa đổi giá trị này.

Đây là ví dụ của bạn trong C ++, nếu bạn hiểu điều đó tốt hơn:

#include <stdio.h>
#include <string.h>
char* example;
char* storage = 0;
char* create_object_once (const char* str) {
  if (storage == 0) {
    storage = new char[strlen (str)];
    strcpy (storage, str);
  }
  return storage;
}
void init () {
  example = create_object_once ("test");
}
void modify () {
  init ();
  printf ("%s\n", example);
  example[0] = 'f';
}
int main (int argc, char *argv[]) {
  modify ();
  modify ();
  modify ();
  return 0;
}

1
Cảm ơn, tôi không biết C ++ nhưng tôi hiểu ví dụ của bạn. Một điều vẫn làm phiền tôi: Trong ví dụ mã của bạn, bạn giới thiệu bộ lưu trữ biến tạo ra đối tượng với giá trị ban đầu chỉ khi nó chưa được đặt. Làm thế nào mà nó dịch sang những gì Trình thông dịch Emacs Lisp đang làm? Ý tôi là nếu tôi đánh giá lại initilizehàm và gọi modifylại thì nó sẽ hiển thị (3)lại chỉ vì tôi đánh giá lại hàm.
clemera

1
Tôi không thể đi vào chi tiết cụ thể (không biết chính xác chúng), nhưng nghĩ về (3)một đối tượng tạm thời là một phần của nó init. initCơ thể đặt examplethành địa chỉ của đối tượng tạm thời, nó không chạm vào giá trị của nó.
abo-abo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.