2012-04-24 34 views
18

Phần API Cheatsheet trong Danh sách dường như chỉ ra rằng '() là một hàm tạo danh sách, giống như (list), nhưng tôi đã thấy rằng trong thực tế chúng không giống hệt nhau. Ví dụ, đưa ra:Sự khác nhau giữa '() và (danh sách) trong Clojure là gì?

(def foo "a") 
(def bar "b") 
(def zip "c") 

Các tuyên bố sau:

(apply str '(foo bar zip)) 

sản xuất đầu ra "foobarzip", mà tôi sẽ không mong đợi.

Nhưng cho là tương đương:

(apply str (list foo bar zip)) 

sản xuất "abc", như tôi mong đợi.

Điều gì đang xảy ra ở đây? Nếu có "viết tắt" cho danh sách trong Clojure (như {} cho bản đồ và [] đối với vectơ), nó là gì?

+0

Không có cách viết tắt cho danh sách vì danh sách có tầm quan trọng thứ yếu đối với Clojure. –

Trả lời

31

Trong lisps, ' (như quote) trích dẫn các đối số của nó, tức là bảo toàn chúng chính xác như được viết ở dạng s-exp của chúng, bao gồm không đánh giá bất kỳ thứ gì bên trong.

Để đặt theo cách khác '(foo bar zip) tạo danh sách chứa các ký hiệu foo, bar, zip; trong khi (list foo bar zip) tạo danh sách chứa các giá trị của foo, bar, zip. Trong trường hợp đầu tiên, str sẽ tự chuyển đổi các ký hiệu thành chuỗi và sau đó ghép chúng lại.

Như một minh chứng cho điều này:

=> (def foo "a") 
=> (type (first '(foo))) 
clojure.lang.Symbol 
=> (type (first (list foo))) 
java.lang.String 
+2

@ Jonathan Trong kinh nghiệm của tôi, hầu hết thời gian nó là thuận tiện hơn để sử dụng một vector chứ không phải là một danh sách trích dẫn. Ngoài việc yêu cầu ít tổ hợp phím hơn, vectơ dễ phân biệt hơn với danh sách không được đánh giá so với danh sách được trích dẫn. – user100464

+0

Bạn cũng có thể bình luận về các trường hợp rỗng, ''()' và '(danh sách)'? Có sự khác biệt nào giữa chúng không? – Lii

+0

@Lii không, tôi tin rằng chúng giống nhau. – huon

10

Sự khác biệt là, như bạn có thể thấy, cú pháp đen '()trích dẫn. Điều này có nghĩa là các biểu tượng bên trong không được đánh giá. Để sử dụng literals danh sách trong khi đánh giá các yếu tố bạn có thể khai thác syntax-quote đọc vĩ mô:

user=> (apply str `(~foo ~bar ~zip)) 
"abc" 
3

Khi bạn viết:

(def foo "a") 
(def bar "b") 
(def zip "c") 

Bạn đã xác định ba biểu tượng: foo, barzip gắn liền với các giá trị: "a", "b""c".

Hiệp hội được lưu trữ bên trong bảng namsepace, vì vậy nếu sử dụng REPL, không gian tên sẽ user theo mặc định, vì vậy các bảng người dùng namespace bây giờ sẽ bao gồm:

{foo 
#'user/foo 
bar 
#'user/bar, 
zip 
#'user/zip} 

Bạn có thể xem bảng namespace bởi đang làm: (ns-interns *ns*)

Bây giờ nếu bạn viết: (foo bar zip) bên trong Clojure, sẽ có bốn cách khác nhau mà người đọc có thể đọc được. Bạn sẽ cần phải chỉ định cho người đọc theo cách mà nó nên được đọc. Đó là nơi mà `, 'list phát huy tác dụng.

Trường hợp không có chỉ thị:

(foo bar zip) 

Khi chỉ đơn giản bằng văn bản mà không cần bất kỳ chỉ thị, người đọc sẽ giải thích điều này như một S thể hiện và sẽ giải thích foo như một ánh xạ biểu tượng cho một hàm, với barzip làm biểu tượng ánh xạ tới các giá trị được chuyển vào hàm foo.

Trong trường hợp của chúng tôi, nó sẽ ném một ngoại lệ:

java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.IFn 

này được vì không có chức năng foo được định nghĩa, foo được gắn liền với "a", mà là một String, không phải là một IFN (một chức năng Clojure).

Nếu chức năng foo được xác định, nó sẽ được gọi là foo chuyển vào làm đối số "b""c".

Trường hợp của list:

(list foo bar zip) 

Khi sử dụng các biểu tượng danh sách, người đọc được thực sự giải thích này giống như trường hợp không có chỉ thị. Tức là, nó đang tìm biểu tượng list ánh xạ tới một hàm sẽ lấy các giá trị được liên kết ánh xạ tới foo, barzip làm đối số. Hàm list được Clojure định nghĩa trước bên trong không gian tên clojure.core; nó trả về một danh sách các đối số của nó.

Vì vậy, khi người đọc tìm kiếm list, nó tìm thấy hàm clojure.core, sau đó nó tìm biểu tượng foo và thấy rằng nó ánh xạ tới "a", v.v. Khi nó đã tìm thấy tất cả ánh xạ cho các ký hiệu, nó gọi số list và chuyển giá trị được kết hợp của foo bar zip, là "a" "b" "c". Vì vậy, (list foo bar zip) giống với (list "a" "b" "c").

Trường hợp của ':

'(foo bar zip) 

Các ' quote nói với người đọc rằng hình thức sau đây là để được đọc như vậy. Trong trường hợp của chúng tôi, foo, barzip là các biểu tượng và (foo bar zip) là danh sách các biểu tượng. Vì vậy, người đọc sẽ giải thích điều này như là một danh sách các biểu tượng.

Đó là lý do tại sao khi bạn chạy (apply str '(foo bar zip)), gọi số str 'foo 'bar 'zip sẽ cung cấp cho bạn foobarzip. Nghĩa là, nó sẽ chuyển đổi mỗi biểu tượng trong danh sách các biểu tượng thành một biểu diễn String, và sau đó nối chúng vào một String.

Bằng cách lấy biểu mẫu như, nó chuyển như đối số một danh sách các biểu tượng, mà không đánh giá các ký hiệu, tức là, mà không tìm kiếm những gì chúng được liên kết. Nếu bạn chạy (eval '(foo bar zip)), bạn sẽ chuyển danh sách các ký hiệu đến evaleval sẽ đánh giá các ký hiệu thành giá trị và trả về danh sách các giá trị mà biểu tượng được ánh xạ tới. Vì vậy, bạn có thể nghĩ ra câu trích dẫn ' khi chuyển mã xung quanh dưới dạng mã.

Trường hợp của `:

`(foo bar zip) 

một Đây là một chút phức tạp hơn. Người đọc sẽ nhìn thấy backquote ` và sẽ giải quyết các biểu tượng bên trong danh sách các biểu tượng đệ quy để có được một danh sách các biểu tượng đủ điều kiện.

Về cơ bản, khi người đọc tra cứu các biểu tượng bên trong bảng biểu tượng, nó sẽ làm như vậy từ bảng biểu tượng của không gian tên hiện tại. Một biểu tượng đủ điều kiện, là một biểu tượng bao gồm thông tin không gian tên. Vì vậy, khi chạy `(foo bar zip) người đọc sẽ thay thế những biểu tượng đó bằng những biểu tượng đủ điều kiện, biến nó thành (user.foo user.bar user.zip).

Có thể thông báo cho người đọc đánh giá một số yếu tố trong danh sách, trong khi thay đổi những người khác thành biểu tượng đủ điều kiện. Để làm như vậy, bạn có tiền tố những biểu tượng mà bạn muốn đánh giá với ~ như trong:

`(foo ~bar zip) 

sẽ cung cấp cho bạn

(clojure.foo "b" clojure.zip) 

có hiệu quả, các backquote ` là rất nhiều giống với trích dẫn ' ở chỗ nó không không đánh giá, nhưng chỉ đơn giản trả về mã, ngoại trừ việc nó thao tác mã được trả về một chút bởi các biểu tượng đủ điều kiện bên trong nó. Điều này có ý nghĩa đối với macro, đôi khi bạn có thể muốn tham chiếu đầy đủ, để tìm nạp từ một không gian tên khác và đôi khi bạn muốn linh hoạt khi nói, hãy tìm biểu tượng này trong không gian tên hiện tại.

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