2013-04-09 38 views
15

Một điều tôi đã nhầm lẫn một chút là sự khác biệt giữa các dấu ngoặc và các dấu ngoặc trong clojure yêu cầu các câu lệnh. Tôi đã tự hỏi nếu ai đó có thể giải thích điều này với tôi. Ví dụ, những làm điều tương tự:Whats sự khác biệt giữa parens và ngoặc trong "yêu cầu"?

(ns sample.core 
    (:gen-class) 
    (:require clojure.set clojure.string)) 

(ns sample.core 
    (:gen-class) 
    (:require [clojure.set] 
      [clojure.string])) 

Tuy nhiên, công trình này từ repl

(require 'clojure.string 'clojure.test) 

Nhưng thất bại trong một file CLJ

(ns sample.core 
    (:gen-class) 
    (:require 'clojure.string 'clojure.test)) 
... 
Exception in thread "main" java.lang.Exception: lib names inside prefix lists must not contain periods 
    at clojure.core$load_lib.doInvoke(core.clj:5359) 
    at clojure.lang.RestFn.applyTo(RestFn.java:142) 
    .... 

Trong khi các apear để làm cùng một mỏng g:

(ns sample.core 
    (:gen-class) 
    (require clojure.set clojure.string)) 

(ns sample.core 
    (:gen-class) 
    (:require clojure.set clojure.string)) 

Nói chung tôi không hiểu điều này. Tôi hiểu việc sử dụng, nhập và yêu cầu. Nhưng tôi không hiểu ":" và sự khác biệt giữa mọi thứ trong [] và '() vv. Có ai có thể soi sáng chủ đề này một cách trực quan không?

+0

bản sao có thể có của [Tại sao yêu cầu trong biểu mẫu ns hoạt động khác với hàm yêu cầu] (http://stackoverflow.com/questions/3719929/why-does-require-in-the-ns-form-behave- khác nhau-from-the-require-function) –

+1

Hm, không thực sự hỏi về sự khác biệt của [] và sự khác biệt giữa mã repl và clj. –

Trả lời

12

Vấn đề ở đây là tinh tế và có thể khó khăn để bẻ khóa mà không cần phải hiểu một chút về macro.

Macro thao tác cú pháp theo cùng cách mà các chức năng thao tác các giá trị. Trên thực tế, các macro chỉ là các hàm có móc làm cho chúng được đánh giá tại thời gian biên dịch. Chúng được chuyển qua dữ liệu mà bạn thấy trong mã nguồn và được đánh giá từ trên xuống. Hãy tạo hàm và macro có cùng nội dung để bạn có thể thấy sự khác biệt:

(defmacro print-args-m [& args] 
    (print "Your args:") 
    (prn args)) 

(defn print-args-f [& args] 
    (print "Your args:") 
    (prn args)) 

(print-args-m (+ 1 2) (str "hello" " sir!")) 

; Your args: ((+ 1 2) (str "hello" " sir!")) 

(print-args-f (+ 1 2) (str "hello" " sir!")) 

; Your args: (3 "hello sir!") 

Macro được thay thế bằng giá trị trả về của chúng. Bạn có thể kiểm tra quá trình này với macroexpand

(defmacro defmap [sym & args] 
    `(def ~sym (hash-map [email protected]))) ; I won't explain these crazy symbols here. 
           ; There are plenty of good tutorials around 

(macroexpand 
    '(defmap people 
    "Steve" {:age 53, :gender :male} 
    "Agnes" {:age 7, :gender :female})) 

; (def people 
; (clojure.core/hash-map 
;  "Steve" {:age 53, :gender :male} 
;  "Agnes" {:age 7, :gender :female})) 

Tại thời điểm này, tôi có lẽ nên giải thích rằng ' gây các hình thức sau đây để được quote d. Điều này có nghĩa là trình biên dịch sẽ đọc biểu mẫu, nhưng không thực thi nó hoặc cố gắng giải quyết các biểu tượng và vv. tức là 'conj đánh giá thành một biểu tượng, trong khi conj đánh giá thành một hàm. (eval 'conj) tương đương với (eval (quote conj)) tương đương với conj.

Với ý nghĩ đó, hãy nhớ rằng bạn không thể giải quyết một biểu tượng làm không gian tên cho đến khi nó được nhập một cách kỳ diệu vào không gian tên của bạn bằng cách nào đó. Đây là chức năng của require. Nó lấy các biểu tượng và tìm các không gian tên mà chúng tương ứng, làm cho chúng có sẵn trong không gian tên hiện tại.

Chúng ta hãy xem những gì các ns macro sẽ mở rộng:

(macroexpand 
    '(ns sample.core 
    (:require clojure.set clojure.string))) 

; (do 
; (clojure.core/in-ns 'sample.core) 
; (clojure.core/with-loading-context 
;  (clojure.core/refer 'clojure.core) 
;  (clojure.core/require 'clojure.set 'clojure.string))) 

Xem cách nó được trích dẫn những biểu tượng clojure.setclojure.string cho chúng ta? Tiện như thế nào! Nhưng thỏa thuận khi bạn sử dụng require thay vì :require là gì?

(macroexpand 
'(ns sample.core 
    (require clojure.set clojure.string))) 

; (do 
; (clojure.core/in-ns 'sample.core) 
; (clojure.core/with-loading-context 
;  (clojure.core/refer 'clojure.core) 
;  (clojure.core/require 'clojure.set 'clojure.string))) 

Dường như bất cứ ai đã viết ns vĩ mô là tốt đẹp, đủ để cho chúng tôi làm điều đó cả hai phương diện, kể từ khi kết quả này là chính xác giống như trước đây. Neato!

chỉnh sửa: tvachon là đúng về chỉ sử dụng :require vì nó là hỗ trợ chính thức hình thức chỉ

Nhưng thỏa thuận với dấu ngoặc là gì?

(macroexpand 
    '(ns sample.core 
    (:require [clojure.set] 
       [clojure.string]))) 

; (do 
; (clojure.core/in-ns 'sample.core) 
; (clojure.core/with-loading-context 
; (clojure.core/refer 'clojure.core) 
; (clojure.core/require '[clojure.set] '[clojure.string]))) 

Hóa ra chúng cũng được trích dẫn, giống như chúng tôi đã thực hiện khi chúng tôi đang thực hiện cuộc gọi độc lập tới require.

Nó cũng chỉ ra rằng ns không quan tâm liệu chúng tôi cung cấp cho nó danh sách (parens) hoặc vectơ (dấu ngoặc) để làm việc. Nó chỉ thấy các đối số là chuỗi các sự vật. Ví dụ, công trình này:

(ns sample.core 
    [:gen-class] 
    [:require [clojure.set] 
      [clojure.string]]) 

require, như đã chỉ ra bởi amalloy trong các ý kiến, có ngữ nghĩa khác nhau cho vectơ và danh sách, do đó, không trộn những lên!

Cuối cùng, tại sao không có tác phẩm sau?

(ns sample.core 
    (:require 'clojure.string 'clojure.test)) 

Vâng, kể từ ns nào chúng tôi trích dẫn cho chúng ta, các biểu tượng này được trích dẫn hai lần, đó là ngữ nghĩa khác nhau từ được trích dẫn chỉ một lần và cũng có thể là sự điên loạn tinh khiết.

conj ; => #<core$conj [email protected]> 
'conj ; => conj 
''conj ; => (quote conj) 
'''conj ; => (quote (quote conj)) 

Tôi hy vọng điều này sẽ hữu ích và tôi khuyên bạn nên học cách viết macro. Họ rất vui.

+2

'(: require (clojure.set) (clojure.string))' không hoạt động chút nào. Nó là một no-op, xảy ra với * trông * giống như nó hoạt động vì bạn đã chọn hai không gian tên đã được yêu cầu. Hãy thử nó trên một số không gian tên không tồn tại: nó thành công âm thầm; trên các không gian tên mở rộng, nó âm thầm không làm gì cả. Sử dụng parens ở đây chỉ ra một danh sách tiền tố, như trong '(: require (chuỗi thiết lập clojure))'; cú pháp bạn đưa ra chỉ hoạt động với vectơ. – amalloy

+0

Cũng được phát hiện. Tôi sẽ chỉnh sửa bài đăng để phản ánh. –

+0

câu trả lời tuyệt vời và +1 cho tham chiếu TDT, nếu đó là nội dung của số – Hendekagon

4

TL; DR:

 
(ns sample.core 
    (:gen-class) 
    (:require clojure.set clojure.string)) 

 
(ns sample.core 
    (:gen-class) 
    (:require [clojure.set] 
      [clojure.string])) 

đều tốt - phiên bản thứ hai chỉ là một trường hợp đặc biệt của cú pháp linh hoạt nhất require hỗ trợ. Điều này cũng có thể được viết là:

 
(ns sample.core 
    (:gen-class) 
    (:require [clojure set string])) 

Nói chung, mẫu cuối cùng này là phương pháp tốt nhất cho yêu cầu cụ thể này.


(require 'clojure.string 'clojure.test)

Cũng làm việc trong một tập tin CLJ - thử điều này:

 
(ns sample.core 
    (:gen-class)) 
(require 'clojure.string 'clojure.test) 

Sự rắc rối ở đây là rằng trong ví dụ vỡ của bạn, bạn đang cố gắng sử dụng "biểu tượng trích dẫn" trong Điều khoản :require của macro ns. Đó có lẽ không phải là giải thích trực quan nhất, nhưng dưới đây là cách giải thích trực quan:

Có hai cách để yêu cầu các mô-đun khác, requirens.

require là chức năng cần danh sách các biểu mẫu được trích dẫn (cần phải "trích dẫn" để tránh bị che dấu tìm các ký hiệu bạn chuyển đến require cách thức thực hiện tất cả các biểu tượng khác).

ns là macro hỗ trợ tùy chọn :require. Nó lấy giá trị của tùy chọn này và, dưới nắp, biến nó thành một cuộc gọi đến hàm require. Bạn không cần phải báo giá trị của tùy chọn :requirens là macro và do đó có khả năng tự trích dẫn các ký hiệu.

Điều đó có thể vẫn chưa rõ ràng, nhưng tôi khuyên bạn nên chuyển sang tài liệu Clojure để làm rõ - khi bạn hiểu đầy đủ tất cả những gì bạn sẽ hiểu rõ hơn về Clojure nói chung.

Trong tệp nguồn Clojure, bạn phải luôn sử dụng mệnh đề ns để yêu cầu thư viện - require chỉ nên được sử dụng trong REPL.


Trong hai ví dụ cuối cùng của bạn, bạn đang ở đúng rằng

 
(ns sample.core 
    (:gen-class) 
    (require clojure.set clojure.string)) 

công trình, nhưng đây là một tai nạn - có lẽ là kết quả của sự kiện là

 
(name :require) 
=> "require" 

(name 'require) 
=> "require" 

Cú pháp ghi nhận là

 
(ns sample.core 
    (:gen-class) 
    (:require clojure.set clojure.string)) 

và là người duy nhất được bảo đảm không phá vỡ trong tương lai.

+0

"Trong các tệp nguồn Clojure, bạn nên luôn sử dụng mệnh đề ns để yêu cầu thư viện - yêu cầu chỉ nên được sử dụng trong REPL." Tại sao? –

+2

Không có lý do kỹ thuật - hoàn toàn là một phong cách và khả năng đọc. Sử dụng 'ns' luôn đảm bảo các lập trình viên khác có thể dễ dàng thấy được không gian tên nào được yêu cầu bởi một tệp mà không cần đào qua toàn bộ tệp. Nó cũng là một chút sạch hơn, vì bạn không cần phải thoát khỏi các hình thức bằng tay. – tvachon

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