Ví dụ của bạn không hoạt động.
Nó có thể hoạt động trong thông dịch viên. Nhưng với trình biên dịch, bạn sẽ thấy một vòng lặp vô tận trong quá trình biên dịch.
CL-USER 23 > (defun test (foo)
(sum-int-seq 5))
TEST
Hãy sử dụng LispWorks thông dịch viên:
CL-USER 24 > (test :foo)
15
Hãy thử biên dịch các chức năng:
CL-USER 25 > (compile 'test)
Stack overflow (stack size 15997).
1 (continue) Extend stack by 50%.
2 Extend stack by 300%.
3 (abort) Return to level 0.
4 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
Vì vậy, bây giờ câu hỏi tiếp theo: tại sao nó làm việc trong các thông dịch viên, nhưng trình biên dịch không thể biên dịch nó?
OK, tôi sẽ giải thích.
Hãy nhìn vào thông dịch viên trước.
- nó thấy
(sum-int-seq 5)
.
- macro này sẽ mở rộng nó thành
(COND ((EQUAL 0 5) 0) (T (+ 5 (SUM-INT-SEQ (- 5 1)))))
.
- rồi đánh giá biểu mẫu ở trên. Nó xác định rằng nó cần phải tính toán
(+ 5 (SUM-INT-SEQ (- 5 1)))
. Cho rằng nó cần phải macroexpand (SUM-INT-SEQ (- 5 1))
.
- cuối cùng nó sẽ mở rộng thành một cái gì đó như
(cond ((EQUAL 0 (- (- (- (- (- 5 1) 1) 1) 1) 1)) 0) ...
. Mà sau đó sẽ trở về 0 và tính toán có thể sử dụng kết quả này và thêm các điều khoản khác vào nó.
Trình thông dịch nhận mã, đánh giá những gì có thể và macro mở rộng nếu cần. Mã được tạo sau đó được đánh giá hoặc macroexpanded. Và cứ thế.
Bây giờ, hãy xem trình biên dịch.
- nó thấy (sum-int-seq 5) và macro mở rộng nó thành
(COND ((EQUAL 0 5) 0) (T (+ 5 (SUM-INT-SEQ (- 5 1)))))
.
- giờ việc mở rộng macro sẽ được thực hiện trên các biểu mẫu con, cuối cùng.
- trình biên dịch sẽ macroexpand
(SUM-INT-SEQ (- 5 1))
. lưu ý rằng mã không bao giờ được đánh giá, chỉ được mở rộng.
- trình biên dịch sẽ macroexpand
(SUM-INT-SEQ (- (- 5 1) 1))
v.v. cuối cùng bạn sẽ thấy tràn ngăn xếp.
Trình biên dịch sẽ chuyển tiếp (đệ quy biên dịch/mở rộng) mã. Nó có thể không thực thi mã (trừ khi nó tối ưu hóa hoặc macro thực sự đánh giá nó một cách rõ ràng).
Đối với macro đệ quy, bạn cần phải thực sự đếm ngược. Nếu bạn đánh giá bên trong macro, thì một cái gì đó như (sum-int-seq 5)
có thể được thực hiện. Nhưng đối với (defun foo (n) (sum-int-seq n))
điều này là vô vọng, vì trình biên dịch không biết giá trị của n là gì.
Nguồn
2011-11-22 22:32:14
Rất tiếc, cảm ơn bạn đã trả lời kỹ lưỡng. – snowape