Що робить `setq-local` і коли я повинен його використовувати?


16

Мені не зовсім зрозуміло всі варіанти буферно-локальних змінних, навіть після того, як прочитав усі документи та купу публікацій тут на SX.

Ось короткий зміст моїх розумінь:

(defvar foo ..)оголошує динамічну змінну для файлу. Але змінна (1) не відома іншим файлам, якщо вони також не включають defvar оператор, і (2) змінна має глобальний обсяг, а не буферна локальна.

(make-variable-buffer-local foo)після defvarвищесказаного повідомляє компілятору та всім іншим, що змінна foo має розглядатися як локальний буфер скрізь, де вона встановлена, коли вона встановлена. Таким чином, ця модель є гарним стилем для оголошення локальної змінної буфера, а також обидва висловлювання повертаються назад у файл.

(defvar xxx ...)                    ;declare xxx with global scope
(make-variable-buffer-local 'xxx)   ;but now make it buffer-local everywhere

Для зручності (defvar-local xxx ...)форму можна використовувати як один рядок, замість двох рядків вище:

(defvar-local xxx ...)       ;make xxx buffer local everywhere

Після того, як оголошено як вище, змінна xxx може використовуватися, як і будь-яка інша змінна в операторах setq.

Якщо я просто хочу мати один екземпляр локальної змінної буфера, яка вже є глобальною динамічною змінною, я б використовував наступні декларації. Перший оголошує динамічну змінну глобального масштабу, а другий висловлює лише один екземпляр локально-буферної версії цієї змінної у поточному буфері:

(defvar xxx ...)             ;declare xxx with global scope
(make-local-variable 'xxx)   ;make xxx local in this buffer only

Тепер для моїх експлікаційних запитань (усе вищезазначене було неявними запитаннями щодо правильності мого розуміння).

При завданні значення змінних, можна використовувати setqабо setq-local. Коли слід setq-localвикористовувати? Чому?

Що станеться, якщо я використовую setq-localбуферні локальні vars або не-буферні локальні vars?

Чи setq-localпотрібно для defvar-localоголошеної змінної?

Чи setq-localперетворить звичайну defvarзаявлену змінну в буферну локальну змінну? (Іншими словами, чи setq-localякимось чином еквівалент (make-variable-local xxx)декларації?


Спасибі за додаткову працю Скотта. Я вкладу додаткові зворотні коментарі звідси в.
Кевін

2
(setq-local VAR VALUE)це лише скорочення (set (make-local-variable VAR) VALUE), яке було (і досі є) загальною фразою.
Дрю

Відповіді:


18

Більшість ваших припущень близькі. Я згадаю кілька пізніше. Але, спочатку головне питання.

Форма setq-local- це просто зручність, це те саме, що робити make-local-variableдалі setq. Якщо ви зробили це, C-h f setq-localщоб переглянути документацію та перейшли до джерела, ви могли це бачити. Ось так я перевірив своє перше враження. Код, правда, трохи незрозумілий, оскільки оптимізує такі речі, як факт, що make-local-variableнасправді повертає саму змінну. Використання setq-local- це спосіб вказати локальність у деякому коді, щоб інші знали, що код не може грати з глобальним значенням змінної. Вам НЕ потрібно використовувати його для доступу до локальних змінних. Будь-яку змінну можна зробити буферно-локальною, і це вплине на весь код, який торкається змінної (за винятком випадків, коли вона виходить зі шляху, щоб отримати глобальну копію з setq-defaultподібною)

Це головна відповідь. Зараз у вашому фоновому режимі є кілька речей, які трохи відключаються. Оскільки ви про це запитали, я торкнуся деяких речей.

Перший - "невідомий іншим файлам". Це насправді не так. Будь-який код може посилатися на будь-яку глобальну змінну (саме тому їх називають "глобальною"), не включаючи defvarC-h f defvarговорить так). Насправді, для кожної глобальної змінної має бути лише одна основна defvar(зі значенням docstring та значенням за замовчуванням). defvarТільки з ім'ям змінної може бути використана для придушення попередження байт-компілятор. Emacs використовує лише значення з першого, яке він бачить¹, і docstring з останнього. Якщо є кілька defvars зі значенням / docstring, вони можуть бентежити людей, які читають код. І порядок завантаження файлів буде мати значення.

Деякі змінні призначені для використання лише як локальні буфери, а ті - ті, що використовують defvar-local(або дві частини defvar/ для make-variable-buffer-localяких це коротко, переважно у старішому коді до додавання короткої форми). Це робить його локальним у всіх буферах. Для деяких змінних їх основне використання не є локальним буфером, але ви можете чомусь хотіти, щоб один буфер мав інше значення. Це коли ви використовуєте make-local-variable, як правило, в якомусь коді настройки буфера майже ніколи після a defvar.

І як загальна річ, у defvarформ є дві цілі . Одне - мати деяку документацію, щоб інші C-h vмогли використовувати її, щоб знати, що таке змінна. Друге - оголосити значення "за замовчуванням", щоб змінна завжди була встановлена. Оголошення значення за замовчуванням впливає лише на глобальний (або за замовчуванням) екземпляр змінної і використовується лише в тому випадку, якщо цей екземпляр уже не встановлений на щось. Це означає, що якщо ви маєте setqякусь змінну, а потім включите файл із defvar(наприклад, через a require), defvar не змінить значення.

¹ Точніше, defvarне змінює значення змінної , якщо він вже встановлений (він встановлюється в рядок документації проте); це мається на увазі, щоб файл init користувача міг зробити (setq some-variable)перед завантаженням пакету, щоб замінити значення за замовчуванням пакета.


1
Дякую за дуже чітку відповідь, це дуже допомагає. Коли я використовував фразу "невідомі іншим файлам", я думав про безкоштовні попередження змінних, оскільки ці інші файли будуть вважати, що змінна є безкоштовною, якщо я також не додаю до них дефавар. Мені подобається ваше твердження, яке set-localповідомляє читачам, що встановлюється місцевий var. Все, що я можу зробити для поліпшення читабельності свого коду - це добре! PS. Я намагатимусь частіше переходити до джерела; на моєму нинішньому рівні розвитку, це часто насправді не потрапляє на семантику ... :-)
Кевін

1
" defvarдля кожної глобальної змінної має бути лише один " не є абсолютно коректним - бувають ситуації, коли (defvar VARNAME) без значення слід оголосити значення, незалежно від належного визначення (з початковим значенням і в ідеалі докстрингом) цього VARNAME.
філ

2
Слід також зазначити , що setq-localі defvar-localбули введені тільки в Emacs 24.3.
філ

1
@phils, ви можете, будь ласка, визначити ситуації, про які ви говорите, де (defvar varname)слід оголосити без значення? Я думаю, що це запропонував Дрю в іншій темі, коли я запитував, як позбутися від безкоштовних попереджувальних змін у своїх файлах для глобальних компаній, які я декларував в іншому місці. Я роблю це зараз. Це та ситуація, про яку ви говорите?
Кевін

1
Уникнення попереджень про компіляцію байтів є однією з причин, так (див. C-h i g (elisp) Warning Tips). Інша - для бібліотек, що використовують лексичні зв'язування, щоб переконатись (якщо необхідно), що змінна є динамічно пов'язаною, а не лексично пов'язаною.
філ
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.