เหตุใดแมโครที่ใช้การอนุญาตจึงแตกต่างจากที่ไม่ได้ใช้

ฉันยังใหม่กับ Lisp และฉันกำลังอ่าน Let Over Lambda ของ Doug Hoyte และเขานำเสนอแมโคร nif ของ Paul Graham ในบทที่ 3 ฉันกำลังเล่นกับเรื่องนั้นและสร้างมาโครสองตัวนี้:

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

เมื่อฉันทำ (macroexpand '(niffy2 10 "positive" "zero" "negative")) ฉันได้รับสิ่งที่ฉันคาดหวัง: (LET ((X 10)) (COND ((PLUSP X) "positive") ((ZEROP X) "zero" (T "negative"))))

แต่เมื่อฉันทำ (macroexpand '(niffy 10 "positive" "zero" "negative")) ฉันเพิ่งได้รับแบบฟอร์มการประเมิน "positive" ซึ่งทำให้ฉันสับสนเพราะใน niffy, cond ถูก backquoted ดังนั้นฉันคิดว่านั่นหมายความว่ามันจะไม่ได้รับการประเมิน การประเมินทั้ง niffy และ niffy2 โดยไม่มีการขยายมาโคร ทั้งสองทำงานตรงตามที่ฉันคาดหวัง โดยส่งคืน "บวก", "ศูนย์" และ "ลบ" สำหรับค่าบวก ศูนย์ และลบ ตามลำดับ


person Matt H    schedule 07.03.2015    source แหล่งที่มา


คำตอบ (2)


เนื่องจาก cond เป็นมาโคร ลองทำสิ่งต่อไปนี้:

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

ใน SBCL ฉันได้รับสิ่งนี้:

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

ใน CLISP ฉันได้รับสิ่งนี้:

"positive" ;
T

แม้ว่าฉันจะเข้าใจพฤติกรรมของ CLISP ได้ แต่ดูเหมือนว่าจะไม่จำเป็นสักหน่อย เนื่องจากการเพิ่มประสิทธิภาพสามารถจัดการได้ง่ายขึ้นหลังจากการขยายตัวแบบมาโคร

(โปรดทราบว่าโดยปกติคุณควรเลือกใช้เวอร์ชันที่มี let เนื่องจากไม่ได้ประเมินอาร์กิวเมนต์หลายครั้ง)

person Dietrich Epp    schedule 07.03.2015
comment
อ๋อ ฉันใช้คลิปป์อยู่ clisp เพิ่งคอมไพล์ cond หรืออะไรสักอย่าง? นอกจากนี้ ตอนนี้ฉันเห็นว่าฉันทำ macroexpand-1 niffy ฉันได้รับพฤติกรรมตามที่คาดหวังหรือไม่ - person Matt H; 07.03.2015
comment
ดูเหมือนว่ามาโครสำหรับ cond ใน CLISP กำลังทำการเพิ่มประสิทธิภาพบางอย่าง การคอมไพล์จะส่งผลให้ได้เอาต์พุตที่แตกต่างกัน ฉันคิดว่า CLISP มีคอมไพเลอร์ที่มีเอาต์พุตโค้ดไบต์ (อย่างไรก็ตาม CLISP นั้นหมดอายุแล้ว) - person Dietrich Epp; 07.03.2015

รหัสของคุณมีข้อผิดพลาด:

(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

ทดสอบ:

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

คอมไพเลอร์บ่น:

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

มันควรจะเป็น:

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

แต่เมื่อฉันทำ (macroexpand '(niffy 10 "positive" "zero" "negative")) ฉันเพิ่งได้รับรูปแบบการประเมิน "บวก"

เนื่องจากคุณเรียกร้องให้มีการขยายมาโคร จึงไม่สามารถประเมินได้ จะต้องเป็นผลจากตัวขยายแมโครในการใช้งานนั้น

โปรดทราบว่า MACROEXPAND เป็นกระบวนการวนซ้ำ แบบฟอร์มจะขยายออกไป แบบฟอร์มผลลัพธ์อาจเป็นรูปแบบมาโครอื่น ก็จะขยายออกไปด้วย และอีกครั้ง. อีกครั้ง. จนกระทั่งฟอร์มผลลัพธ์ไม่ใช่ฟอร์มมาโคร โปรดทราบว่าการดำเนินการนี้จะไม่ข้ามฟอร์มย่อย มันทำการขยายมาโครแบบวนซ้ำของฟอร์มบนสุดอย่างเคร่งครัด

ใช้ MACROEXPAND-1

ดังนั้นหากคุณต้องการดูเอฟเฟกต์ของมาโครของคุณเท่านั้น ให้โทร MACROEXPAND-1 ฟังก์ชันนี้จะขยายเพียงครั้งเดียว

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
เป็นเรื่องจริงที่ t ไม่ใช่ฟังก์ชัน แต่นั่นไม่ใช่ข้อผิดพลาด เนื่องจากไม่ได้ถูกใช้เป็นฟังก์ชัน (อย่างน้อยก็เมื่อมีการเพิ่มวงเล็บที่หายไป) - person Dietrich Epp; 08.03.2015
comment
@DietrichEpp: ระบบ Lisp จะไม่รู้ว่าวงเล็บวางผิด มันจะบ่นเกี่ยวกับแอพพลิเคชั่นฟังก์ชั่น - person Rainer Joswig; 08.03.2015
comment
แต่ในฐานะมนุษย์ เรารู้ว่ามีข้อผิดพลาดเพียงข้อเดียวในโค้ด นั่นคือ วงเล็บที่หายไป ไม่ว่าระบบ Lisp จะซับซ้อนพอที่จะเข้าใจเรื่องนี้หรือไม่นั้นเป็นอีกเรื่องหนึ่ง - person Dietrich Epp; 08.03.2015
comment
@DietrichEpp: ฉันไม่แน่ใจว่ามีกี่คนที่ค้นหาได้ง่ายเมื่อดูโค้ด หากต่อมาระบบ Lisp บ่น มันบ่นเกี่ยวกับการเรียกใช้ฟังก์ชัน - นั่นคือเหตุผลที่ฉันพูดถึงมัน จากนั้นจะต้องกลับมาทำงานอีกครั้งเพื่อหาเหตุผลว่าทำไม T จึงถูกใช้เป็นฟังก์ชัน - person Rainer Joswig; 08.03.2015
comment
ใช่ แต่คุณคงเข้าใจแล้วว่าข้อผิดพลาดที่แท้จริงอยู่ที่ไหน แม้ว่าระบบ Lisp อาจจะบ่นว่า t ไม่ใช่ฟังก์ชัน แต่ก็คุ้มค่าที่จะอธิบายว่าสิ่งนี้มีสาเหตุมาจากวงเล็บที่หายไป และมันก็คุ้มค่าที่จะสอนผู้คนถึงวิธีระบุข้อผิดพลาดจากข้อความวินิจฉัย แต่ก็ยังมีเพียง ข้อผิดพลาดประการหนึ่ง: วงเล็บหายไป - person Dietrich Epp; 08.03.2015