Tôi đang cố gắng học Lisp từ cuốn sách "Practical Common Lisp" của Peter Seibel. hiện hữu chương 8: "Macro: Xác định của riêng bạn" , Tôi đã xem qua macro one-shot này. Ở cuối trang, phần triển khai được đưa ra. Đối với tôi đây có vẻ là một macro rất phức tạp, nên tôi thấy câu hỏi này trên stackoverflowCó một số lời giải thích tốt ngoài kia.
Tuy nhiên, ngay cả khi tôi không (vẫn) hiểu đầy đủ về macro này, tôi vẫn hiểu mục đích của nó. Vì vậy, tôi đã thử viết cách triển khai của riêng mình:
(defmacro my-once-only ((&rest name) &body body)
(cho phép
(
(gensyms (vòng lặp cho n trong tên thu thập (gensym)))
)
`(danh sách 'hãy
(list ,@(loop for n in name for g in gensyms thu thập `(list ',g ,n)))
(cho phép
,(vòng lặp cho n trong tên cho g trong gensyms thu thập `(,n ',g))
,@thân hình))))
(Hãy tha thứ cho tôi nếu tôi không tuân theo các quy ước thụt lề lisp tiêu chuẩn, tôi đang cố gắng thụt mã theo một cách nhất định để tôi có thể hiểu nó đi đâu, vì tôi chưa quen với nó)
Tôi đã thử nghiệm macro này theo cách tương tự như được mô tả trong chương mà tôi đã liên kết tới, tức là. Gọi hàm với các đối số như (ngẫu nhiên 100) để nếu chúng được đánh giá hai lần thì kết quả sẽ sai. Tôi cũng đã mở rộng các macro của mình (và các macro tôi đang sử dụng trong chúng) thông qua macroexpand/macroexpand-1 và điều đó dường như cũng đúng.
Vì vậy, tôi muốn biết liệu việc thực hiện của tôi có đúng không? Hay tôi đang thiếu thứ gì đó (tôi nghĩ là có thể) ...
Chúng ta hãy thực sự mở rộng vĩ mô hai cách triển khai này và xem chúng khác nhau như thế nào:
* (macroexpand '(chỉ một lần (thanh foo) (+ thanh foo)))
(LET ((#:G619 (GENSYM)) (#:G620 (GENSYM)))
`(LET ((,#:G619 ,FOO) (,#:G620 ,BAR))
,(LET ((FOO #:G619) (BAR #:G620))
(+ THANH FOO))))
* (macroexpand '(chỉ một lần của tôi (thanh foo) (+ thanh foo)))
(LIST 'LET (LIST (LIST '#:G621 FOO) (LIST '#:G622 BAR))
(LET ((FOO '#:G621) (BAR '#:G622))
(+ THANH FOO)))
Hãy viết lại bản mở rộng macro của bạn thành thứ gì đó Lisper có thể dễ đọc hơn:
`(LET ((#:G621 ,FOO) (#:G622 ,BAR))
,(LET ((FOO '#:G621) (BAR '#:G622))
(+ THANH FOO)))
Xin lưu ý rằng phiên bản của bạn thiếu phần bổ sung gensym
địa chỉ gián tiếp. Điều này có nghĩa là mỗi khi macro bên ngoài được gọi (sử dụng chỉ-một-của-tôi
macro) sử dụng các ký hiệu giống nhau mọi lúc. Nếu lệnh gọi macro của bạn được lồng nhau (ví dụ: bạn sử dụng macro bên ngoài trong nội dung khác sử dụng macro bên ngoài), các ký hiệu sẽ xung đột.
Tôi là một lập trình viên xuất sắc, rất giỏi!