Để đưa ra câu trả lời ngắn, các macro được sử dụng để xác định các phần mở rộng cú pháp ngôn ngữ cho Lisp thông thường hoặc Ngôn ngữ cụ thể miền (DSL). Các ngôn ngữ này được nhúng ngay vào mã Lisp hiện có. Bây giờ, DSL có thể có cú pháp tương tự Lisp (như Phiên dịch Prolog của Peter Norvig cho Lisp thông thường) hoặc hoàn toàn khác nhau (ví dụ: Infix Notation Math for Clojure).
Dưới đây là một ví dụ cụ thể hơn:
Python có danh sách hiểu được tích hợp vào ngôn ngữ. Điều này đưa ra một cú pháp đơn giản cho một trường hợp phổ biến. Dòng
divisibleByTwo = [x for x in range(10) if x % 2 == 0]
mang lại một danh sách chứa tất cả các số chẵn từ 0 đến 9. Quay lại Python 1,5 ngày không có cú pháp như vậy; bạn sẽ sử dụng một cái gì đó giống như thế này:
divisibleByTwo = []
for x in range( 10 ):
if x % 2 == 0:
divisibleByTwo.append( x )
Cả hai đều tương đương về chức năng. Chúng ta hãy viện dẫn sự đình chỉ của chúng ta về sự hoài nghi và giả vờ Lisp có một macro vòng lặp rất hạn chế chỉ lặp đi lặp lại và không có cách nào dễ dàng để làm tương đương với việc hiểu danh sách.
Trong Lisp bạn có thể viết như sau. Tôi nên lưu ý ví dụ giả định này được chọn là giống hệt với mã Python không phải là một ví dụ hay về mã Lisp.
;; the following two functions just make equivalent of Python's range function
;; you can safely ignore them unless you are running this code
(defun range-helper (x)
(if (= x 0)
(list x)
(cons x (range-helper (- x 1)))))
(defun range (x)
(reverse (range-helper (- x 1))))
;; equivalent to the python example:
;; define a variable
(defvar divisibleByTwo nil)
;; loop from 0 upto and including 9
(loop for x in (range 10)
;; test for divisibility by two
if (= (mod x 2) 0)
;; append to the list
do (setq divisibleByTwo (append divisibleByTwo (list x))))
Trước khi tôi đi xa hơn, tôi nên giải thích rõ hơn về macro là gì. Nó là một phép biến đổi được thực hiện trên mã theo mã. Đó là, một đoạn mã, được đọc bởi trình thông dịch (hoặc trình biên dịch), lấy mã làm đối số, thao tác và trả về kết quả, sau đó được chạy tại chỗ.
Tất nhiên đó là rất nhiều đánh máy và lập trình viên là lười biếng. Vì vậy, chúng tôi có thể định nghĩa DSL để thực hiện việc hiểu danh sách. Trên thực tế, chúng tôi đang sử dụng một macro (macro vòng lặp).
Lisp định nghĩa một vài hình thức cú pháp đặc biệt. Trích dẫn ( '
) chỉ ra mã thông báo tiếp theo là một nghĩa đen. Quasiquote hoặc backtick ( `
) chỉ ra mã thông báo tiếp theo là một nghĩa đen với các lần thoát. Thoát hiểm được chỉ định bởi toán tử dấu phẩy. Nghĩa đen '(1 2 3)
là tương đương với Python [1, 2, 3]
. Bạn có thể gán nó cho một biến khác hoặc sử dụng nó tại chỗ. Bạn có thể nghĩ `(1 2 ,x)
tương đương với Python trong [1, 2, x]
đó x
một biến được xác định trước đó. Ký hiệu danh sách này là một phần của phép thuật đi vào macro. Phần thứ hai là trình đọc Lisp thay thế macro một cách thông minh cho mã nhưng điều đó được minh họa rõ nhất dưới đây:
Vì vậy, chúng ta có thể định nghĩa một macro được gọi là lcomp
(viết tắt của danh sách hiểu). Cú pháp của nó sẽ giống hệt như con trăn mà chúng ta đã sử dụng trong ví dụ [x for x in range(10) if x % 2 == 0]
-(lcomp x for x in (range 10) if (= (% x 2) 0))
(defmacro lcomp (expression for var in list conditional conditional-test)
;; create a unique variable name for the result
(let ((result (gensym)))
;; the arguments are really code so we can substitute them
;; store nil in the unique variable name generated above
`(let ((,result nil))
;; var is a variable name
;; list is the list literal we are suppose to iterate over
(loop for ,var in ,list
;; conditional is if or unless
;; conditional-test is (= (mod x 2) 0) in our examples
,conditional ,conditional-test
;; and this is the action from the earlier lisp example
;; result = result + [x] in python
do (setq ,result (append ,result (list ,expression))))
;; return the result
,result)))
Bây giờ chúng ta có thể thực thi tại dòng lệnh:
CL-USER> (lcomp x for x in (range 10) if (= (mod x 2) 0))
(0 2 4 6 8)
Khá gọn gàng hả? Bây giờ nó không dừng lại ở đó. Bạn có một cơ chế, hoặc một cây cọ vẽ, nếu bạn thích. Bạn có thể có bất kỳ cú pháp nào bạn có thể muốn. Giống như with
cú pháp của Python hoặc C # . Hoặc cú pháp LINQ của .NET. Cuối cùng, đây là điều thu hút mọi người đến với Lisp - sự linh hoạt tối thượng.