溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

關(guān)于Emacs中的變量需要知道的事情有哪些

發(fā)布時(shí)間:2021-11-01 17:39:34 來源:億速云 閱讀:127 作者:柒染 欄目:系統(tǒng)運(yùn)維

關(guān)于Emacs中的變量需要知道的事情有哪些,針對這個(gè)問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡單易行的方法。

學(xué)習(xí) Elisp 是如何處理變量的,以及如何在你的腳本與配置中使用它們。

GNU Emacs 是由 C 和 Emacs Lisp(Elisp,Lisp 編程語言的一種方言)寫成,它是一個(gè)編輯器的同時(shí),又碰巧是 Elisp 的沙盒。因此,理解 Elisp 中的一些基本編程概念會(huì)對你有一些幫助。

如果你是 Emacs 新手,請先閱讀 Sacha Chua 的《給 Emacs 新手的資源》精品帖。本篇文章假定你熟悉常見的 Emacs 術(shù)語,并且能夠閱讀并求值 Elisp 代碼的簡單片段。最好你也聽說過變量作用域的概念,知道它在其它編程語言中的作用。本篇文章中的示例假定你使用的是相對較新的 Emacs 版本(v.25 之后的版本)。

Elisp 手冊  包含了 Elisp 的方方面面,但它是寫給那些有明確查找目標(biāo)的人們的(它在這方面也做得相當(dāng)棒)。但是很多人想要能夠在更高的層次上解釋  Elisp  概念的材料,同時(shí)將信息壓縮成最精華的部分。本篇文章也正是我回應(yīng)這種呼聲的一次嘗試,為讀者描繪基礎(chǔ)的大體輪廓。使他們能在配置中用上這些技巧,也讓他們在手冊中查詢細(xì)節(jié)變得更容易。

全局變量

defcustom 定義的用戶設(shè)置和用 defvardefconst 定義的變量是全局的。使用 defcustomdefvar 聲明變量的一個(gè)非常重要的原因是,當(dāng)一個(gè)變量已經(jīng)被綁定bind,對它們進(jìn)行重新求值不會(huì)覆蓋掉已有的值。舉個(gè)栗子,如果你在初始化文件中對 my-var 進(jìn)行如下綁定:

(setq my-var nil)

對如下表達(dá)式求值不會(huì)將變量覆蓋為 t

(defvar my-var t)

注意此處有一個(gè)例外:如果你用 C-M-x 快捷鍵對上述聲明求值,它將調(diào)用 eval-defun 函數(shù),并將變量覆蓋為 t。通過此方式,你可以按需將變量強(qiáng)制覆蓋。這種行為是刻意而為之的:你可能知道,Emacs 中的許多特性是按需加載的,也可以稱為自動(dòng)加載。如果那些文件中的聲明將變量覆蓋為它們的默認(rèn)值,那它也就覆蓋了你初始化文件中的設(shè)置。

用戶選項(xiàng)

用戶選項(xiàng)就是使用 defcustom 聲明的全局變量。與使用 defvar 聲明的變量不同,這些變量可以用 M-x customize 界面來配置。據(jù)我所知,大部分人因?yàn)橛X得它開銷較大而不經(jīng)常使用。一旦你知道如何在你的初始化文件中設(shè)置變量,也就沒有理由一定要去使用它了。許多用戶沒有意識(shí)到的一個(gè)細(xì)節(jié)是,通過 customize 的方式設(shè)置用戶選項(xiàng)能夠執(zhí)行代碼,有的時(shí)間可用來運(yùn)行一些附加的配置說明:

(defcustom my-option t  "My user option."  :set (lambda (sym val)         (set-default sym val)         (message "Set %s to %s" sym val)))

若你對這段代碼求值,并鍵入 M-x customize-option RET my-option RET 運(yùn)行 customize 界面,lambda 匿名函數(shù)就會(huì)被調(diào)用,回顯區(qū)域就會(huì)顯示出該選項(xiàng)的符號(hào)名與值。

如果你在初始化文件中使用 setq 改變該選項(xiàng)的值,那么匿名函數(shù)不會(huì)運(yùn)行。要想在 Elisp 中正確設(shè)置一個(gè)選項(xiàng),你需要使用函數(shù) customize-set-variable。或者,人們在他們的配置文件中使用了各種版本的 csetq 宏來自動(dòng)處理(如你所愿,你可以通過 GitHub 的代碼搜索發(fā)現(xiàn)更復(fù)雜的變體)。

(defmacro csetq (sym val)  `(funcall (or (get ',sym 'custom-set) 'set-default) ',sym ,val))

若你正在使用 use-package 宏,:custom 關(guān)鍵字會(huì)替你處理好以上這些。

在你將以上代碼放入到你的初始化文件中之后,你便可以使用 csetq 宏在設(shè)置變量的同時(shí)運(yùn)行任何現(xiàn)存的 setter 函數(shù)。要證明這點(diǎn),你可以使用此宏來改變上面定義的選項(xiàng),并觀察回顯區(qū)域的消息輸出。

(csetq my-option nil)

動(dòng)態(tài)綁定與詞法綁定

當(dāng)你在使用其它編程語言時(shí),你可能不會(huì)意識(shí)到動(dòng)態(tài)綁定與詞法綁定的區(qū)別。當(dāng)今的大部分編程語言使用詞法綁定,并且在學(xué)習(xí)變量作用域與變量查找時(shí)也沒有必要去了解它們之間的區(qū)別。

如此看來,Emacs Lisp 比較特殊因?yàn)閯?dòng)態(tài)綁定是默認(rèn)選項(xiàng),詞法綁定需要顯式啟用。這里有一些歷史遺留原因,但在實(shí)際使用中,你應(yīng)該時(shí)刻啟用詞法綁定,因?yàn)樗觳⑶也蝗菀壮鲥e(cuò)。要啟用詞法綁定,只需將如下的注釋行作為你的 Emacs Lisp 文件的第一行:

;;; -*- lexical-binding: t; -*-

另一種方式,你可以調(diào)用 add-file-local-variable-prop-line,在你選擇將變量 lexical-binding 置為 t 后,會(huì)自動(dòng)插入如上的注釋行。

在加載包含如上特殊格式行的文件時(shí),Emacs 會(huì)相應(yīng)地設(shè)置變量,這意味著該緩沖區(qū)中的代碼加載時(shí)啟用了詞法綁定。若要采用交互式的方式,你可以調(diào)用 M-x eval-buffer 命令,它會(huì)將詞法綁定考慮在內(nèi)。

既然你已經(jīng)知道了如何啟用詞法綁定,那么了解這些術(shù)語的含義就很明智了。對于動(dòng)態(tài)綁定,在程序執(zhí)行期間建立的最后一個(gè)綁定將用于變量查找。你可以通過將以下代碼放入空緩沖區(qū)并執(zhí)行 M-x eval buffer,以對此進(jìn)行測試:

(defun a-exists-only-in-my-body (a)  (other-function)) (defun other-function ()  (message "I see `a', its value is %s" a)) (a-exists-only-in-my-body t)

你可能會(huì)很驚訝地發(fā)現(xiàn),在 other-function 中查找變量 a 竟然成功了。

若你在頂部添加了特殊的詞法綁定注釋后,重新運(yùn)行前面的示例,這段代碼將拋出 variable is void 錯(cuò)誤,因?yàn)?other-functioin 無法識(shí)別變量 a。如果你使用的是其它編程語言,這才是你所期望的行為。

啟用詞法綁定后,作用域會(huì)由周圍的代碼所定義。這并不單單是性能原因,時(shí)間也已經(jīng)表明了詞法綁定才是更受喜愛的。

特殊變量與動(dòng)態(tài)綁定

如你所知,let 用于臨時(shí)建立局部綁定:

(let ((a "I'm a")      (b "I'm b"))  (message "Hello, %s. Hello %s" a b))

接下來有趣的是——使用 defcustom、defvar 以及 defconst 定義的變量被稱為特殊變量,不論詞法綁定是否啟用,它們都將使用動(dòng)態(tài)綁定:

;;; -*- lexical-binding: t; -*- (defun some-other-function ()  (message "I see `c', its value is: %s" c)) (defvar c t) (let ((a "I'm lexically bound")      (c "I'm special and therefore dynamically bound"))  (some-other-function)  (message "I see `a', its values is: %s" a))

通過 C-h e 切換至 Messages 緩沖區(qū),查看上述示例輸出的消息。

使用 let 或者函數(shù)參數(shù)綁定的局部變量會(huì)遵循由 lexical-binding 變量定義的查找規(guī)則,但使用 defvardefconstdefcustom 定義的全局變量,能夠沿著調(diào)用棧在 let 表達(dá)式中被修改。

這種技巧允許方便地進(jìn)行特殊定制,并且經(jīng)常在 Emacs 中被使用。這并不奇怪,畢竟 Emacs Lisp 最開始只提供動(dòng)態(tài)綁定作為唯一選擇。下面是一個(gè)常見的示例,說明如何向只讀緩沖區(qū)臨時(shí)寫入數(shù)據(jù):

(let ((inhibit-read-only t))  (insert ...))

這是另一個(gè)常見的示例,如何進(jìn)行大小寫敏感的搜索:

(let ((case-fold-search nil))  (some-function-which-uses-search ...))

動(dòng)態(tài)綁定允許你采用作者未曾預(yù)料的方式對函數(shù)進(jìn)行修改。對于像 Emacs 這樣設(shè)計(jì)使用的程序來說,這是個(gè)強(qiáng)大的工具與特性。

有一點(diǎn)需要注意:你可能會(huì)意外地使用局部變量名,該變量在其他地方被聲明為特殊變量。防止這種沖突的一個(gè)技巧是避免在局部變量名中使用下劃線。在我當(dāng)前的 Emacs 會(huì)話中,以下代碼只留下少數(shù)潛在沖突的候選:

(let ((vars ()))  (mapatoms   (lambda (cand)     (when (and (boundp cand)                (not (keywordp cand))                (special-variable-p cand)                (not (string-match "-"                                   (symbol-name cand))))       (push cand vars))))  vars) ;; => (t obarray noninteractive debugger nil)

緩沖區(qū)局部變量

每個(gè)緩沖區(qū)都能夠擁有變量的一個(gè)局部綁定。這就意味著對于任何變量,都會(huì)首先在當(dāng)前緩沖區(qū)中查找緩沖區(qū)局部變量取代默認(rèn)值。局部變量是 Emacs 中一個(gè)非常重要的特性,比如它們被主模式用來建立緩沖區(qū)范圍內(nèi)的行為與設(shè)置。

事實(shí)上你已經(jīng)在本文中見過緩沖區(qū)局部變量——也就是將 lexical-binding 在緩沖區(qū)范圍內(nèi)設(shè)置為 t 的特殊注釋行。在 Emacs 中,在特殊注釋行中定義的緩沖區(qū)局部變量也被稱為文件局部變量。

任何的全局變量都可以用緩沖區(qū)局部變量來遮掩,比如上面定義的變量 my-var,你可用如下方式設(shè)置局部變量:

(setq-local my-var t);; or (set (make-local-variable 'my-var) t)

此時(shí) my-var 對于你在對上述代碼進(jìn)行求值時(shí)對應(yīng)的緩沖區(qū)來說就是局部變量。若你對它調(diào)用 describe-variable,文檔會(huì)同時(shí)告訴你局部與全局的值。從編程的角度來講,你可以分別用 buffer-local-value 獲取局部值,用 default-value 獲取全局值。若要移除局部值,你可以調(diào)用 kill-local-variable

另一個(gè)需要注意的重要性質(zhì)就是,一旦一個(gè)變量成為緩沖區(qū)局部變量,后續(xù)在該緩沖區(qū)中使用的 setq 都將只能設(shè)置局部的值。要想設(shè)置默認(rèn)值,你需要使用 setq-default

因?yàn)榫植孔兞恳馕吨鴮彌_區(qū)的定制,它們也就經(jīng)常被用于模式鉤子中。一個(gè)典型的例子如下所示:

(add-hook 'go-mode-hook          (defun go-setup+ ()            (setq-local compile-command              (if (string-suffix-p "_test.go" buffer-file-name)                  "go test -v"                (format "go run %s"                        (shell-quote-argument                         (file-name-nondirectory buffer-file-name)))))))

這將設(shè)置 go-mode 緩沖區(qū)中 M-x compile 使用的編譯命令。

另一個(gè)重要的方面就是一些變量會(huì)自動(dòng)成為緩沖區(qū)局部變量。這也就意味著當(dāng)你使用 setq 設(shè)置這樣一個(gè)變量時(shí),它會(huì)針對當(dāng)前緩沖區(qū)設(shè)置局部綁定。這個(gè)特性不應(yīng)該被經(jīng)常使用,因?yàn)檫@種隱式的行為并不好。不過如果你想的話,你可以使用如下方法創(chuàng)建自動(dòng)局部變量:

(defvar-local my-automatical-local-var t);; or (make-variable-buffer-local 'my-automatical-local-var)

變量 indent-tabs-mode 就是 Emacs 內(nèi)建的一個(gè)例子。如果你在初始化文件中使用 setq 改變變量的值,根本不會(huì)影響默認(rèn)值。只有在你加載初始化文件時(shí)正處在當(dāng)前的緩沖區(qū)的局部值會(huì)被改變。因此,你需要使用 setq-default 來改變 indent-tabs-mode 的默認(rèn)值。

Emacs 是一個(gè)強(qiáng)大的編輯器,并且隨著你的定制它將變得更加強(qiáng)大?,F(xiàn)在,你知道了 Elisp 是如何處理變量的,以及你應(yīng)如何在你自己的腳本與配置中使用它們。

關(guān)于關(guān)于Emacs中的變量需要知道的事情有哪些問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI