2009-12-01 38 views
8

Tôi muốn tạo một fn hoàn toàn khi chạy (tức là tên và biểu tượng arg được quyết định khi chạy, không phải trong mã) Cách tốt nhất để đạt được điều này là gì?Clojure: Cách tạo hàm tại thời gian chạy

Ví dụ: làm cách nào để triển khai chức năng sau?

(defn gen-fn [name arg-symbols body] 
... 
... 

mà sẽ được sử dụng như thế này:

(gen-fn "my-func-name" (symbol "x") (symbol "y") (println "this is body. x=" x)) 

Lưu ý rằng chức năng tên, args và cơ thể không được mã hóa nhưng có thể được quyết định trong thời gian chạy

Trả lời

12
(defn gen-fn 
    [n as b] 
    (let [n  (symbol n) 
     as  (vec (map symbol as)) 
     fn-value (eval `(fn ~n ~as ~b))] 
    (intern *ns* n fn-value)))

Và một số sử dụng :

user=> (gen-fn "foo" ["x"] '(do (println x) (println (inc x)))) 
#'user/foo 
user=> (foo 5) 
5 
6 
nil

Tuy nhiên, Tôi không thực sự thích cách tiếp cận này. Nó có mùi thực sự khó khăn: eval. Tại sao bạn muốn tạo các hình cầu trong thời gian chạy? Tôi thấy các vấn đề khác nhau với không gian tên sai và trục trặc xấu xí khác tăng ở đường chân trời ...

+0

Yes. Đây là thứ xấu xí. Lý do tôi cần điều này là tôi đang thử nghiệm với GeneticProgramming dưới Clojure. Clojure có vẻ thực sự tự nhiên cho việc này. – GabiMe

+0

Ah. Được. GP có thể là sử dụng hợp pháp của 'eval'. Nhưng vẫn cảnh giác với những hiệu ứng lạ với 'eval'. – kotarak

+0

lý do tại sao eval? bạn không thể làm điều này với một macro? –

0

Tôi không hoàn toàn chắc chắn nhưng tôi tin rằng bạn có thể làm điều này với macro sẽ vượt trội hơn eval.

(defmacro gen-fn 
    [n as b] 
    (let [n (symbol n) 
     as (vec (map symbol as))] 
    `(intern *ns* n (fn ~n ~as [email protected])))) 
+1

Điều này không hoạt động khi chạy. Vấn đề là: bạn cần biết trước 'n',' as' và 'b'. Và sau đó bạn có thể làm: (defmacro gen-fn [n as b] '(defn ~(symbol n) ~(vec (map symbol as)) [email protected])). Với 'eval'-phương pháp tiếp cận các đối số để' gen-fn' có thể là kết quả của tính toán tùy ý. – kotarak

1

Một cách khác là sử dụng "eval" và "đọc-string":

user => (def f1 (eval (read-string "(fn [xy] (* xy))")))

# 'user/f1

user => (f1 3 5)

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