Co robi `setq-local` i kiedy powinienem go używać?

16

Nie jestem całkiem pewien na temat wszystkich odmian zmiennych lokalnych buforów, nawet po przeczytaniu wszystkich dokumentów i kilku postów tutaj na SX.

Oto podsumowanie mojego zrozumienia:

(defvar foo ..)deklaruje zmienną dynamiczną dla pliku. Ale zmienna (1) nie jest znana innym plikom, chyba że zawierają one również defvar instrukcję, oraz (2) zmienna ma zasięg globalny, a nie bufor lokalny.

(make-variable-buffer-local foo)po defvarpowyższym informuje kompilator i wszystkich innych, że zmienna foo ma być traktowana jako bufor lokalna wszędzie tam, gdzie jest ustawiona, kiedy jest ustawiona. Ten wzorzec jest dobrym stylem do deklarowania zmiennej lokalnej bufora, umieszczając obie instrukcje w pliku z powrotem.

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

Dla wygody (defvar-local xxx ...)formularz można wykorzystać jako jedną linię zamiast dwóch linii powyżej:

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

Po zadeklarowaniu jak powyżej zmienna xxx może być używana jak każda inna zmienna w instrukcjach setq.

Jeśli chcę mieć tylko jedną instancję zmiennej lokalnej bufora, która jest już globalną zmienną dynamiczną, użyłbym następujących deklaracji. Pierwsza deklaruje zmienną dynamiczną o zasięgu globalnym, a druga instrukcja tworzy tylko jedną instancję lokalnej wersji tej zmiennej w buforze:

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

Teraz moje pytania wyjaśniające (wszystkie powyższe były dorozumianymi pytaniami, czy moje rozumienie jest prawidłowe).

Podczas ustawiania wartości zmiennych mogę użyć setqlub setq-local. Kiedy należy setq-localstosować? Dlaczego?

Co się stanie, jeśli korzystam setq-localz lokalnych zmiennych buforujących lub zmiennych lokalnych buforujących?

Czy setq-localwymagana jest defvar-localdeklarowana zmienna?

Czy setq-localnormalna defvardeklarowana zmienna zmieni ją w zmienną lokalną w buforze? (Innymi słowy, czy w setq-localjakiś sposób jest to odpowiednik (make-variable-local xxx)deklaracji?

Kevin
źródło
Dzięki za dodatkową pracę Scott. Odtąd wstawię dodatkowe cytaty z tyłu.
Kevin,
2
(setq-local VAR VALUE)jest po prostu skrótem (set (make-local-variable VAR) VALUE), który był (i nadal jest) powszechnym idiomem.
Drew

Odpowiedzi:

18

Większość twoich założeń jest bliska. Wspomnę kilka później. Ale najpierw najważniejsze pytanie.

Forma setq-localjest jedynie wygodą, jest taka sama jak robienie, make-local-variablepo której następuje setq. Jeśli zrobiłeś a, C-h f setq-localaby zobaczyć dokumentację i przeszedłeś do źródła, mógłbyś to zobaczyć. W ten sposób zweryfikowałem moje pierwsze wrażenie. Kod jest jednak nieco niejasny, ponieważ optymalizuje takie rzeczy, jak fakt, że make-local-variablefaktycznie zwraca samą zmienną. Użycie setq-localjest sposobem na wskazanie lokalności w pewnym kodzie, aby inni wiedzieli, że kod nie może grać z globalną wartością zmiennej. Zdajesz nie trzeba go używać, aby uzyskać dostęp zmienne lokalne. Każda zmienna może być lokalna w buforze, co wpłynie na cały kod, który dotyka zmiennej (z wyjątkiem sytuacji, gdy nie będzie w stanie uzyskać kopii globalnej za pomocą setq-defaultlub podobnej).

To jest podstawowa odpowiedź. Teraz w twoim tle jest kilka rzeczy, które są trochę nie w porządku. Skoro o to pytałeś, zajmę się niektórymi rzeczami.

Pierwszy jest „nieznany innym plikom”. To nie jest do końca prawda. Każdy kod może odwoływać się do dowolnej zmiennej globalnej (dlatego nazywane są „globalnymi”) bez uwzględnienia defvar(i C-h f defvartak mówi). W rzeczywistości powinna istnieć tylko jedna główna defvar(z dokumentacją i wartością domyślną) dla każdej zmiennej globalnej. A defvarz samą nazwą zmiennej można pominąć ostrzeżenie kompilatora bajtów. Emacs używa tylko wartości z pierwszej, którą widzi¹, oraz docstring z ostatniej. Jeśli jest wiele defvarznaków z wartością / docstring, mogą być mylące dla osób czytających kod. Kolejność ładowania plików będzie miała znaczenie.

Niektóre zmienne są przeznaczone do stosowania tylko bufor lokalnego i są to te, które zastosowanie defvar-local(lub dwuskładnikowa defvar/ make-variable-buffer-local, który jest za krótki, głównie w starszych kod przed skrótem dodano). To sprawia, że ​​jest buforowy lokalnie we wszystkich buforach. W przypadku niektórych zmiennych ich pierwotne zastosowanie nie jest lokalne dla bufora, ale z jakiegoś powodu możesz chcieć, aby jeden bufor miał inną wartość. To wtedy używasz make-local-variable, zwykle w jakimś kodzie konfiguracji bufora prawie nigdy zaraz po defvar.

I ogólnie rzecz biorąc, defvarformularze mają dwa cele . Jednym z nich jest posiadanie dokumentacji, która C-h vmoże być wykorzystana przez innych, aby dowiedzieć się, do czego służy zmienna. Drugim jest zadeklarowanie wartości „domyślnej”, aby zmienna była zawsze ustawiona. Deklaracja wartości domyślnej wpływa tylko na globalną (lub domyślną) instancję zmiennej i jest używana tylko wtedy, gdy ta instancja nie jest już ustawiona na coś. Oznacza to, że jeśli wprowadzisz setqjakąś zmienną, a następnie dodasz plik do defvar(np. Przez a require), defvar nie zmieni wartości.

¹ Dokładniej, defvarnie modyfikuje wartości zmiennej, jeśli jest już ustawiona (ustawia jednak dokumentację); oznacza to, że plik inicjujący użytkownika może to zrobić (setq some-variable)przed załadowaniem pakietu, aby zastąpić domyślną wartość pakietu.

MAPA
źródło
1
Dzięki za bardzo jasną odpowiedź, bardzo pomaga. Kiedy użyłem wyrażenia „nieznany innym plikom”, myślałem o ostrzeżeniach o zmiennej wolnej, ponieważ te inne pliki będą myśleć, że zmienna jest darmowa, chyba że dodam do nich również defvar. Podoba mi się twoje stwierdzenie, które set-localmówi czytelnikom, że ustawiana jest lokalna zmienna. Wszystko, co mogę zrobić, aby poprawić czytelność mojego kodu, jest dobre! PS. Spróbuję częściej przechodzić do źródła; na moim obecnym poziomie rozwoju często tak naprawdę nie zajmuje się semantyką ... :-)
Kevin
1
„powinien być tylko jeden defvardla każdej zmiennej globalnej” nie jest ściśle poprawne - zdarzają się sytuacje, w których (defvar VARNAME) bez wartości należy zadeklarować, niezależnie od właściwej definicji (o wartości początkowej i najlepiej dokumentacji) tej zmiennej.
phils
2
Należy również zauważyć, że setq-locali defvar-localzostały wprowadzone tylko w Emacs 24.3.
phils
1
@phils, czy mógłbyś podać sytuacje, o których mówisz, gdzie (defvar varname)należy zadeklarować bez wartości? Myślę, że właśnie to zasugerował Drew w innym wątku, gdy pytałem, jak pozbyć się bezpłatnych ostrzeżeń o zmiennych w moich plikach dla globali, które zadeklarowałem gdzie indziej. Robię to teraz. Czy o takiej sytuacji mówisz?
Kevin
1
Unikanie ostrzeżeń kompilacji bajtów jest jednym z powodów, tak (patrz C-h i g (elisp) Warning Tips). Drugi dotyczy bibliotek wykorzystujących wiązanie leksykalne, aby zapewnić (jeśli to konieczne), że zmienna jest dynamicznie związana, a nie leksykalnie.
phils