Hiểu các biểu tượng không liên quan và mở rộng vĩ mô?


8

Tôi muốn chứng minh sự thiếu hiểu biết của mình bằng một ví dụ.

Sử dụng hai định nghĩa vĩ mô sau đây,

(defmacro for (var from init to final do &rest body)
  "Execute a simple for loop: (for i from 1 to 10 do (print i))."
  (let ((tempvar 'max))
    `(let ((,var ,init)
           (,tempvar ,final))
       (while (<= ,var ,tempvar)
         ,@body
         (setq ,var (1+ ,var))))))
(defmacro for (var from init to final do &rest body)
  "Execute a simple for loop: (for i from 1 to 10 do (print i))."
  (let ((tempvar (make-symbol "max")))
    `(let ((,var ,init)
           (,tempvar ,final))
       (while (<= ,var ,tempvar)
         ,@body
         (setq ,var (1+ ,var))))))

macro đầu tiên che mờ bất kỳ biến nào có tên maxxảy ra trong cơ thể và biến thứ hai thì không, có thể được hiển thị với ví dụ sau:

(let ((max 0)) ;; this variable is shadowed if the first macro defintion is used
  (for i from 1 to 3 do
       (setq square (* i i))
       (if (> square max) 
         (princ (format "\n%d %d" i square)))))

Theo như tôi đã học được việc đánh giá một cuộc gọi macro hoạt động như thế này:

Macro được đánh giá hai lần. Đầu tiên cơ thể được đánh giá và trả lại một hình thức. Hình thức này sau đó được đánh giá lại.

Càng xa càng tốt.

Nhưng nếu tôi cho rằng macro thực sự trả về một đoạn văn bản có thể là một dạng không thể thiếu, sau đó được giải thích, tôi nhận được một xung đột khiến tôi không thể hiểu được ví dụ trên.

Đoạn văn bản nào mà macro thứ hai sử dụng make-symbolreturn, để không xảy ra hiện tượng đổ bóng? Theo hiểu biết của tôi, một tên biểu tượng được chọn ngẫu nhiên cực kỳ khó có thể có ý nghĩa.

Nếu tôi sử dụng pp-macroexpand...cả hai macro trả lại cùng một bản mở rộng.

Có ai có thể giúp tôi thoát khỏi sự nhầm lẫn này?


1
Nó không phải là một đoạn văn bản, mà là một danh sách các biểu tượng. Một số trong đó có thể đặc biệt và do đó không bao giờ va chạm với người khác ...
wasamasa

2
Nếu bạn đặt print-gensymprint-circlecho tbạn sẽ có thể thấy sự khác biệt trong các mở rộng macro.
npostavs

@wasamasa làm thế nào họ đặc biệt và những gì làm cho điều này thực sự hoạt động là chính xác những gì tôi muốn hiểu.
clemera

@npostavs Cảm ơn, vì vậy biểu mẫu được trả về là khác nhau và điều này không hiển thị với các cài đặt mặc định .... Tôi thấy biểu tượng không liên kết được thay thế bởi #:max. Điều đó nghĩa là gì ? Tôi rất quan tâm đến nhiều chi tiết hơn.
clemera

3
Đây #:chỉ là một quy ước của máy in để chỉ ra một biểu tượng không liên quan (đây là những gì được print-gensymbật), có nhiều chi tiết hơn trong hướng dẫn sử dụng : (elisp) Creating Symbols.
npostavs

Câu trả lời:


4

Như đã đề cập trong các bình luận, bạn phải bật cài đặt này để xem cách mở rộng macro hoạt động chính xác hơn. Lưu ý rằng nó chỉ thay đổi cách macroexpandhiển thị, các macro vẫn hoạt động như nhau trong cả hai trường hợp:

(setq print-gensym t)

Sau đó, phiên bản đầu tiên mở rộng thành (không chính xác):

(let ((max 0))
  (let ((i 1)
        (max 3))
    (while (<= i max)
      (setq square (* i i))
      (if (> square max)
          (princ
           (format "\n%d %d" i square)))
      (setq i (1+ i)))))

Và cái thứ hai mở rộng thành (chính xác):

(let ((max 0))
  (let
      ((i 1)
       (#:max 3))
    (while
        (<= i #:max)
      (setq square
            (* i i))
      (if
          (> square max)
          (princ
           (format "\n%d %d" i square)))
      (setq i
            (1+ i)))))

Để hiểu thêm về sự khác biệt, hãy xem xét đánh giá này:

(setq max 10)
;; => 10
(symbol-value 'max)
;; => 10
(symbol-value (make-symbol "max"))
;; => Caught unbound variable #:max, setting it to nil.
(make-symbol "max")
;; => #:max
(eq (make-symbol "max")
    (make-symbol "max"))
;; => nil

Như bạn có thể thấy, kết quả của (make-symbol "max")không có giá trị. Điều này đảm bảo tính chính xác của vượt qua eval đầu tiên trên macro. Với pass eval thứ hai, #:maxđạt được một giá trị vì giờ đây nó bị ràng buộc như một biến động.


Bạn cũng cần print-circlecó 2 #:maxcái không có eq(xem xét cũng lồng nhau for).
npostavs

Cảm ơn, với câu trả lời của bạn và các ý kiến, nó trở nên rõ ràng hơn nhiều đối với tôi. Nhưng tên biểu tượng vẫn tối đa phải không? Vì vậy, tôi cho rằng có một cách mà Thông dịch viên biết không tìm kiếm tối đa biểu tượng không liên kết trong bảng biểu tượng thông thường nhưng trong một bảng tra cứu khác ...
clemera

Trên đường chuyền thứ hai, ký hiệu max là một biến cho phép: tất cả đều ổn trong trường hợp đó. Trong trường hợp đầu tiên, nó chỉ là một biểu tượng độc nhất vô nhị - không có nỗ lực để thực hiện nó. Hãy nhìn xem cl-gensymnếu nó vẫn chưa rõ ràng.
abo-abo

Cảm ơn bạn đã giúp đỡ. Tôi vẫn gặp vấn đề với các chi tiết: Trên đường chuyền thứ hai có giới hạn cho phép max và #: max và tôi vẫn không hiểu cách thức hoạt động của chúng mà chúng có thể phân biệt được. Nếu tôi gọi tên biểu tượng trên biểu tượng tạo được tạo tối đa thì nó cũng là tối đa. Vậy tại sao người phiên dịch không nhầm lẫn, cơ chế mà anh ta biết cách tra cứu đúng giá trị là gì?
clemera
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.