2013-07-09 37 views
5

Tôi đang cố gắng xác định cấu trúc với một số thuộc tính mà tôi biết mình muốn và một số thuộc tính khác không cần thiết cho cấu trúc cơ bản.Làm thế nào để xác định một cấu trúc trong lisp với số lượng tùy ý của args?

(defstruct (node (:type list)) label [other args here]) 

Tôi biết trong một chức năng bạn có thể làm:

(defun foo (arg1 &rest args) ...) 

Có một số loại &rest tương đương cho defstruct?

Tôi chỉ học lisp, vì vậy tôi có cảm giác rằng tôi đang thiếu thứ gì đó. Nếu không có tương đương &rest, bất kỳ ý tưởng nào về cách tôi có thể thực hiện việc này? Cảm ơn trước!

Trả lời

6

Không rõ chính xác những gì bạn đang tìm kiếm. Trường hợp mặc định cho cấu trúc là một loại bản ghi với số lượng vị trí cố định, mỗi vị trí có tên và có thể truy cập thông qua một hàm được tạo bởi macro defstruct. Ví dụ, khi bạn đã thực hiện

(defstruct node 
    label) 

bạn có thể truy cập vào một nhãn node 's với node-label và nhận được thời gian tra cứu nhanh (vì nó thường chỉ là một chỉ số vào một đoạn bộ nhớ). Bây giờ, khi bạn đang thực hiện, bạn có thể chọn sử dụng danh sách khi triển khai cấu trúc, trong trường hợp này node-label chỉ là bí danh cho car hoặc first.

(defstruct (node (:type list)) 
    label) 

CL-USER> (make-node :label 'some-label) 
(SOME-LABEL) 
CL-USER> (node-label (make-node :label 'some-label)) 
SOME-LABEL 
CL-USER> (first (make-node :label 'some-label)) 
SOME-LABEL 
CL-USER> (car (make-node :label 'some-label)) 

Nếu bạn đang tìm kiếm các cặp giá trị then chốt danh sách dựa trên tùy ý, bạn có thể muốn có một property list, mà Common Lisp chứa một số chức năng tiện lợi.

Nếu bạn muốn có cấu trúc cũng chứa danh sách thuộc tính, bạn có thể thêm một hàm tạo đặc biệt điền danh sách đó. Ví dụ:

(defstruct (node (:type list) 
       (:constructor make-node (label &rest plist))) 
    label 
    plist) 

CL-USER> (make-node 'some-label :one 1 :two 2) 
(SOME-LABEL (:ONE 1 :TWO 2)) 
CL-USER> (node-plist (make-node 'some-label :one 1 :two 2)) 
(:ONE 1 :TWO 2) 
+0

Cảm ơn, đây gần như chính xác những gì tôi đang tìm kiếm. Nhưng có cách nào để có danh sách thuộc tính, khi bạn đặt nó, giống như các khe khác trong 'nút'? Nói cách khác, có cách nào để làm '(make-node 'some-label: một 1: hai 2) => (MỘT SỐ NHÃN: MỘT: HAI 2)', thay vì '(MỘT SỐ NHÃN HIỆU (: ONE 1: HAI 2)) '? –

+1

Tóm lại, không. [Câu trả lời của Rainer] (http://stackoverflow.com/a/17556349/1281433) đi sâu hơn về cấu trúc nói chung, nhưng vấn đề là cấu trúc có số khe cố định. Mặc dù bạn có thể sử dụng 'defstruct' để tạo ra" bản ghi của người nghèo "với tùy chọn' (: list list) ', bạn vẫn chỉ có một số khe cố định (có nghĩa là danh sách có một số cố định yếu tố). Nó thực sự có vẻ như bạn chỉ muốn một danh sách tài sản. –

+0

Vâng, đọc qua câu trả lời của Rainer, tôi nghĩ rằng đó là hỏa lực nhiều hơn tôi cần cho những gì tôi đang làm. Cám ơn những lời khuyên của bạn! –

7

Trong Lisp thông thường, cấu trúc được cho là hồ sơ cứng và cấp thấp. Họ không có tính năng năng động ưa thích.

Việc bạn có thể làm với cấu trúc là xác định loại cấu trúc mới kế thừa từ cấu trúc khác. Có duy nhất thừa kế có sẵn.

Để xử lý khả năng mở rộng động, cách thông thường là thêm khe danh sách thuộc tính vào cấu trúc. Xem câu trả lời của Joshua.

Sau đó, có Hệ thống đối tượng chung Lisp, cung cấp nhiều thừa kế và bạn có thể thay đổi các lớp khi chạy. Vì vậy, bạn có thể thêm một khe vào một lớp và các cá thể của bản cập nhật lớp đó. Bạn cũng có thể thay đổi lớp của đối tượng và vị trí có thể bị thêm hoặc xóa. Tuy nhiên, thông thường tất cả các phiên bản của một lớp sẽ có cùng một tập hợp các vị trí. Một lần nữa, người ta thấy rằng một khe với một danh sách tài sản có thể được thêm vào và sử dụng cho khả năng mở rộng.

Có các hệ thống đối tượng khác cho Common Lisp, có thể dễ dàng thêm các vị trí trên cơ sở từng trường hợp. Nhưng nó thường là quá nhiều để sử dụng chúng chỉ vì điều đó, vì chúng khá mạnh hơn một chút.

Với CLOS và giao thức Siêu đối tượng, người dùng có thể cố gắng ẩn nó.Ở đây tôi đang sử dụng LispWorks:

Chúng ta định nghĩa một lớp mixin đối với tài sản của chúng tôi:

(defclass property-mixin() 
    ((plist :initform nil)) 
    #+lispworks 
    (:optimize-slot-access nil)) 

Setting và đọc các thuộc tính:

(defmethod set-property ((object property-mixin) key value) 
    (setf (getf (slot-value object 'plist) key) value)) 

(defmethod get-property ((object property-mixin) key) 
    (getf (slot-value object 'plist) key)) 

Bây giờ chúng ta viết phương pháp để làm cho SLOT-VALUE chấp nhận tên thuộc tính của chúng tôi :

(defmethod (setf clos:slot-value-using-class) 
     (value (class standard-class) (object property-mixin) slot-name) 
    (declare (ignorable class)) 
    (if (slot-exists-p object slot-name) 
     (call-next-method) 
    (progn 
     (set-property object slot-name value) 
     value))) 

(defmethod clos:slot-value-using-class ((class standard-class) 
             (object property-mixin) 
             slot-name) 
    (declare (ignorable class)) 
    (if (slot-exists-p object slot-name) 
     (call-next-method) 
    (get-property object slot-name))) 

Ví dụ. Chúng ta định nghĩa một lớp ô tô với hai khe:

(defclass automobile (property-mixin) 
    ((company :initarg :company) 
    (motor :initarg :motor)) 
    #+lispworks 
    (:optimize-slot-access nil)) 

Bây giờ một ví dụ:

CL-USER 45 > (setf a6 (make-instance 'automobile :company :audi :motor :v6)) 
#<AUTOMOBILE 402005B47B> 

Chúng tôi có thể nhận được một giá trị khe bình thường:

CL-USER 46 > (slot-value c1 'motor) 
:V6 

Hãy viết thư cho một khe cắm mà không làm tồn tại, nhưng sẽ được thêm vào danh sách tài sản của chúng tôi:

CL-USER 47 > (setf (slot-value a6 'seats) 4) 
4 

Chúng ta có thể nhận được giá trị trở lại:

CL-USER 48 > (slot-value c1 'seats) 
4 
1

Tôi nghĩ điều này sẽ có giá trị trả lời riêng biệt chứ không phải sau đó một bình luận, vì vậy ở đây đi:

Một số lần, khi bạn nghĩ rằng bạn cần một cấu trúc hoặc một đối tượng, nhưng bạn có một số yêu cầu đặc biệt mà các thực thể này không thực hiện, có lẽ điều này là bởi vì những gì bạn thực sự cần là một cấu trúc dữ liệu khác? Đối tượng hoặc cấu trúc là tốt khi một số điều kiện được đáp ứng, một điều kiện như vậy là các khe được biết đến tĩnh - điều này cho phép trình biên dịch có lý do tốt hơn về mã, cả hai đều tốt cho việc tối ưu hóa và báo cáo lỗi.

Mặt khác, có cấu trúc dữ liệu. Một số được cung cấp với thư viện chuẩn ngôn ngữ, một số khác được thêm vào trên nó. Dưới đây là một thư viện cung cấp nhiều thư viện: http://cliki.net/cl-containers nhưng thậm chí còn có nhiều thư viện hơn cho các trường hợp đặc biệt.

Bây giờ, tôi sẽ cho rằng việc sử dụng cấu trúc như danh sách, mảng, một số loại cây vv sẽ tốt hơn sau đó cố mở rộng các đối tượng hoặc cấu trúc để cho phép thêm các vị trí động. Điều này là do thông thường chúng tôi hy vọng thời gian để truy cập vào một vị trí không đáng kể. Đó là chúng tôi hy vọng nó sẽ là O (1). Đây là những gì bình thường xảy ra bất kể số lượng khe mà một đối tượng có. Bây giờ, khi bạn đang sử dụng một danh sách bên dưới bạn đang làm cho nó O (n), trong khi bạn giữ cùng ngữ nghĩa! Bạn có thể, tất nhiên, sử dụng một bảng băm để làm cho nó O (1) (mặc dù điều này sẽ vẫn còn chậm hơn sau đó truy cập khe), nhưng sau đó bạn sẽ có một số hành vi bất ngờ khác, chẳng hạn như nil trả lại khi khe không tồn tại thay vì lỗi thường xuyên, vv ..

Tôi không nghĩ rằng việc mở rộng đối tượng theo cách như vậy là một thực tế phổ biến trong CL, đây có thể là lý do tại sao các phản hồi khác không ngăn cản bạn thực hiện điều đó. Tôi biết ít hơn CL sau đó những người trả lời khác, nhưng tôi đã có rất nhiều đau buồn với loại thao tác này bằng ngôn ngữ khác, nơi mà điều này là phổ biến và thường không khuyến khích.

Các vấn đề liên quan