Một khía cạnh thú vị của macro là chúng cung cấp cho bạn khả năng mở rộng cú pháp của lisp và thêm các tính năng cú pháp mới vào nó, và điều này chỉ xảy ra vì các đối số được truyền tới macro sẽ chỉ được đánh giá trong thời gian chạy chứ không phải tại thời điểm biên dịch macro của bạn. Như một ví dụ, các macro luồng đầu tiên / cuối cùng trong clojure -> ->>
không đánh giá các đối số biểu thức của chúng, nếu không, các kết quả được đánh giá này không thể chấp nhận bất cứ điều gì nữa (nói (+ 3) => 3
và 3
không phải là một hàm có thể chấp nhận đối số chính đầu tiên của bạn trong một cái gì đó như (-> 4 (+ 3))
).
Bây giờ nếu tôi thích cú pháp này và tôi muốn thêm nó vào triển khai Lisp chung của tôi (không có nó) tôi có thể thêm nó bằng cách tự xác định một macro. Một cái gì đó như thế này:
;;; in SBCL
;;; first thread:
(defmacro -> (x &rest more)
(loop for m in more
for n = `(,(first m) ,x ,@(rest m)) then `(,(first m) ,n ,@(rest m))
finally (return n)))
;;; last thread:
(defmacro ->> (x &rest more)
(loop for m in more
for n = `(,(first m) ,@(rest m) ,x) then `(,(first m) ,@(rest m) ,n)
finally (return n)))
Bây giờ tôi sẽ có thể sử dụng chúng trong Common Lisp giống như trong Clojure:
(-> #'+
(mapcar '(2 3 4) '(1 2 3))) ;; => (3 5 7)
(->> #'>
(sort '(9 8 3 5 7 2 4))) ;; => (9 8 7 5 4 3 2)
Ngoài ra, có thể bạn muốn có một cú pháp mới cho range
chức năng của clojure với các từ khóa của riêng bạn cho cú pháp của bạn, đại loại như:
(from 0 to 10) ;=> (0 1 2 3 4 5 6 7 8 9)
(from 0 to 10 by 0.5) ;;=> (0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5)
có thể được định nghĩa như macro này:
(defmacro from
"Just another range!"
[x y z & more]
`(when (= '~y '~'to)
(if '~more (range ~x ~z (second '~more))
(range ~x ~z))))
hoặc thêm một cái gì đó tương tự như Common Lisp (tất nhiên chỉ là vì tất cả những thứ này đã có thể được thực hiện bằng các ngôn ngữ của khóa học!):
(defmacro from (x y z &rest r)
`(when (eql ',y 'to)
(if (and ',r (eql (first ',r) 'by))
(loop for i from ,x to ,z by (second ',r) collect i)
(loop for i from ,x to ,z collect i))))
(from 0 to 10) ;=> (0 1 2 3 4 5 6 7 8 9 10)
(from 0 to 5 by 1/2) ;=> (0 1/2 1 3/2 2 5/2 3 7/2 4 9/2 5)