Memahami simbol yang tidak diinginkan dan ekspansi makro?

8

Saya ingin menunjukkan kurangnya pengetahuan saya dengan contoh.

Menggunakan dua definisi makro berikut,

(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))))))

makro pertama bayangan variabel apa pun nama maxyang mungkin terjadi di tubuh dan yang kedua tidak, yang dapat ditunjukkan dengan contoh berikut:

(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)))))

Sejauh yang telah saya pelajari, evaluasi panggilan makro berfungsi seperti ini:

Makro dievaluasi dua kali. Pertama tubuh dievaluasi dan mengembalikan formulir. Formulir ini kemudian dievaluasi kembali.

Sejauh ini bagus.

Tetapi jika saya berasumsi bahwa makro benar-benar mengembalikan sepotong teks yang kebetulan merupakan bentuk lisp, yang kemudian ditafsirkan, saya mendapatkan konflik yang membuat saya tidak dapat memahami contoh di atas.

Apa potongan teks yang makro kedua yang menggunakan make-symbolkembali, sehingga tidak ada bayangan yang terjadi? Dalam pemahaman saya, nama simbol yang dipilih acak ekstrim yang tidak masuk akal akan masuk akal.

Jika saya menggunakan pp-macroexpand...kedua makro mengembalikan ekspansi yang sama.

Apakah seseorang dapat membantu saya keluar dari kebingungan ini?

clemera
sumber
1
Ini bukan sepotong teks, tetapi daftar simbol. Beberapa di antaranya dapat menjadi istimewa dan karenanya tidak pernah bertabrakan dengan yang lain ...
wasamasa
2
Jika Anda mengatur print-gensymdan print-circleuntuk tAnda akan dapat melihat perbedaan dalam ekspansi makro.
npostavs
@adalah bagaimana mereka istimewa dan apa yang membuat ini benar-benar berfungsi adalah persis apa yang ingin saya pahami.
clemera
@npostavs Terima kasih, jadi formulir yang dikembalikan berbeda dan ini tidak terlihat dengan pengaturan default .... Saya melihat simbol yang tidak diinginkan akan diganti oleh #:max. Apa artinya ? Saya sangat tertarik dengan detail lebih lanjut.
clemera
3
The #:hanyalah sebuah konvensi dari printer untuk menunjukkan simbol uninterned (ini adalah apa yang print-gensymmenyala), ada lebih detail dalam manual: (elisp) Creating Symbols.
npostavs

Jawaban:

4

Seperti disebutkan dalam komentar, Anda harus mengaktifkan pengaturan ini untuk melihat bagaimana ekspansi makro bekerja lebih tepat. Perhatikan bahwa ini hanya mengubah cara macroexpandditampilkan, makro masih berfungsi sama dalam kedua kasus:

(setq print-gensym t)

Kemudian instance pertama diperluas ke (salah):

(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)))))

Dan yang kedua berkembang menjadi (benar):

(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)))))

Untuk lebih memahami perbedaannya, pertimbangkan untuk mengevaluasi ini:

(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

Seperti yang Anda lihat, hasil (make-symbol "max")tidak memiliki nilai. Ini memastikan kebenaran dari eval pass pertama pada makro. Dengan eval pass kedua, #:maxdapatkan nilai karena sekarang terikat sebagai variabel dinamis.

abo-abo
sumber
Anda perlu juga print-circlekalau tidak Anda punya 2 #:maxyang tidak eq(pertimbangkan juga bersarang for).
npostavs
Terima kasih, dengan jawaban Anda dan komentarnya menjadi lebih jelas bagi saya. Tapi nama simbol tetap maks benar? Jadi saya berasumsi ada cara Penerjemah tahu untuk tidak mencari simbol maks yang tidak diinginkan dalam tabel simbol biasa tetapi dalam tabel pencarian lain ...
clemera
Pada pass kedua, simbol max adalah variabel let-bound: semuanya baik-baik saja dalam kasus itu. Dalam kasus pertama, itu hanya simbol unik yang tidak diinginkan - tidak ada upaya untuk menginternasinya. Lihat cl-gensymapakah masih belum jelas.
abo-abo
Terima kasih atas bantuan Anda. Saya masih memiliki masalah dengan perinciannya: Pada pass kedua ada let let max dan #: max dan saya masih tidak mengerti cara kerjanya sehingga mereka dapat dibedakan. Jika saya memanggil nama-simbol pada make-simbol yang dibuat maks, juga max. Jadi mengapa penerjemah tidak bingung, mekanisme apa yang dia tahu bagaimana mencari nilai yang benar?
clemera