summaryrefslogtreecommitdiffhomepage
path: root/zh-cn/elisp-cn.html.markdown
blob: 06f38d770f45a63a826f8c67fa6c72153019817d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
---
language: elisp
contributors:
    - ["Bastien Guerry", "http://bzg.fr"]
translators:
    - ["Chenbo Li", "http://binarythink.net"]
filename: learn-emacs-lisp-zh.el
lang: zh-cn
---

```scheme
;; 15分钟学会Emacs Lisp (v0.2a)
;;(作者:bzg,https://github.com/bzg 
;;  译者:lichenbo,http://douban.com/people/lichenbo)
;;
;; 请先阅读Peter Norvig的一篇好文:
;; http://norvig.com/21-days.html
;; (译者注:中文版请见http://blog.youxu.info/21-days/)
;;
;; 之后安装GNU Emacs 24.3:
;;
;; Debian: apt-get install emacs (视具体发行版而定)
;; MacOSX: http://emacsformacosx.com/emacs-builds/Emacs-24.3-universal-10.6.8.dmg
;; Windows: http://ftp.gnu.org/gnu/windows/emacs/emacs-24.3-bin-i386.zip
;;
;; 更多信息可以在这里找到:
;; http://www.gnu.org/software/emacs/#Obtaining
 
;; 很重要的警告:
;;
;; 按照这个教程来学习并不会对你的电脑有任何损坏
;; 除非你自己在学习的过程中愤怒地把它砸了
;; 如果出现了这种情况,我不会承担任何责任
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 
;; 打开emacs
;;
;; 按'q'消除欢迎界面
;;
;; 现在请注意窗口底部的那一个灰色长条
;;
;; "*scratch*" 是你现在编辑界面的名字。
;; 这个编辑界面叫做一个"buffer"。
;;
;; 每当你打开Emacs时,都会默认打开这个scratch buffer
;; 此时你并没有在编辑任何文件,而是在编辑一个buffer
;; 之后你可以将这个buffer保存到一个文件中。
;; 
;; 之后的"Lisp interaction" 则是表明我们可以用的某组命令
;; 
;; Emacs在每个buffer中都有一组内置的命令
;; 而当你激活某种特定的模式时,就可以使用相应的命令
;; 这里我们使用`lisp-interaction-mode',
;; 这样我们就可以使用内置的Emacs Lisp(以下简称Elisp)命令了。
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 分号是注释开始的标志
;;
;; Elisp 是由符号表达式构成的 (即"s-表达式"或"s式"):
(+ 2 2)
 
;; 这个s式的意思是 "对2进行加2操作".
 
;; s式周围有括号,而且也可以嵌套:
(+ 2 (+ 1 1))
 
;; 一个s式可以包含原子符号或者其他s式
;; 在上面的例子中,1和2是原子符号
;; (+ 2 (+ 1 1)) 和 (+ 1 1) 是s式.
 
;; 在 `lisp-interaction-mode' 中你可以计算s式.
;; 把光标移到闭括号后,之后按下ctrl+j(以后简写为'C-j')
 
(+ 3 (+ 1 2))
;;           ^ 光标放到这里
;; 按下`C-j' 就会输出 6
 
;; `C-j' 会在buffer中插入当前运算的结果
 
;; 而`C-xC-e' 则会在emacs最底部显示结果,也就是被称作"minibuffer"的区域
;; 为了避免把我们的buffer填满无用的结果,我们以后会一直用`C-xC-e'
 
;; `setq' 可以将一个值赋给一个变量
(setq my-name "Bastien")
;; `C-xC-e' 输出 "Bastien" (在 mini-buffer 中显示)
 
;; `insert' 会在光标处插入字符串:
(insert "Hello!")
;; `C-xC-e' 输出 "Hello!"
 
;; 在这里我们只传给了insert一个参数"Hello!", 但是
;; 我们也可以传给它更多的参数,比如2个:
 
(insert "Hello" " world!")
;; `C-xC-e' 输出 "Hello world!"
 
;; 你也可以用变量名来代替字符串
(insert "Hello, I am " my-name)
;; `C-xC-e' 输出 "Hello, I am Bastien"
 
;; 你可以把s式嵌入函数中
(defun hello () (insert "Hello, I am " my-name))
;; `C-xC-e' 输出 hello
 
;; 现在执行这个函数
(hello)
;; `C-xC-e' 输出 Hello, I am Bastien
 
;; 函数中空括号的意思是我们不需要接受任何参数
;; 但是我们不能一直总是用my-name这个变量
;; 所以我们现在使我们的函数接受一个叫做"name"的参数 
 
(defun hello (name) (insert "Hello " name))
;; `C-xC-e' 输出 hello
 
;; 现在我们调用这个函数,并且将"you"作为参数传递
 
(hello "you")
;; `C-xC-e' 输出 "Hello you"
 
;; 成功!
 
;; 现在我们可以休息一下
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 下面我们在新的窗口中新建一个名为 "*test*" 的buffer:
 
(switch-to-buffer-other-window "*test*")
;; `C-xC-e' 这时屏幕上会显示两个窗口,而光标此时位于*test* buffer内
 
;; 用鼠标单击上面的buffer就会使光标移回。
;; 或者你可以使用 `C-xo' 使得光标跳到另一个窗口中
 
;; 你可以用 `progn'命令将s式结合起来:
(progn
  (switch-to-buffer-other-window "*test*")
  (hello "you"))
;; `C-xC-e' 此时屏幕分为两个窗口,并且在*test* buffer中显示"Hello you"
 
;; 现在为了简洁,我们需要在每个s式后面都使用`C-xC-e'来执行,后面就不再说明了
 
;; 记得可以用过鼠标或者`C-xo'回到*scratch*这个buffer。
 
;; 清除当前buffer也是常用操作之一:
(progn
  (switch-to-buffer-other-window "*test*")
  (erase-buffer)
  (hello "there"))
 
;; 也可以回到其他的窗口中
(progn
  (switch-to-buffer-other-window "*test*")
  (erase-buffer)
  (hello "you")
  (other-window 1))
 
;; 你可以用 `let' 将一个值和一个局部变量绑定:
(let ((local-name "you"))
  (switch-to-buffer-other-window "*test*")
  (erase-buffer)
  (hello local-name)
  (other-window 1))
 
;; 这里我们就不需要使用 `progn' 了, 因为 `let' 也可以将很多s式组合起来。
 
;; 格式化字符串的方法:
(format "Hello %s!\n" "visitor")
 
;; %s 是字符串占位符,这里被"visitor"替代.
;; \n 是换行符。
 
;; 现在我们用格式化的方法再重写一下我们的函数:
(defun hello (name)
  (insert (format "Hello %s!\n" name)))
 
(hello "you")
 
;; 我们再用`let'新建另一个函数:
(defun greeting (name)
  (let ((your-name "Bastien"))
    (insert (format "Hello %s!\n\nI am %s."
                    name       ; the argument of the function
                    your-name  ; the let-bound variable "Bastien"
                    ))))
 
;; 之后执行:
(greeting "you")
 
;; 有些函数可以和用户交互:
(read-from-minibuffer "Enter your name: ")
 
;; 这个函数会返回在执行时用户输入的信息
 
;; 现在我们让`greeting'函数显示你的名字:
(defun greeting (from-name)
  (let ((your-name (read-from-minibuffer "Enter your name: ")))
    (insert (format "Hello!\n\nI am %s and you are %s."
                    from-name ; the argument of the function
                    your-name ; the let-bound var, entered at prompt
                    ))))
 
(greeting "Bastien")
 
;; 我们让结果在另一个窗口中显示:
(defun greeting (from-name)
  (let ((your-name (read-from-minibuffer "Enter your name: ")))
    (switch-to-buffer-other-window "*test*")
    (erase-buffer)
    (insert (format "Hello %s!\n\nI am %s." your-name from-name))
    (other-window 1)))
 
;; 测试一下:
(greeting "Bastien")
 
;; 第二节结束,休息一下吧。
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 我们将一些名字存到列表中:
(setq list-of-names '("Sarah" "Chloe" "Mathilde"))
 
;; 用 `car'来取得第一个名字:
(car list-of-names)
 
;; 用 `cdr'取得剩下的名字:
(cdr list-of-names)
 
;; 用 `push'把名字添加到列表的开头:
(push "Stephanie" list-of-names)
 
;; 注意: `car' 和 `cdr' 并不修改列表本身, 但是 `push' 却会对列表本身进行操作.
;; 这个区别是很重要的: 有些函数没有任何副作用(比如`car')
;; 但还有一些却是有的 (比如 `push').
 
;; 我们来对`list-of-names'列表中的每一个元素都使用hello函数:
(mapcar 'hello list-of-names)
 
;; 将 `greeting' 改进,使的我们能够对`list-of-names'中的所有名字执行:
(defun greeting ()
    (switch-to-buffer-other-window "*test*")
    (erase-buffer)
    (mapcar 'hello list-of-names)
    (other-window 1))
 
(greeting)
 
;; 记得我们之前定义的 `hello' 函数吗? 这个函数接受一个参数,名字。
;; `mapcar' 调用 `hello', 并将`list-of-names'作为参数先后传给`hello'
 
;; 现在我们对显示的buffer中的内容进行一些更改:
 
(defun replace-hello-by-bonjour ()
    (switch-to-buffer-other-window "*test*")
    (goto-char (point-min))
    (while (search-forward "Hello")
      (replace-match "Bonjour"))
    (other-window 1))
 
;; (goto-char (point-min)) 将光标移到buffer的开始
;; (search-forward "Hello") 查找字符串"Hello"
;; (while x y) 当x返回某个值时执行y这个s式
;; 当x返回`nil' (空), 退出循环
 
(replace-hello-by-bonjour)
 
;; 你会看到所有在*test* buffer中出现的"Hello"字样都被换成了"Bonjour"
 
;; 你也会得到以下错误提示: "Search failed: Hello".
;;
;; 如果要避免这个错误, 你需要告诉 `search-forward' 这个命令是否在
;; buffer的某个地方停止查找, 并且在什么都没找到时是否应该不给出错误提示
 
;; (search-forward "Hello" nil t) 可以达到这个要求:
 
;; `nil' 参数的意思是 : 查找并不限于某个范围内
;; `t' 参数的意思是: 当什么都没找到时,不给出错误提示
 
;; 在下面的函数中,我们用到了s式,并且不给出任何错误提示:
 
(defun hello-to-bonjour ()
    (switch-to-buffer-other-window "*test*")
    (erase-buffer)
    ;; 为`list-of-names'中的每个名字调用hello
    (mapcar 'hello list-of-names)
    (goto-char (point-min))
    ;; 将"Hello" 替换为"Bonjour"
    (while (search-forward "Hello" nil t)
      (replace-match "Bonjour"))
    (other-window 1))
 
(hello-to-bonjour)
 
;; 给这些名字上个色:
 
(defun boldify-names ()
    (switch-to-buffer-other-window "*test*")
    (goto-char (point-min))
    (while (re-search-forward "Bonjour \\(.+\\)!" nil t)
      (add-text-properties (match-beginning 1)
                           (match-end 1)
                           (list 'face 'bold)))
    (other-window 1))
 
;; 这个函数使用了 `re-search-forward':
;; 和查找一个字符串不同,你用这个命令可以查找一个模式,即正则表达式
 
;; 正则表达式 "Bonjour \\(.+\\)!" 的意思是:
;; 字符串 "Bonjour ", 之后跟着
;; 一组           |  \\( ... \\) 结构
;; 任意字符       |  . 的含义
;; 有可能重复的   |  + 的含义
;; 之后跟着 "!" 这个字符串
 
;; 准备好了?试试看。
 
(boldify-names)
 
;; `add-text-properties' 可以添加文字属性, 比如文字样式
 
;; 好的,我们成功了!
 
;; 如果你想对一个变量或者函数有更多的了解:
;;
;; C-h v 变量 回车
;; C-h f 函数 回车
;;
;; 阅读Emacs Lisp官方文档:
;;
;; C-h i m elisp 回车
;;
;; 在线阅读Emacs Lisp文档:
;; https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html
 
;; 感谢以下同学的建议和反馈:
;; - Wes Hardaker
;; - notbob
;; - Kevin Montuori
;; - Arne Babenhauserheide
;; - Alan Schmitt
```