2010-03-21 36 views
16

Đưa ra danh sách tên cho các biến, tôi muốn đặt các biến đó thành một biểu thức.Trong Clojure, làm thế nào để xác định một biến được đặt tên bởi một chuỗi?

Tôi cố gắng này:

(doall (for [x ["a" "b" "c"]] (def (symbol x) 666))) 

... nhưng điều này mang lại các lỗi

java.lang.Exception: đối số đầu tiên để def phải là một Symbol

Can bất cứ ai cho tôi thấy đúng cách để thực hiện điều này, xin vui lòng?

Trả lời

30

Clojure của "thực tập" chức năng là cho mục đích này:

(doseq [x ["a" "b" "c"]] 
    (intern *ns* (symbol x) 666)) 
+0

Câu trả lời ngắn gọn và đơn giản nhất (có thể?). Tôi đã đưa cho bạn dấu tick và hy vọng sepp2k không phải là điên về mất nó. Cảm ơn! –

13
(doall (for [x ["a" "b" "c"]] (eval `(def ~(symbol x) 666)))) 

Đáp lại bình luận của bạn:

Không có macro liên quan ở đây. eval là một hàm nhận danh sách và trả về kết quả thực thi danh sách đó dưới dạng mã. `và ~ là các phím tắt để tạo danh sách được trích dẫn một phần.

'có nghĩa là nội dung của danh sách dưới đây được trích dẫn trừ khi trước bởi một ~

~ danh sách sau đây là một lời gọi hàm đó sẽ được thực thi, không trích dẫn.

Vì vậy `` (def ~ (ký hiệu x) 666) is the list containing the symbol def , followed by the result of executing biểu tượng x followed by the number of the beast. I could as well have written (eval (danh sách 'def (symbol x) 666)) `để đạt được hiệu quả tương tự.

+0

tôi biết câu trả lời này tham gia eval bằng cách nào đó nhưng quên về ~ cấu trúc. –

+0

Vâng, ngày nào cũng biết, nó chỉ hoạt động! Tôi đã không nghĩ rằng nó là cần thiết để giảm đến mức vĩ mô để thực hiện điều này, nhưng tôi vui mừng để bao gồm điều này trong "sách dạy nấu ăn của tôi cho những thứ tôi không hoàn toàn hiểu". Cảm ơn bạn! –

+2

@CarlSmotricz: Tôi vừa thêm một số giải thích cho mã. Hy vọng rằng bạn hiểu đầy đủ hơn bây giờ. – sepp2k

6

Đã cập nhật để nhận xét của Stuart Sierra (đề cập clojure.core/intern) vào tài khoản.

Sử dụng eval đây là điều tốt, nhưng có thể thú vị khi biết rằng nó không cần thiết, bất kể Vars có tồn tại hay không. Trong thực tế, nếu chúng được biết là tồn tại, thì tôi nghĩ giải pháp alter-var-root dưới đây là sạch hơn; nếu chúng có thể không tồn tại, thì tôi sẽ khăng khăng yêu cầu thay thế của tôi là sạch hơn nhiều, nhưng nó dường như làm cho mã ngắn nhất (nếu chúng ta bỏ qua chi phí của ba dòng cho định nghĩa hàm), vì vậy tôi sẽ đăng nó để bạn xem xét.


Nếu Var được biết là tồn tại:

(alter-var-root (resolve (symbol "foo")) (constantly new-value)) 

Vì vậy, bạn có thể làm

(dorun 
    (map #(-> %1 symbol resolve (alter-var-root %2)) 
     ["x" "y" "z"] 
     [value-for-x value-for-y value-for z])) 

(Nếu giá trị tương tự đã được sử dụng cho tất cả Vars, bạn có thể sử dụng (repeat value) cho đối số cuối cùng để ánh xạ hoặc chỉ đặt nó vào hàm ẩn danh.)


Nếu Vars có thể cần phải được tạo ra, sau đó bạn có thể thực sự viết một chức năng để làm điều này (một lần nữa, tôi sẽ không nhất thiết phải khẳng định điều này là sạch hơn eval, nhưng dù sao - chỉ cho vì lợi ích của nó):

(defn create-var 
    ;; I used clojure.lang.Var/intern in the original answer, 
    ;; but as Stuart Sierra has pointed out in a comment, 
    ;; a Clojure built-in is available to accomplish the same 
    ;; thing 
    ([sym] (intern *ns* sym)) 
    ([sym val] (intern *ns* sym val))) 

Lưu ý rằng nếu một Var hóa ra đã được thực tập nội trú với tên được đặt trong không gian tên nào đó, thì đây có gì thay đổi trong trường hợp đối số duy nhất hoặc chỉ reset Var đến đưa ra giá trị mới trong hai trường hợp đối số. Với điều này, bạn có thể giải quyết vấn đề ban đầu như vậy:

(dorun (map #(create-var (symbol %) 666) ["x" "y" "z"])) 

Một số ví dụ bổ sung:

user> (create-var 'bar (fn [_] :bar)) 
#'user/bar 
user> (bar :foo) 
:bar 

user> (create-var 'baz) 
#'user/baz 
user> baz 
; Evaluation aborted. ; java.lang.IllegalStateException: 
         ; Var user/baz is unbound. 
         ; It does exist, though! 

;; if you really wanted to do things like this, you'd 
;; actually use the clojure.contrib.with-ns/with-ns macro 
user> (binding [*ns* (the-ns 'quux)] 
     (create-var 'foobar 5)) 
#'quux/foobar 
user> quux/foobar 
5 
+0

Rất hay và rất thông tin, cảm ơn rất nhiều! Thoạt nhìn, điều này có vẻ như đang làm mã cho 'def'; Tôi đoán đó là một "ngã ba" 'def'. Tôi có thể sử dụng nó trong dự án mini tiếp theo của mình. –

+1

Theo quy tắc: Không sử dụng eval cho đến khi cần thiết và bạn biết mình đang làm gì. Tôi sẽ đi với thực tập để giải quyết vấn đề ở bàn tay; câu trả lời này cho thấy độc đáo rằng eval là không cần thiết ở đây. – danlei

+2

Var.intern có sẵn như là chức năng Clojure tích hợp "thực tập" –

3

quy tắc đánh giá cho các cuộc gọi chức năng bình thường là để đánh giá tất cả các mục của danh sách, và gọi là người đầu tiên mục trong danh sách dưới dạng hàm với phần còn lại của các mục trong danh sách dưới dạng tham số.

Nhưng bạn không thể đưa ra bất kỳ giả định nào về quy tắc đánh giá cho các biểu mẫu hoặc macro đặc biệt. Một hình thức đặc biệt hoặc mã được tạo ra bởi một cuộc gọi macro có thể đánh giá tất cả các đối số, hoặc không bao giờ đánh giá chúng, hoặc đánh giá chúng nhiều lần, hoặc đánh giá một số đối số chứ không phải các đối số khác. def là một dạng đặc biệt và nó không đánh giá đối số đầu tiên của nó. Nếu nó đã làm, nó không thể làm việc. Việc đánh giá foo trong (def foo 123) sẽ dẫn đến lỗi "không có lỗi '' foo '" hầu hết thời gian (nếu foo đã được xác định, có thể bạn sẽ không tự định nghĩa nó).

Tôi không chắc chắn bạn đang sử dụng cái này cho cái gì, nhưng nó không có vẻ rất thành ngữ. Sử dụng def ở bất kỳ nơi nào nhưng ở mức độ phức tạp của chương trình của bạn thường có nghĩa là bạn đang làm điều gì đó sai.

(Lưu ý:. doall + for = doseq)

+0

Cảm ơn lời nhắc về 'doseq'! Tôi đang xác định một số ít các biến ở cấp cao nhất và chỉ muốn giảm bớt việc gõ. –

+2

Tại sao không sử dụng các ký hiệu để bắt đầu bằng, thay vì chuỗi? –

+0

Bởi vì tên tôi đã dự định sử dụng đã được tính toán (bằng cách nối, natch).Tôi nhấn mạnh rằng tôi làm điều này chỉ cho mục đích thử nghiệm, "nhanh chóng và bẩn" mã; có nhiều cách tốt hơn để thể hiện những gì tôi đang làm. Tôi đã đăng câu hỏi này chỉ vì tôi muốn thực hiện điều này trong mã dự thảo ban đầu của mình và đã bực mình rằng ngôn ngữ Clojure tuyệt vời khác dường như không hỗ trợ cho jiggerpokery cụ thể này. –

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