Một trang trí về cơ bản chỉ là một chức năng .
Ví dụ trong Lisp chung:
(defun attributes (keywords function)
(loop for (key value) in keywords
do (setf (get function key) value))
function)
Ở trên hàm là một ký hiệu (sẽ được trả về DEFUN
) và chúng ta đặt các thuộc tính vào danh sách thuộc tính của ký hiệu .
Bây giờ chúng ta có thể viết nó xung quanh một định nghĩa hàm:
(attributes
'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
)
Nếu chúng ta muốn thêm một cú pháp ưa thích như trong Python, chúng ta viết một macro đọc . Một macro người đọc cho phép chúng ta lập trình theo cấp độ cú pháp biểu thức s:
(set-macro-character
#\@
(lambda (stream char)
(let ((decorator (read stream))
(arg (read stream))
(form (read stream)))
`(,decorator ,arg ,form))))
Sau đó chúng ta có thể viết:
@attributes'((version-added "2.2")
(author "Rainer Joswig"))
(defun foo (a b)
(+ a b))
Người đọc Lisp đọc ở trên để:
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Bây giờ chúng tôi có một hình thức trang trí trong Common Lisp.
Kết hợp các macro và macro đọc.
Trên thực tế tôi sẽ thực hiện dịch trên mã thực bằng cách sử dụng macro chứ không phải hàm.
(defmacro defdecorator (decorator arg form)
`(progn
,form
(,decorator ,arg ',(second form))))
(set-macro-character
#\@
(lambda (stream char)
(declare (ignore char))
(let* ((decorator (read stream))
(arg (read stream))
(form (read stream)))
`(defdecorator ,decorator ,arg ,form))))
Việc sử dụng là như trên với cùng một macro đọc. Ưu điểm là trình biên dịch Lisp vẫn xem nó như một dạng được gọi là cấp cao nhất - trình biên dịch tệp * xử lý các biểu mẫu cấp cao nhất, ví dụ: nó thêm thông tin về chúng vào môi trường thời gian biên dịch . Trong ví dụ trên, chúng ta có thể thấy rằng macro nhìn vào mã nguồn và trích xuất tên.
Trình đọc Lisp đọc ví dụ trên vào:
(DEFDECORATOR ATTRIBUTES
(QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(DEFUN FOO (A B) (+ A B)))
Mà sau đó được macro mở rộng thành:
(PROGN (DEFUN FOO (A B) (+ A B))
(ATTRIBUTES (QUOTE ((VERSION-ADDED "2.2")
(AUTHOR "Rainer Joswig")))
(QUOTE FOO)))
Macro rất khác với macro đọc .
Macro nhận được mã nguồn được thông qua, có thể làm bất cứ điều gì họ muốn và sau đó trả lại mã nguồn. Nguồn đầu vào không cần phải là mã Lisp hợp lệ. Nó có thể là bất cứ điều gì và nó có thể được viết hoàn toàn khác nhau. Kết quả phải là mã Lisp hợp lệ sau đó. Nhưng nếu mã được tạo cũng sử dụng macro, thì cú pháp của mã được nhúng trong lệnh gọi macro lại có thể là một cú pháp khác. Một ví dụ đơn giản: người ta có thể viết một macro toán học chấp nhận một số loại cú pháp toán học:
(math y = 3 x ^ 2 - 4 x + 3)
Biểu thức y = 3 x ^ 2 - 4 x + 3
không phải là mã Lisp hợp lệ, nhưng macro có thể phân tích cú pháp và trả về mã Lisp hợp lệ như thế này:
(setq y (+ (* 3 (expt x 2))
(- (* 4 x))
3))
Có nhiều trường hợp sử dụng macro khác trong Lisp.