2015-10-25 15 views
5

Tôi muốn macro xác định các hàm trả về biểu mẫu mà chúng được gọi là, ví dụ: (func 1 (a b)) trả lại (func 1 (a b)). Tôi cũng muốn cho phép xác minh đầu vào cho các chức năng này để đảm bảo rằng tôi không giới thiệu bất kỳ lỗi nào. (Các biểu mẫu này sau đó sẽ được đánh giá, nhưng mã đó vẫn chưa được viết.)Xác định hàm trong macro: không thể sử dụng tên đủ điều kiện làm tham số

Tôi vẫn gặp lỗi này.

(defmacro defecho 
    "Echo function call after asserting a few things about the input" 
    ([f] `(defecho ~f nil nil)) 
    ([f assertions] `(defecho ~f assertions nil)) 
    ([f assertions assert-failed-message] 
    `(defn ~f [& body]    ; define a function 
     ~(when-not (nil? assertions) ; if given a function for input validation 
     `(assert (~assertions body) ; define the function to assert this as true 
        ~assert-failed-message)) ; with a given error message 
     (conj body (quote ~f)))))  ; return the (f [email protected]) list 

(defecho my-test 
    #(< 2 (count %)) 
    "Must be greater than zero") 
  1. Unhandled clojure.lang.Compiler$CompilerException 
    Error compiling: 
    /private/var/...228.clj:1:1 
    Can't use qualified name as parameter: my-test/body 
    
  2. Caused by java.lang.RuntimeException 
    Can't use qualified name as parameter: my-test/body 
    

Trả lời

4

Bạn không thể sử dụng biểu tượng đủ điều kiện làm thông số chức năng. Quan sát rằng

`body 

đánh giá để current-namespace/body

Trong một cú pháp-báo giá, bạn luôn có thể unquote báo phi cú pháp để có được một biểu tượng không đủ tiêu chuẩn:

`~'body 

đánh giá để body. (Lưu ý rằng unquoting ở đây phục vụ đánh giá của bản thân trích dẫn bên trong).

Tuy nhiên, trong trường hợp này, bạn nên tạo biểu tượng thay vào đó, vì nếu người dùng sử dụng ký hiệu body trong e. g mã số assert-failed-message bạn không muốn ràng buộc của mình là body để bị che khuất với bạn (hãy quan sát rằng mã của anh ấy được đánh giá khi hàm được tạo thực sự được gọi).

Đó là thực tế phổ biến để tạo ra biểu tượng cho mục đích đó, hoặc sử dụng gensym hoặc một biểu tượng kết thúc bằng một băm, mà cú pháp quote sẽ mở rộng tới một cuộc gọi gensym ..

`body# 

đánh giá với (không đủ tiêu chuẩn!) biểu tượng body__34343__auto__ nơi số khác nhau trên mỗi lời gọi và được đảm bảo khác nhau mỗi lần.

Vì bạn đề cập đến nội dung từ trong hai dấu ngoặc kép khác nhau, tôi đã chọn tùy chọn gensym kết hợp với let để chỉ một biểu tượng được tạo.

(defmacro defecho ; overloads stripped for brevity 
    [f assertions assert-failed-message] 
    (let [args-sym (gensym "body")] ; define a symbol for function arglist 
    `(defn ~f [& ~args-sym]    ; define a function 
     ~(when-not (nil? assertions)  ; if given a function for input validation 
      `(assert (apply ~assertions ~args-sym) ; define the function to assert this as true 
        ~assert-failed-message)) ; with a given error message 
     (conj ~args-sym (quote ~f))))) 
+0

Bạn cũng có thể sử dụng cú pháp 'body #' để tự động gensym tên. – drnewman

+0

@drnewman Vì ví dụ này sử dụng 'body #' trong hai bối cảnh cú pháp trích dẫn khác nhau, các ký hiệu khác nhau sẽ được tạo ra. –

+0

Leon Grapenthin, bạn nói đúng, cảm ơn vì đã sửa – drnewman

0

Bạn có thể làm cho cuộc sống của bạn đơn giản hơn một chút bằng cách sử dụng một chức năng thích hợp cho việc nâng hạng nặng và sử dụng các macro chỉ cho cú pháp đường:

(defmacro defecho 
    "Echo function call after asserting a few things about the input" 
    ([f] `(defecho ~f nil nil)) 
    ([f assertions] `(defecho ~f assertions nil)) 
    ([f assertions assert-failed-message] 
    `(def ~f (echo-function (quote ~f) ~assertions ~assert-failed-message)))) 

(defn echo-function [f assertion assert-failed-message] 
    (fn [& body] 
    (when assertion 
     (assert (assertion body) 
       assert-failed-message)) 
    (conj body f))) 
Các vấn đề liên quan