2011-04-03 59 views
15

Làm cách nào để tôi có thể tạo macro Clojure hoạt động như một hàm, vì vậy tôi có thể chuyển macro đó thành một đối số chẳng hạn? Tôi mong đợi phải quấn nó bằng cách nào đó. Tôi không mong đợi phiên bản gói để hành xử chính xác giống như macro gốc (sự khác biệt của cuộc gọi theo tên so với cuộc gọi theo giá trị), nhưng nó sẽ hữu ích trong một vài trường hợp mà điều này không quan trọng.Xử lý macro Clojure làm chức năng

Trả lời

19

Nếu tôi hiểu bạn một cách chính xác, bạn chỉ có thể bọc nó trong một hàm.

Xem xét việc này (ngớ ngẩn) thực hiện một chức năng bình phương như một macro:

(defmacro square [x] 
    (list * x x)) 

Đi qua nó trực tiếp như một arg sẽ không hoạt động, như bạn đã biết:

user=> (map square [1 2 3 4 5]) 
java.lang.Exception: Can't take value of a macro: #'user/square (NO_SOURCE_FILE:8) 

Nhưng gói nó trong một chức năng sẽ thực hiện thủ thuật:

user=> (map #(square %) [1 2 3 4 5]) 
(1 4 9 16 25) 

Hoặc (và hơi xấu hơn một chút), bạn có thể thực hiện một macro khác để làm một gói chung chung hơn:

(defmacro make-fn [m] 
    `(fn [& args#] 
    (eval `(~'~m [email protected]#)))) 

user=> (map (make-fn square) [1 2 3 4 5]) 
(1 4 9 16 25) 

Tôi muốn gắn bó với gói chức năng bình thường và bỏ qua lần hack cuối cùng này! :)

+3

Hm, điều này không hoạt động đối với các macro biến thể: người dùng => (áp dụng # (hoặc%) [true false false]) java.lang.IllegalArgumentException: Số lượng sai số args (3) được chuyển đến: người dùng $ eval3 $ fn (NO_SOURCE_FILE: 0) – pauldoo

+2

3 điểm: (1) cách tốt nhất để giải quyết ví dụ này là '(một số danh tính [true false false])', (2) nếu bạn biết số tham số & làm rõ, nó ' sẽ làm thủ thuật: '(áp dụng # (hoặc% 1% 2% 3) [true false false])', (3) hacky 'make-fn' hoạt động:' (áp dụng (make-fn hoặc) [true false false]) ' – trptcolin

5

Có macro nguy hiểm, không dùng nữa mà bạn không bao giờ nên sử dụng. :-P

http://clojure.github.com/clojure-contrib/apply-macro-api.html

+0

Hm, điều này mang lại cho tôi một chồng tràn bộ nhớ khi tôi cố gắng ví dụ này trong Clojure 1.2: (? Áp dụng vĩ mô hoặc [đúng false false])) – pauldoo

+0

Ugh đó là sự thật, vấn đề là với chức năng lan truyền tin trong ns áp dụng-macro: nó là một bản sao của một clojure.core lỗi thời, nếu bạn thay thế nó bằng phiên bản thực tế [công việc hack] (https://gist.github.com/986321) – jneira

+0

Sự khác biệt là tò mò (phần còn lại [1]) ->(), (tiếp theo [1]) -> nil. Đầu tiên với nil? như là vị ngữ bảo vệ để dừng đệ quy làm tăng stackoverflow. – jneira

4

Đối với macro điên make-fn, cách bên dưới? Nó cũng hoạt động tốt và hy vọng dễ hiểu hơn.

(defmacro make-fn [m] 
`(fn [& args#] 
    (eval 
     (cons '~m args#)))) 
Các vấn đề liên quan