1 前言

最近正在学习elisp的基础部分,下面是一些知识点的总结。

2 总述

2.1 表达式

Lisp程序由表达式构成,表达式分为两种:

  1. 原子
  2. 列表

3 列表

3.1 构成

由()及之中的由空格分隔的原子构成。 如(原子1 原子2 原子3),原子之间使用空格分隔。

3.2 lisp解释器遇到列表时的处理方式

lisp遇到列表时,按照如下步骤进行处理

  1. 查看列表前面是否有单引号,如果存在就直接给出这个列表。
  2. 如果不存在,查看第一个元素,如果是一个有效的函数,那么就执行它。
  3. 否则报错。

3.2.1 结论

列表前面的单引号('), 称为引用(quote). 如果这个单引号存在, 那么lisp解释器认为他就是一个列表. 否则,认为他是一条指令(就是对一个lisp函数的调用), 指令的名称就是列表的第一个元素, 后面的原子为参数.

3.3 空列表

()称为空列表 注意 空列表既是一个原子, 又是一个列表

4 原子

4.1 原子的种类

  1. 列表
  2. 数字
  3. 字符串 "[a-z1-9]"
  4. 引用 在列表的左边加上一个',如'(+ 7 6)
  5. 函数 只有列表中的第一个原子可以是函数
  6. 变量

5 符号

5.1 概念

程序中任意的字符(不包括数字)都是一个符号。 一个符号可以被赋值, 同时它可以被赋予一个函数.

(+ 2 3) ;;鼠标直接移动到+后面执行时,会报错.
(setq + "haha") ;;给+号赋值.
(+ 2 3) ;;再次对+求值, 成功.

6 lisp解释器的工作方式

最基本的, 对于列表以上述方式进行处理.除此以外还有三种工作方式:

  1. 如果遇到一个符号, 没有引号也没有括号包围它. 这种情况下, lisp解释器将试图像变量一样确定符号的值.
  2. 如果是一个指令列表, 并且指令是一些特殊的关键字的函数(这些函数称为特殊表(special form)). 他们会执行一些预定义好的特殊操作, 比如(defun …),这就会定义一个函数.
  3. 如果列表中嵌套了列表, 那么解释器会按照从内到外, 从左到右的顺序处理列表.既首先处理嵌套在最内部的列表, 再从左至右执行.
'(rose violet daisy buttercup (a list in side)) ;;如果列表前面有'(单引号), 那么就认为这就是个单纯的列表
(rose violet daisy buttercup (a list in side)) ;;执行会报错, 因为前面没有', 因此认为这是个函数调用, 列表的第一个原子为函数名,这个函数不存在.
(+ 2 (- 3 1)) ;;按照从内到外,从左到右的顺序处理
fill-column ;;这是一个变量
"fill-column" ;;这是一个字符串

7 变量

7.1 全局变量

7.1.1 赋值方式

(setq abc [原子])

7.2 局部变量

赋值方式

(let (变量的定义)(执行的命令)...)

例子

(let ((left 100) dd cc (right 200)) (message "dfdfdsf") )

绑定多个变量

;; 这里会报错, let中的变量定义部分所有变量之前不可见
(let ((a "dfsf")
(b (concat a "cc")))

(print b))


;; 可以使用如下方式
(let* ((a "dfsf")
(b (concat a "cc")))

(print b))

8 执行指令

有四种情况

8.1 当光标在一个列表的后面时执行

此时第一个原子被看成一个函数,后面的原子为此函数的参数,返回其计算的结果。

8.2 当光标在一个字符串上面时执行

返回字符串本身。

8.3 当光标在一个数字上面时执行

返回数字本身。

8.4 当光标在一个被引用的列表的后面时执行

返回列表本身。

9 nil和空列表()

nil和空列表都代表false,其他全部认为是真。

10 emacsclient 启动参数

10.1 –eval 执行elisp代码

可以使用如下方式在启动时执行特定的elisp代码,重点是eval参数的值里面为要运行的代码,每一条指令之间是以空格分隔的 emacsclient –eval "(prong (setq baseDir \"~/tmp/www\") \ (setq publishing-dir \"~/tmp/output\") (setq publishing-dir-static \"~/tmp/output/static\") \ (load-file \"$(ORGCONFIGFILE)\"))"

11 函数

11.1 格式

(defun 函数名 (参数列表…) "文档字符串" 可选 (interactive …) 可选 体..)

11.1.1 例子

(defun helloworld(msg msg2) "the first elisp function" (let ((prefix " haha")) (concat prefix " " msg))) (helloworld "cy" "ch")

11.2 查询某个函数的文档

C-h f **

12 with-eval-after-load

由于上述的auload的执行逻辑,看如下代码:

(require 'a); 在a这个package中有一个autoload函数kk,其内部对abc的初始化, 当代码执行到这里时对abc的初始化不会执行
(setq abc "haha) ;这里abc被设置为haha
(kk) ;;请求执行kk, 执行对abc的初始化

;;这里实际上的意图是希望对abc进行自定义为"haha",但是可以看到并不成功.

为了解决这个问题, 引入with-eval-after-load

(with-eval-after-load 'helm
;; Code
)

;;这样写之后, 只有在helm被真正导入后才执行内部代码

13 emacs导入package的流程

对于pakcge的导入需要考虑三个方面的问题

  1. 正确地导入pakcge
  2. 顺序合理
  3. 性能(一个可能的解决方案:如果可能尽量不要一次性全部导入, 而是在实际使用的时候再导入)

考虑最简单的情况, emacs中通过

(load-file "~/elisp/foo.el")

来导入文件

更进一步, 可以通过在一个.el文件中添加(provide 'my-feature)声明一个feature, 之后通过(require 'my-feature)来导入, 具体过程如下

This checks whether the feature my-feature has already been loaded. If not, it looks for a file called my-feature.el, my-feature.elc or some such. If it finds such a file, it will load it. When the call to provide is evaluated, the feature is added to the list of loaded features, so that subsequent calls to require will do nothing. This will cause an error if no such file can be found.

The file my-feature.el may very well contain other calls to require, and in fact this is quite a common way to ensure that dependencies are loaded before your code runs.

Pacpkage authors should use this technique to make sure that dependencies are loaded before their code runs.

13.1 设置package文件所在路径

(push "/some/path/" load-path)

13.2 autoload

(require) 可以保证package被正确的导入,但是他们会一次性全部被导入,从而导致性能问题. 因此可以采用autoload技术来解决此问题.

在希望被autoload的方法上面如此配置:

;;;###autoload
(defun my-function ()
;; Source code...
)

这样配置后, 当(require)时,此方法会快速被创建,但不执行内部的具体代码,直到此方法被执行的时候, 其内部才会执行.

13.3 with-eval-after-load

由于上述的auload的执行逻辑,看如下代码:

(require 'a); 在a这个package中有一个autoload函数kk,其内部对abc的初始化, 当代码执行到这里时对abc的初始化不会执行
(setq abc "haha) ;这里abc被设置为haha
(kk) ;;请求执行kk, 执行对abc的初始化

;;这里实际上的意图是希望对abc进行自定义为"haha",但是可以看到并不成功.

为了解决这个问题, 引入with-eval-after-load

(with-eval-after-load 'helm
;; Code
)

;;这样写之后, 只有在helm被真正导入后才执行内部代码

14 在org mode中插入图片时,想要插入一些html属性

15 键绑定

;;; 绑定热键时, 函数必须是interactive的
(global-set-key (kbd "C-c C-;") (lambda () (interactive) (org-publish-project "html")))

16 语法引用

(setq org-publish-project-alist
;;; `相当于clojure中的语法引用(0)
	`(("html-source"
;;; ,相当于clojure中的反引用(~)
	   :base-directory ,(f-dirname (current-buffer-file))

	   :base-extension "org"
	   :publishing-directory ,(f-no-ext (current-buffer-file))
	   :publishing-function org-html-publish-to-html)
	  ("org-static"
	   :base-directory "/Applications/workspace/docs/emacs/emacs/OrgMode-Html-Themes" ;; Change this to your local dir
	   :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|txt\\|asc\\|svg"
	   :publishing-directory ,(f-expand "styles" (f-no-ext (current-buffer-file)))
	   :recursive t
	   :publishing-function org-publish-attachment
	   )
	  ("org-static-1"
	   :base-directory ,(f-dirname (current-buffer-file))
	   :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|txt\\|asc\\|svg"
	   :publishing-directory ,(f-no-ext (current-buffer-file))
	   :recursive t
	   :publishing-function org-publish-attachment
	   )
	  ("pdf"
	   :base-directory "~/org/"
	   :base-extension "org"
	   :publishing-directory "~/org/exports"
	   :publishing-function org-publish-org-to-pdf)
	  ("html" :components ("html-source" "org-static" "org-static-1"))))