Mengapa makro yang menggunakan let expand berbeda dengan makro yang tidak menggunakan let expand?

Saya baru mengenal Lisp dan saya membaca Let Over Lambda karya Doug Hoyte dan dia menyajikan makro nif Paul Graham di Bab 3. Saya bermain-main dengan itu dan membuat dua makro ini:

(defmacro niffy (expr pos zero neg)
  `(cond ((plusp ,expr) ,pos)
         ((zerop ,expr) ,zero)
         (t ,neg)))

(defmacro niffy2 (expr pos zero neg)
  `(let ((x ,expr))
     (cond ((plusp x) ,pos)
           ((zerop x) ,zero
            (t ,neg)))))

Ketika saya melakukan (macroexpand '(niffy2 10 "positive" "zero" "negative")), saya mendapatkan apa yang saya harapkan: (LET ((X 10)) (COND ((PLUSP X) "positive") ((ZEROP X) "zero" (T "negative"))))

Tetapi ketika saya melakukan (macroexpand '(niffy 10 "positive" "zero" "negative")) saya baru saja mendapatkan formulir evaluasi "positive". Yang membingungkan saya karena di niffy, cond di-backquoted, jadi saya pikir itu berarti tidak akan dievaluasi. Mengevaluasi niffy dan niffy2 tanpa perluasan makro, keduanya berfungsi persis seperti yang saya harapkan, mengembalikan "positif", "nol", dan "negatif" masing-masing untuk nilai positif, nol, dan negatif.


person Matt H    schedule 07.03.2015    source sumber


Jawaban (2)


Ini karena cond adalah makro. Coba jalankan yang berikut ini:

(macroexpand
 '(cond ((plusp 10) "positive")
        ((zerop 10) "negative")
        (t "zero")))

Di SBCL, saya mendapatkan ini:

(IF (PLUSP 10)
    (PROGN "positive")
    (COND ((ZEROP 10) "negative") (T "zero")))
T

Di CLISP, saya mendapatkan ini:

"positive" ;
T

Meskipun saya dapat memahami perilaku CLISP, tampaknya ini tidak diperlukan, karena pengoptimalan dapat lebih mudah ditangani setelah ekspansi makro.

(Perhatikan bahwa Anda biasanya memilih versi dengan let karena versi tersebut tidak mengevaluasi argumennya berkali-kali.)

person Dietrich Epp    schedule 07.03.2015
comment
Ah oke, saya menggunakan clipp. Jadi apakah CLISP baru saja mengkompilasi cond atau semacamnya? Selain itu, sekarang saya melihat apakah saya melakukan macroexpand-1 niffy saya mendapatkan perilaku yang saya harapkan. - person Matt H; 07.03.2015
comment
Sepertinya makro untuk cond di CLISP hanya melakukan beberapa optimasi. Kompilasi akan menghasilkan keluaran yang berbeda, menurut saya CLISP memiliki kompiler dengan keluaran kode byte. (Omong-omong, CLISP sudah tidak berfungsi) - person Dietrich Epp; 07.03.2015

Kode Anda mengalami kesalahan:

(defmacro niffy2 (expr pos zero neg)
  `(let ((x ,expr))
     (cond ((plusp x) ,pos)
           ((zerop x) ,zero              <- missing parenthesis
            (t ,neg)))))                 <- T is not a function

Tes:

(defun test (x)
  (niffy2 x "positive" "zero" "negative"))

Kompiler mengeluh:

The following function is undefined:
T which is referenced by TEST

Harus:

(defmacro niffy2 (expr pos zero neg)
  `(let ((x ,expr))
     (cond ((plusp x) ,pos)
           ((zerop x) ,zero)
           (t ,neg))))

Tetapi ketika saya melakukannya (macroexpand '(niffy 10 "positif" "nol" "negatif")) saya hanya mendapatkan bentuk evaluasi "positif".

Karena Anda memerlukan perluasan makro, perluasan tersebut tidak dapat dievaluasi. Itu pasti efek dari macro expander dalam implementasinya.

Perhatikan bahwa MACROEXPAND adalah proses berulang. Bentuknya akan diperluas. Bentuk hasilnya kemudian bisa menjadi bentuk makro lainnya. Itu juga akan diperluas. Dan lagi. Lagi. Hingga bentuk hasilnya bukan merupakan bentuk makro. Perhatikan bahwa ini tidak melintasi subformulir. Ini secara ketat melakukan ekspansi makro berulang dari bentuk teratas.

Gunakan MACROEXPAND-1

Jadi jika Anda ingin melihat efek makro saja, hubungi MACROEXPAND-1. Fungsi ini akan diperluas tepat satu kali.

CL-USER 23 > (macroexpand-1 '(niffy 10 "positive" "zero" "negative"))
(COND ((PLUSP 10) "positive")
      ((ZEROP 10) "zero")
      (T "negative"))

CL-USER 24 > (macroexpand-1 '(COND ((PLUSP 10) "positive")
                                   ((ZEROP 10) "zero")
                                   (T "negative")))
(IF (PLUSP 10)
  (PROGN "positive")
  (IF (ZEROP 10)
     (PROGN "zero")
     (PROGN "negative")))
T
person Rainer Joswig    schedule 07.03.2015
comment
Memang benar t bukan fungsi, tapi itu bukan kesalahan, karena tidak digunakan sebagai fungsi (setidaknya, ketika tanda kurung yang hilang ditambahkan). - person Dietrich Epp; 08.03.2015
comment
@DietrichEpp: Sistem Lisp tidak akan mengetahui bahwa tanda kurung salah. Ini akan mengeluh tentang aplikasi fungsi. - person Rainer Joswig; 08.03.2015
comment
Namun sebagai manusia, kita tahu bahwa hanya ada satu kesalahan dalam kode tersebut: tanda kurung hilang. Apakah sistem Lisp cukup canggih untuk mengetahui hal ini adalah masalah lain. - person Dietrich Epp; 08.03.2015
comment
@DietrichEpp: Saya tidak yakin berapa banyak orang yang dapat menemukannya dengan mudah, dengan melihat kodenya. Jika nanti sistem Lisp mengeluh. Ia mengeluh tentang pemanggilan fungsi - itu sebabnya saya menyebutkannya. Kita kemudian harus bekerja kembali untuk menemukan alasan mengapa T digunakan sebagai suatu fungsi. - person Rainer Joswig; 08.03.2015
comment
Benar, tetapi Anda sudah mengetahui di mana kesalahan sebenarnya. Meskipun perlu disebutkan bahwa sistem Lisp mungkin akan mengeluh bahwa t bukan suatu fungsi, dan perlu dijelaskan bahwa ini disebabkan oleh hilangnya tanda kurung, dan ada baiknya mengajari orang cara mengetahui kesalahan dari pesan diagnostik, yang ada hanyalah satu kesalahan: tanda kurung hilang. - person Dietrich Epp; 08.03.2015