2013-07-24 18 views
6

Mã Lisp/Clojure có tính nhất quán trong cú pháp của chúng và đó là điểm cộng vì không cần hiểu nhiều cấu trúc khác nhau. Nhưng đôi khi dễ hiểu hơn bằng cách nhìn vào đoạn mã chỉ bằng cú pháp khác được sử dụng như đây là trường hợp chuyển đổi hoặc đây là mẫu phù hợp với cấu trúc v.v.Cách xác định biểu mẫu nào là macro và các hàm nào đang hoạt động khi xem mã Clojure?

Tôi đã bắt đầu với Clojure vài tháng trước và tôi nhận ra rằng tôi không thể hiểu mã mà không đọc tên của biểu mẫu và sau đó googling cho dù đó là macro hay hàm và cách hoạt động.

Vì vậy, hóa ra, một đoạn mã Clojure, không phân biệt tính thống nhất của cú pháp không đồng nhất.

Nó có vẻ giống như một hàm nhưng nếu ở tất cả thì đó là một macro thì nó có thể không đánh giá tất cả các đối số của nó.

Có quy ước đặt tên hoặc phong cách thụt đầu dòng mà tất cả các macro sử dụng để dễ dàng hơn cho ai đó nắm bắt được tên của những gì đang diễn ra?

+1

Câu hỏi hay. Tôi không nghĩ rằng có một cách để biết sự khác biệt chỉ bằng cách nhìn. Là người mới bắt đầu, bạn đã nói cú pháp rất đơn giản, mọi thứ đều là (fn args), vậy thì bạn hỏi tại sao không (áp dụng hoặc thu thập)? Hoặc tại sao không (bản đồ trích dẫn bản đồ)? Ồ, chúng là các macro (hoặc các dạng đặc biệt). Bạn chỉ cần học đoán cái nào. – Hendekagon

+0

hehe: (fn? +) True (fn? ->) Không thể lấy giá trị của macro! Và, không có macro? hoặc dạng đặc biệt? Nhưng, (: macro (meta # '->)) true – Hendekagon

+0

@Hendekagon: Đây là thứ mà người ta học với kinh nghiệm, nhưng nó không đơn giản * đoán * người đó học, mà đúng hơn là làm * dự đoán được giáo dục * (thường với (gần) chắc chắn). Lấy '(map quote coll)' làm ví dụ, 'quote' có thể không phải là một hàm vì các thuộc tính cơ bản của ngôn ngữ, và trong mọi trường hợp' (map quote coll) 'là một hàm nhận' coll' post đánh giá, do đó, nó không rõ ràng những gì được dự định (mặc dù '(bản đồ # (danh sách 'quote%) ...)' có thể khá hữu ích trong một định nghĩa macro, miễn là '...' là một bộ sưu tập vĩ mô có truy cập vào). –

Trả lời

4

Trực giác hữu ích nhất theo ý kiến ​​của tôi là từ việc hiểu mục đích của nhà điều hành/Var đã cho. Các macro được thiết kế tốt không thể được viết dưới dạng hàm và vẫn cung cấp cùng chức năng với cùng cú pháp, nếu chúng có thể, chúng sẽ được viết dưới dạng hàm (xem phần "được thiết kế tốt" ở trên!). Vì vậy, nếu bạn đang đối phó với một cấu trúc mà không thể có thể là một chức năng bình thường, sau đó bạn biết nó không phải là; nếu không nó có thể là.

Ngoài ra, cách thông thường để tìm hiểu về Vars được xuất bởi thư viện cho bạn biết liệu bạn đang xử lý macro hay hàm ở phía trước. Điều đó đúng với số doc ((doc foo) nói rằng foo là macro gần đầu đầu ra nếu đó thực sự là trường hợp), source (vì nó cung cấp cho bạn toàn bộ mã) và M-. (chuyển đến định nghĩa trong Emacs với nrepl.el hoặc swank-clojure; M-, nhảy ngược lại). Tài liệu có thể được đề cập đến là macro và cái gì không (ngoại trừ điều đó không nhất thiết đúng với docstrings, vì tất cả các cách thông thường để truy cập một docstring đã cho bạn biết liệu bạn đang xử lý macro, như đã giải thích ở trên).

Nếu bạn đang lướt qua một đoạn mã với ý định tạo thành một sự hiểu biết sơ bộ về những gì nó có thể làm với giả định rằng các toán tử khác nhau thực hiện các chức năng được đề xuất theo tên của chúng, thì (1) đủ và bạn có ý tưởng về những gì được dự định bởi mã, vì vậy bạn thậm chí không cần phải quan tâm đến nhà khai thác nào là macro hoặc (2) tên không đủ gợi ý, vì vậy bạn sẽ cần phải đi sâu vào tài liệu hoặc nguồn cho một số toán tử, và sau đó điều đầu tiên bạn sẽ học là cái nào trong số chúng được đăng ký dưới dạng macro.

Cuối cùng, không có kiểu đặt tên duy nhất cho macro, mặc dù có một số quy ước cụ thể cho các trường hợp sử dụng cụ thể. Ví dụ: with-foo cấu trúc kiểu có xu hướng là các macro tiện lợi có mục đích là đơn giản hóa việc xử lý các tài nguyên thuộc loại foo; dofoo cấu trúc kiểu có xu hướng là macro lấy nội dung biểu thức được thực thi (bao nhiêu lần và bối cảnh bổ sung được thiết lập tùy thuộc vào macro; thành viên cơ bản nhất của họ này, do, thực sự là biểu mẫu đặc biệt thay vì một macro); Cấu trúc kiểu dáng deffoo giới thiệu các thực thể Vars hoặc kiểu giống mới.

Điều đáng nói là các mẫu tương tự đôi khi bị hỏng. Ví dụ, hầu hết các cấu trúc luồng (-> & Co.) là các macro, nhưng xml-> từ clojure.data.zip.xml là một hàm. Điều đó có ý nghĩa hoàn hảo khi người ta xem xét chức năng được cung cấp, điều đó đưa chúng ta trở lại điểm về mục đích của một nhà điều hành là nguồn trực giác hữu ích nhất.


Có thể có một số ngoại lệ cho quy tắc này. Người ta sẽ mong đợi những tài liệu này. Một số dự án dĩ nhiên không được ghi chép (hoặc rất gần như vậy); ở đây vấn đề biến mất hoàn toàn, vì người ta phải đi đến nguồn để hiểu mọi thứ.

0

Theo như tôi biết không có quy ước đặt tên được thi hành.

Theo quy tắc chung, các chức năng được ưu tiên bất cứ khi nào có thể, nhưng đôi khi macro có thể được phát hiện khi họ theo mẫu def<something> để thiết lập một cái gì đó hoặc with-<resource> để làm điều gì đó với tài nguyên mở.

Vì lý do này, bạn có thể tìm thấy tiện ích vĩ mô của clojure là doc. Nó sẽ cho bạn biết một form là một macro/function/form đặc biệt, cũng như cung cấp cho nó danh sách arg và chuỗi doc (nếu có). Ví dụ:

(use 'clojure.repl) 
(doc and) 

Sẽ in nội dung sau đây để thay thế.

clojure.core/và

([] [x] [x & tiếp theo])

Macro

Ước lượng exprs cùng một lúc, từ trái sang phải. Nếu biểu mẫu trả về false false (nil hoặc false) và trả về giá trị đó và không đánh giá bất kỳ biểu thức nào khác, ngược lại trả về giá trị của expr cuối cùng. (và) trả về đúng sự thật.

Một số người chỉnh sửa (ví dụ: emacs) sẽ cung cấp tài liệu này dưới dạng cửa sổ bật lên hoặc trên tổ hợp phím, giúp truy cập nó (và đọc) nhanh hơn nhiều.

+0

"Vì lý do này, bạn có thể thấy macro của clojure hữu ích. Nó sẽ cho bạn biết biểu mẫu là macro/hàm/dạng đặc biệt, cũng như cung cấp danh sách arg và chuỗi tài liệu (nếu có)" Điều đó chắc chắn Hữu ích. điều này làm việc một macro được xác định bởi chính nhà phát triển? –

+0

Có. Trình biên dịch biết rằng nó được cho là sử dụng một hàm làm macro vì Var nó được lưu trữ trong đó có ': doc true' được bao gồm trong bản đồ siêu dữ liệu của nó. 'doc' tìm kiếm cùng một phần siêu dữ liệu. –

1

Có hai thuộc tính mà thường phân biệt một macro (hoặc hình thức đôi khi đặc biệt) từ một hàm:

  1. Khi các hình thức thực hiện một số loại ràng buộc (tức là tuyên bố nhận dạng mới để sử dụng sau)
  2. Khi một số các đối số được đánh giá một cách lười biếng

Ví dụ về các trường hợp đầu tiên là let, letfn, bindingwith-local-vars. Kỳ lạ thay, mặc dù defn được định nghĩa là một hàm, nhưng tôi khá chắc chắn rằng nó có một cái gì đó để làm với quá trình bootstrapping Clojure (defn được định nghĩa trước khi defmacro được định nghĩa).

Ví dụ về số thứ hai sẽ là and, orlazy-seq. Trong tất cả các cấu trúc này, các đối số được đánh giá một cách lười biếng bằng cách đặt chúng trong các nhánh có điều kiện (như if) hoặc di chuyển chúng bên trong một thân hàm.

Cả hai thuộc tính đó thực sự chỉ là biểu hiện của macro thao tác cú pháp Clojure. Tôi không nghĩ rằng các luồng luồng (->->>) phù hợp rất tốt với một trong các danh mục đó, nhưng các phiên bản không an toàn (-?>-?>>) thuộc loại có các đối số lười lười biếng.

+1

'defn' là macro; nó được đăng ký như vậy bởi '(. (var defn) (setMacro))' ngay lập tức theo định nghĩa của nó. Lý do cho điều này là khi bạn chỉ ra - không có sẵn 'defmacro' tại thời điểm đó. Trong thực tế, 'defmacro' kết thúc tốt đẹp mẫu này (' defn' theo sau là một cuộc gọi 'setMacro') với một số tiền xử lý bổ sung (để chèn các tham số' & form' và '& env' ngầm định). –

+0

@ MichałMarczyk - Cảm ơn bạn đã xác nhận điều đó. Định nghĩa 'defn' xuất hiện để trả về cú pháp (được trích dẫn), vì vậy nó trông giống như một macro mặc dù nó được định nghĩa với' def' và 'fn'. Tôi đoán rằng có ý nghĩa mặc dù, kể từ khi macro chỉ là chức năng được gọi là lúc biên dịch thay vì thời gian chạy. Cảm ơn bạn đã chỉ ra phần 'setMacro'! – DaoWen

Các vấn đề liên quan