แก้ไข : วิธีที่ดีกว่าการทำเช่นนี้ใน Emacs ล่าสุดคือการกำหนดแมโครคอมไพเลอร์เพื่อตรวจสอบจำนวนข้อโต้แย้ง คำตอบเดิมของฉันที่ใช้แมโครปกติจะถูกเก็บไว้ด้านล่าง แต่คอมไพเลอร์ - แมโครนั้นยอดเยี่ยมเพราะมันไม่ได้ป้องกันการส่งผ่านฟังก์ชันไปยังfuncall
หรือapply
ที่รันไทม์
ในเวอร์ชันล่าสุดของ Emacs คุณสามารถทำได้โดยการกำหนดคอมไพเลอร์มาโครสำหรับฟังก์ชันของคุณซึ่งตรวจสอบจำนวนอาร์กิวเมนต์และสร้างคำเตือน (หรือแม้แต่ข้อผิดพลาด) หากมันไม่ตรงกัน ความละเอียดเพียงอย่างเดียวคือแมโครคอมไพเลอร์ควรส่งคืนฟอร์มการเรียกใช้ฟังก์ชันดั้งเดิมโดยไม่มีการเปลี่ยนแปลงสำหรับการประเมินหรือการรวบรวม สิ่งนี้ทำได้โดยใช้&whole
อาร์กิวเมนต์และคืนค่าของมัน สามารถทำได้เช่นนี้:
(require 'cl-lib)
(defun my-caller (&rest args)
(while args
(message "%S %S" (pop args) (pop args))))
(define-compiler-macro my-caller (&whole form &rest args)
(when (not (cl-evenp (length args)))
(byte-compile-warn "`my-caller' requires an even number of arguments"))
form)
(my-caller 1 2 3 4)
(my-caller 1 2)
(funcall #'my-caller 1 2 3 4) ; ok
(apply #'my-caller '(1 2)) ; also ok
(my-caller 1) ; produces a warning
(funcall #'my-caller 1 2 3) ; no warning!
(apply #'my-caller '(1 2 3)) ; also no warning
โปรดทราบว่าfuncall
และapply
สามารถใช้งานได้ แต่จะข้ามการตรวจสอบอาร์กิวเมนต์โดยแมโครคอมไพเลอร์ แม้จะมีชื่อของพวกเขาแมโครคอมไพเลอร์ก็ดูเหมือนว่าจะได้รับการขยายตัวในหลักสูตรของ 'ตีความ' การประเมินผลผ่านทางC-xC-e, M-xeval-bufferดังนั้นคุณจะได้รับข้อผิดพลาดในการประเมินผลเช่นเดียวกับการรวบรวมตัวอย่างนี้
คำตอบเดิมมีดังนี้:
นี่คือวิธีที่คุณสามารถใช้คำแนะนำของ Jordon เพื่อ "ใช้มาโครที่จะให้คำเตือนเมื่อถึงเวลาการขยาย" กลายเป็นเรื่องง่ายมาก:
(require 'cl-lib)
(defmacro my-caller (&rest args)
(if (cl-evenp (length args))
`(my-caller--function ,@args)
(error "Function `my-caller' requires an even number of arguments")))
(defun my-caller--function (&rest args)
;; function body goes here
args)
(my-caller 1 2 3 4)
(my-caller 1 2 3)
การพยายามรวบรวมข้างต้นในไฟล์จะล้มเหลว (ไม่มีการสร้าง.elc
ไฟล์) โดยมีข้อความแสดงข้อผิดพลาดที่สามารถคลิกได้ดีในบันทึกการคอมไพล์โดยระบุ:
test.el:14:1:Error: `my-caller' requires an even number of arguments
คุณสามารถแทนที่(error …)
ด้วย(byte-compile-warn …)
เพื่อสร้างคำเตือนแทนข้อผิดพลาดช่วยให้การรวบรวมเพื่อดำเนินการต่อ (ขอบคุณ Jordon ที่ชี้เรื่องนี้ในความคิดเห็น)
เนื่องจากแมโครถูกขยายในเวลารวบรวมจึงไม่มีบทลงโทษที่เกี่ยวข้องกับการตรวจสอบนี้ แน่นอนคุณไม่สามารถหยุดคนอื่นโทรmy-caller--function
โดยตรง แต่อย่างน้อยคุณสามารถโฆษณามันเป็นฟังก์ชั่น "ส่วนตัว" โดยใช้การประชุมยัติภังค์คู่
ข้อเสียที่โดดเด่นของการใช้แมโครเพื่อจุดประสงค์นี้my-caller
คือไม่มีฟังก์ชั่นชั้นหนึ่งอีกต่อไป: คุณไม่สามารถส่งผ่านไปยังfuncall
หรือapply
ที่รันไทม์ (หรืออย่างน้อยก็จะไม่ทำสิ่งที่คุณคาดหวัง) ในแง่ที่ว่าวิธีนี้ไม่ดีเท่าที่จะสามารถประกาศเตือนคอมไพเลอร์สำหรับฟังก์ชั่นจริง แน่นอนว่าการใช้apply
จะทำให้ไม่สามารถตรวจสอบจำนวนข้อโต้แย้งที่ส่งผ่านไปยังฟังก์ชันในเวลารวบรวมได้ดังนั้นบางทีนี่อาจเป็นการแลกเปลี่ยนที่ยอมรับได้