กำจัดการอ้างอิงถึงคำเตือนการคอมไพล์ไบต์ตัวแปรอิสระ

ฉันกำลังเขียนโหมดหลักของ emacs ซึ่งใช้ตัวแปรบัฟเฟอร์ภายในเพื่อจัดเก็บสถานะบางอย่าง:

(defun foo-mode ()
  "My nice major mode"
  (interactive)
  (kill-all-local-variables)
  (setq mode-name "foo")
  (setq major-mode 'foo-mode)
  (set (make-local-variable 'foo-state) "bar"))

(defun foo-change-state ()
  (setq foo-state "baz"))

สิ่งนี้ทำงานได้ดีมากและมีคุณสมบัติที่ในบัฟเฟอร์ใดๆ ที่ไม่ได้ใช้โหมดหลักของฉัน ตัวแปร foo-state จะไม่ถูกผูกไว้ (ซึ่งเป็นสิ่งที่ดีในความคิดของฉัน เนื่องจากจะช่วยหลีกเลี่ยงไม่ให้ตารางสัญลักษณ์ยุ่งเหยิง)

อย่างไรก็ตาม การคอมไพล์โค้ดแบบไบต์ดังกล่าวทำให้เกิดคำเตือนต่อไปนี้:

Warning: assignment to free variable `foo-state'

การใช้ defvar จะกำจัดคำเตือน แต่มีผลข้างเคียงที่ตอนนี้ foo-state ถูกผูกไว้ทุกที่ ซึ่งไม่เป็นที่พึงปรารถนาในความคิดของฉัน

มีวิธีกำจัดคำเตือนในขณะที่ยังไม่รวมตัวแปรเฉพาะโหมดในทุกบัฟเฟอร์หรือไม่? หรือฉันคิดผิดเมื่อคิดว่าไม่ควรประกาศตัวแปรเหล่านี้ทั่วโลก


person François Févotte    schedule 14.09.2012    source แหล่งที่มา
comment
นั่น เป็นการแปลงคำตอบต่อความคิดเห็นที่ไม่เป็นประโยชน์อย่างยิ่งจากใครบางคน หากต้องการให้ข้อความข้างต้นสามารถอ่านได้อีกครั้ง: C-h i g (elisp) Warning Tips RET เป็นที่ซึ่งมีเอกสารซึ่งตอบคำถามนี้อยู่   -  person phils    schedule 08.04.2014
comment
@phils ใช่ ประการหนึ่งฉันชอบคำตอบของคุณ ขอบคุณ!   -  person François Févotte    schedule 08.04.2014
comment
BTW ฉันขอแนะนำให้คุณใช้ define-derived-mode เช่นเดียวกับใน (define-derived-mode foo-mode nil "foo" "My nice major mode." (set (make-local-variable 'foo-state) "bar"))   -  person Stefan    schedule 16.04.2014
comment
ระวัง! ส่วน Warning Tips ในคู่มือ Emacs มีการโกหกอย่างโจ่งแจ้งเกี่ยวกับ defvar: คำจำกัดความดังกล่าวไม่มีผลใดๆ ยกเว้นการบอกคอมไพเลอร์ว่าอย่าเตือนเกี่ยวกับการใช้ตัวแปร [...] ในไฟล์นี้.. แน่นอนว่ามันสามารถมีผลกระทบมากกว่านี้อย่างแน่นอน มากกว่าการปิดเสียงคอมไพเลอร์: [it] ยังประกาศตัวแปรเป็นแบบพิเศษ ดังนั้นมันจึงถูกผูกไว้แบบไดนามิกเสมอ แม้ว่า 'การรวมคำศัพท์' จะเป็น t ก็ตาม (คำพูดที่นำมาจากเอกสารจริงสำหรับ defvar) บางทีคำแนะนำใน Warning Tips อาจถูกเขียนขึ้นก่อนที่จะมีการแนะนำการเชื่อมโยงคำศัพท์ใน Emacs Lisp...   -  person ack    schedule 01.03.2018


คำตอบ (2)


วิธีที่เป็นทางการในการทำสิ่งที่คุณต้องการคือ (defvar foo-state) สังเกตว่าไม่มีข้อโต้แย้งที่สอง โปรดทราบด้วยว่าการประกาศดังกล่าวใช้กับไฟล์ที่พบเท่านั้น (หรือกับขอบเขตที่พบ หากใช้ภายในฟังก์ชัน)

person Stefan    schedule 15.09.2012
comment
ขอบคุณ. การอ่านเอกสารประกอบของ defvar ซ้ำทำให้ชัดเจน: หาก INITVALUE หายไป ค่าของ SYMBOL จะไม่ถูกตั้งค่า ฉันพลาดสิ่งนี้และมักจะให้อาร์กิวเมนต์ที่สองเสมอ (ซึ่งทำให้ตัวแปรถูกผูกไว้เสมอ) - person François Févotte; 15.09.2012

ประกาศตัวแปรด้วย defvar ไม่มีทางอื่นที่จะลบคำเตือนได้ และถือเป็นแนวปฏิบัติที่ดีจริงๆ

ความตั้งใจของคุณที่จะรักษาตารางสัญลักษณ์ให้ไม่เกะกะนั้นคุ้มค่า แต่คุณไม่ได้ทำเช่นนั้นจริงๆ ฉันคิดว่าคุณเข้าใจความหมายของการเชื่อมโยงตัวแปรใน Emacs Lisp ผิด เนื่องจากดูเหมือนว่าคุณจะเชื่อว่าการไม่ประกาศ foo-state จะไม่ถูกผูกไว้ในบัฟเฟอร์ใดๆ ที่ไม่ได้ใช้ foo-mode นั่นไม่ใช่กรณี

ในชื่อ Emacs Lisp (หรือที่เรียกว่าสัญลักษณ์) จะเป็น global ทันทีที่มีการประเมิน foo-state ในครั้งแรก รันไทม์จะสร้างออบเจ็กต์สัญลักษณ์ใหม่สำหรับ foo-state และใส่สิ่งนี้ลงในตารางสัญลักษณ์ส่วนกลาง (หรือที่เรียกว่า obarray) ไม่มีตารางสัญลักษณ์ในเครื่อง ดังนั้นจึงไม่สำคัญว่า foo-state จะถูกประเมินที่ไหนและอย่างไร foo-state อ้างถึงออบเจ็กต์สัญลักษณ์ เดียวกัน ที่ สถานที่ใดก็ได้ (ดู การสร้างสัญลักษณ์)

วัตถุสัญลักษณ์แต่ละรายการประกอบด้วยส่วนประกอบ (หรือที่เรียกว่าเซลล์) หนึ่งในนั้นคือเซลล์ ตัวแปร (ดู ส่วนประกอบสัญลักษณ์) setq แก้ไขการเชื่อมโยงปัจจุบันของระบบ ที่ระดับบนสุดโดยไม่มีการเชื่อมโยงคำศัพท์ จะเป็นการเปลี่ยนเซลล์ตัวแปรของออบเจ็กต์สัญลักษณ์อย่างมีประสิทธิภาพ ดังนั้นค่า ทั่วโลก ของตัวแปร ขอย้ำอีกครั้งว่าไม่สำคัญว่า setq จะถูกประเมินไว้ที่ใด จริงๆ แล้ว หาก bar-mode ประเมิน (setq foo-state "bar") บางส่วน foo-state ก็จะถูกผูกไว้กับ "bar" ใน foo-mode เช่นกัน และในทางกลับกัน

ดังนั้นผลกระทบเพียงอย่างเดียวของ (defvar) ที่มากกว่า (setq) นั้นคือ เอกสาร ความตั้งใจของการใช้สัญลักษณ์เป็นตัวแปรร่วม ดังนั้นการบอกผู้อื่นว่าอย่าแก้ไขตัวแปรนี้ เว้นแต่ตั้งใจที่จะบิดเบือนพฤติกรรมของ foo-mode คุณสามารถแนบเอกสารประกอบกับตัวแปร และทำเครื่องหมายว่าถูกกำหนดไว้ในบัฟเฟอร์ของคุณ (C-h v foo-state จะให้ลิงก์เพื่อข้ามไปยังคำจำกัดความ)

เนื่องจาก Emacs Lisp ไม่มีเนมสเปซและมีการกำหนดขอบเขตแบบไดนามิกตามค่าเริ่มต้น เอกสารจึงมีความสำคัญขั้นพื้นฐานในการหลีกเลี่ยงความขัดแย้งระหว่างโมดูล หากฉันเขียน bar-mode โดยใช้ foo-mode ของคุณ ฉันอาจเชื่อมโยงกับ foo-state โดยไม่ได้ตั้งใจ ให้โทร foo-change-state แล้วเห็นว่าโหมดของฉันทำงานผิดปกติเนื่องจากตัวแปรถูกเขียนทับโดยไม่ได้ตั้งใจ การประกาศ foo-state ไม่ได้ทำให้สิ่งนี้เป็นไปไม่ได้ แต่อย่างน้อยก็ช่วยให้ฉันจับข้อผิดพลาดได้ เพราะ C-h v foo-state จะเปิดเผยว่าตัวแปรนี้ถูกใช้โดยโหมดอื่น ดังนั้นฉันจึงไม่ควรใช้มันเว้นแต่ว่าฉันตั้งใจจะจัดการโหมดนั้นจริงๆ

ดังคำสุดท้าย: ในข้อความ "โหมด" ทั้งหมดที่กล่าวมาข้างต้นสามารถแทนที่ได้ด้วยไฟล์ Emacs Lisp modes ไม่มีอะไรพิเศษเกี่ยวกับสัญลักษณ์ ที่กล่าวมาทั้งหมดยังใช้สำหรับ Emacs Lisp ที่ไม่ได้ประกาศโหมด แต่มีเพียงฟังก์ชันมากมาย

person lunaryorn    schedule 14.09.2012
comment
ไม่เป็นความจริงที่ foo-state จะไม่ถูกแยกออกจากบัฟเฟอร์อื่นๆ — ลองทำตามตัวอย่างของผู้โพสต์ แม้ว่า obarray จะเป็นสากล ค่าของสัญลักษณ์บัฟเฟอร์ภายในจะเปลี่ยนไปบนสวิตช์บัฟเฟอร์แต่ละตัว ซึ่งอาจรวมถึงการเปลี่ยนแปลงสถานะที่ไม่ถูกผูกไว้ด้วย หากคุณกด M-: foo-state RET ในบัฟเฟอร์ foo-mode คุณจะได้รับ "bar" ในขณะที่ถ้าคุณทำเช่นเดียวกันในบัฟเฟอร์อื่น คุณจะได้รับข้อผิดพลาด แม้ว่าสิ่งนี้จะไม่ใช่สำนวนใน elisp แต่ก็สามารถรับได้อย่างแน่นอนโดยไม่คำนึงถึงตารางสัญลักษณ์สากล - person user4815162342; 15.09.2012
comment
นอกจากนี้ โปรดทราบด้วยว่า เนื่องจาก Emacs Lisp ขาดระบบโมดูล จึงไม่ใช่เอกสารที่ปกป้องคุณจากการปะทะกันของตัวแปรส่วนกลางโดยไม่ตั้งใจ แต่เป็นหลักการของการใช้คำนำหน้าสัญลักษณ์ นี่คือสาเหตุที่แต่ละโหมดนำหน้าสัญลักษณ์ทั้งหมดด้วย mode- defvar อนุญาตให้คอมไพลเลอร์ปล่อย (หรือ ไม่ ที่จะปล่อย) คำเตือนที่จับการมอบหมายที่สะกดผิด ซึ่งเป็นคำเตือนเดียวกันกับที่เป็นผลบวกลวงในกรณีของผู้โพสต์ - person user4815162342; 15.09.2012
comment
หาก foo-state ถูกตั้งค่าโดยบัฟเฟอร์อื่น ก่อน foo-mode จะประกาศว่าบัฟเฟอร์ในเครื่อง ก็จะได้รับค่าโกลบอล หลังจากประกาศตัวแปรบัฟเฟอร์ภายในเครื่องแล้วเท่านั้น การผูกบัฟเฟอร์ภายในจึงมีผล และถึงแม้การเชื่อมโยงทั่วโลกยังคงสามารถเปลี่ยนแปลงได้ผ่านทาง setq-default แม้ว่าสิ่งนี้จะไม่ส่งผลกระทบต่อบัฟเฟอร์การเชื่อมโยงภายในเครื่องก็ตาม - person lunaryorn; 15.09.2012
comment
คำนำหน้าสัญลักษณ์ช่วยหลีกเลี่ยงความขัดแย้ง แต่ไม่ได้ป้องกันความขัดแย้ง ยิ่งเนื่องจากโดยเฉพาะตัวแปรที่ประกาศโดยโหมดการเขียนโปรแกรมมักจะมีชื่อที่แพร่หลาย เพียงเพราะว่าคำนำหน้าสัญลักษณ์ (เช่น ชื่อของภาษาการเขียนโปรแกรม) นั้นแพร่หลาย ตัวอย่างเช่น ruby-mode อาจประกาศตัวแปร ruby-executable สำหรับการประเมินบัฟเฟอร์ผ่านตัวแปล Ruby แต่ไลบรารีอื่นๆ ที่เกี่ยวข้องกับ Ruby (เช่น ตัวตรวจสอบ flymake สำหรับ Ruby) ก็อาจใช้ตัวแปรนี้ได้อย่างง่ายดายเช่นกัน เนื่องจากเป็นเพียงชื่อที่เป็นธรรมชาติที่สุดในการเลือก นี้. นั่นเป็นเหตุผลว่าทำไมจึงต้องมีเอกสารประกอบ... - person lunaryorn; 15.09.2012
comment
สรุปแล้วมันหมายความว่าใน elisp ทุกอย่างเป็นเพียงตัวแปรโกลบอลใช่หรือไม่ การแก้ไขที่เดียวจะส่งผลให้มีการดำเนินการต่อไปนี้ทั้งหมดเพื่อใช้ค่าใหม่หรือไม่ คำอธิบายที่ดีมาก! - person Hot.PxL; 24.05.2015