- 浏览: 233912 次
文章分类
最新评论
-
sunyukun8888:
多谢啦!
重新整理后的Oracle OAF学习笔记——离线版本 -
singlespider:
很不错啊,谢楼主
重新整理后的Oracle OAF学习笔记——离线版本 -
000fuli:
000fuli 写道请问各位学长:你们可以下载吗?能下载的烦请 ...
重新整理后的Oracle OAF学习笔记——离线版本 -
000fuli:
请问各位学长:你们可以下载吗?能下载的烦请发一份到fuli.w ...
重新整理后的Oracle OAF学习笔记——离线版本 -
goodscript:
确实是不错的好文章!
重新整理后的Oracle OAF学习笔记——离线版本
准备图表
我们的目标是构造一个图表显示Emacs lisp源码中所有函数定义的长度范围。
在实际应用中,如果你要创建一个图表,你可能会使用gnuplot之类的程序来完成这个工作。(gnuplot与GNU Emacs集成得很好。)但在这里,我们将使用前面我们所学的知识来完成这个工作。
在这章,我们将先编写一个简单的图表打印函数。第一个版本将作为原型,在此基础上来增强。
打印图表列
由于Emacs被设计为能在各种终端上工作,包括字符终端,图表需要是可打印字符。我们可以使用星号来打印图表。
我们把这个函数命名为graph-body-print;它使用numbers-list作为参数。
graph-body-print函数根据numbers-list中的每个原素,分别插入垂直方向的星号列。每一列的高度取决于numbers-list上元素值的大小。
插入列是一个重复动作,因此函数可以用while循环或递归实现。
我们面临的第一个挑战就是如何打印星号列。通常,在Emacs我们打印字符的时候是横向打印的,一行一行的打印。我们有两个办法来实现:编写我们自己的列插入函数或者查找Emacs中是否有现成的方法。
为查找Emacs中的函数,我们可以使用M-x apropos命令。这个命令与C-h a(command-apropos)命令类似,但后者只查找作为命令的函数。而M-x apropos命令将列出所有匹配正则表达式的符号,包括那些非交互式的函数。
我们想找到那些可以打印或插入纵向列的命令。这个函数的名称肯定包含有'print'或'insert'或'column'等单词。因此,我们只要输入M-x apropos RET print\|insert\|column RET
并查看结果。在我们系统上,这个命令执行需要一些时间,结果包含有79个函数和变量。查找这个列表,我们看到有个insert-rectangle函数有可能能完成这个工作。
这个函数文档如下:
insert-rectangle:
Insert text of RECTANGLE with upper left corner at point.
RECTANGLE's first line is inserted at point,
its second line is inserted at a point vertically under point, etc.
RECTANGLE should be a list of strings.
我们可以测试一下,以确认它是否如我们期望的那样工作。
把光标放在insert-rectange语句的后面按C-u C-x C-e(eval-last-sexp)。这个函数将在point的下面插入"first","second","third"。函数返回值为nil。
(insert-rectangle '("first" "second" "third"))first在绘制图表的程序中使用这上函数。我们需要先确保point位于需要插入的位置,然后用insert-rectangle函数插入列。
second
third
nil
如果你是在Info中读取这个文档,你可以切换到另一个缓冲区,比如*scratch*
,将point放在任何地方,输入M-:
,在提示区输入insert-rectangle语句,然后回车。Emacs将执行输入的语句,交把*scratch*
缓冲区中的point位置作为point的值。(M-:
被绑定到eval-expression上。)
我们将发现当执行完成插入后,point被设置在了最后插入的那行,也就是说这个函数移动了point。如果我们重复执行这个命令,下次插入的内容将在上次插入内容的下面。我们并不需要这样,我们需要的是一个柱状图表,一列挨着一列。
我们看出每次while循环插入列时必须重新设置point的位置,这个位置必须在列的顶部,而不是在底部。并且,我们打印图表时,并不需要每个列 都一样高。这意味着每个列的顶部并不是一样高的。我们不能简单在一同一行上执行同一个操作,而是需要先将point移到正确的位置。
我们准备用星号来描述柱状图。星号的数量取决于当前numbers-list中元素的值。我们需要构造一个包含星号的列表以便insert- rectangle来画出正确高度的列。如果这个list只包含一定数量的星号,那我们就必须在绘制前将point设置到正确的高度。这比较困难。
我们可以想出另外一种方式,每次传递给insert-rectangle一个同样长度的list,它们可以在同一行插入,每次插入时只需要向右移动一列。比如,如果最高的高度为5,但实际高度只有3,则insert-rectangle需要的参数如下:
(" " " " "*" "*" "*")
最后一个需求不是很难,我们需要决定列的高度。有两种方法:我们可以使用任意的值或使用整个list中最大的数字作为最大高度值。Emacs中提供了内置的函数检查参数中的最大值。我们可以使用这个函数。这个函数被称为max它返回它所有参数中的最大值。例:
(max 3 4 6 5 7 3)
将返回7。(相反的函数是min它返回参数中最小的值)
但是,我们不能简单的在numbers-list上调用max;max函数需要数字类型的参数,而不是包含数字的list。因此,下面的语句:
(max '(3 4 6 5 7 3))将出错:
Wrong type of argument: number-or-marker-p, (3 4 6 5 7 3)
我们需要一个函数将list拆开作为参数传递给函数。这个函数是apply。这个函数将其它的参数传递给它的第一个参数,它的最后一个参数可以是一个list。
例如:
(apply 'max 3 4 7 3 '(4 8 5))将返回8。
(顺便说一句,我不知道你如何学习书本上没有介绍过的函数。可以根据函数名称,比如search-forward或insert-rectangle,根据他们的部分名称使用apropos查找函数的相关信息。)
传递给apply的第二个参数是可选参数,我们可以使用aplly调用一个函数并将list中的元素传递给这个函数,比如下面的代码也将返回8:
(apply 'max '(4 8 5))后面我们将使用apply。函数recursive-lengths-list-many-files返回包含数字的list,我们对其调用max。
这样,查找图表中的最大数量的代码如下:
(setq max-graph-height (apply 'max numbers-list))
现在我们回到如何构造包含列图表字符串的list的问题上。知道图表的最大高度和星号的数量后,函数应该可以返回一个传递给insert-rectangle的list了。
每一列由星号或空格构成。因为函数传递了列高度和列中的星号数量两个参数,空白的数量应该是高度减去星号数量。给出空白数量和星号数量后,两个循环可以构造出这个list:
;;; First version.安装这个函数后,执行下面的代码:
(defun column-of-graph (max-graph-height actual-height)
"Return list of strings that is one column of a graph."
(let ((insert-list nil)
(number-of-top-blanks
(- max-graph-height actual-height)))
;; Fill in asterisks.
(while (> actual-height 0)
(setq insert-list (cons "*" insert-list))
(setq actual-height (1- actual-height)))
;; Fill in blanks.
(while (> number-of-top-blanks 0)
(setq insert-list (cons " " insert-list))
(setq number-of-top-blanks
(1- number-of-top-blanks)))
;; Return whole list.
insert-list))
(column-of-graph 5 3)将返回:
(" " " " "*" "*" "*")
如上面所写,column-of-graph包含一个瑕疵:用于标识空白和列的符号是硬编码的,使用了空白和星号。这是一个很好的原型,如果其它人 想换成其它的符号。比如用逗号代替空白,用加号代替星号等。程序应该更具弹性一些。应该使用两个变量来代替空白和星号:将graph-blank和 graph-symbol定义为两个独立的变量。
上面也没有编写文档。我们可以编写这个函数的第二个版本:
(defvar graph-symbol "*"如果需要,我们可以再次重写column-of-graph,使用线型图表代替柱状图表。这不会很困难。其中一个办法就是让柱状图中第一个星号以下的显示 为空白。在构造线型图表的一个列时,函数首先构造一个空的list,长度比元素的值小1,然后用cons将符号和列表连接;然后再次使用cons将顶部用 空白填充。
"String used as symbol in graph, usually an asterisk.")
(defvar graph-blank " "
"String used as blank in graph, usually a blank space.
graph-blank must be the same number of columns wide
as graph-symbol.")
;;(For an explanation of defvar, see Initializing a Variable with defvar.)
;;; Second version.
(defun column-of-graph (max-graph-height actual-height)
"Return MAX-GRAPH-HEIGHT strings; ACTUAL-HEIGHT are graph-symbols.
The graph-symbols are contiguous entries at the end
of the list.
The list will be inserted as one column of a graph.
The strings are either graph-blank or graph-symbol."
(let ((insert-list nil)
(number-of-top-blanks
(- max-graph-height actual-height)))
;; Fill in graph-symbols.
(while (> actual-height 0)
(setq insert-list (cons graph-symbol insert-list))
(setq actual-height (1- actual-height)))
;; Fill in graph-blanks.
(while (> number-of-top-blanks 0)
(setq insert-list (cons graph-blank insert-list))
(setq number-of-top-blanks
(1- number-of-top-blanks)))
;; Return whole list.
insert-list))
现在,我们终于完成第一个打印图表的函数。它只打印了图表的body部分,而没有水平和垂直方向的轴,因此我们把这个函数称为graph-body-print。
graph-body-print函数
上一节,graph-body-print函数完成了打印图表列的功能。这应该是一个重复执行的动作。我们可以使用递减的while循环或递归函数来完成这些操作。这节,我们使用while循环来编写函数定义。
column-of-graph函数需要图表高度作为参数,因此我们需要决定图表高度并将它保存到一个局部变量中。
我们的使用while循环的函数模板如下:
(defun graph-body-print (numbers-list)
"documentation..."
(let ((height ...
...))
(while numbers-list
insert-columns-and-reposition-point
(setq numbers-list (cdr numbers-list))))
我们需要填空。
我们可以用(apply 'max numbers-list)
获取图表的高度。
while循环遍历numbers-list。并用(setq numbers-list (cdr numbers-list))
截短它。每次list的CAR值,就是传递给column-of-graph的参数。
每个循环周期中,insert-rectangle函数使用column-of-graph插入list。由于insert-rectangle函 数将point移到了插入的矩形区域的右下解,我们需要保存当前point的位置,在插入矩形区域后恢复point的位置,然后将point水平移动到下 一个列,并再次调用insert-rectangle。
如果被插入的列是一个字符宽(比如星号或一个空格),这个命令比较简单(forward-char 1)
;但如果列宽超过1。这时命令需要写为(forward-char symbol-width)
symbol-width是graph-blank的长度,可以使用(length graph-blank)
。可以在let语句的变量列表中设置symbol-width变量。
函数定义如下:
(defun graph-body-print (numbers-list)
"Print a bar graph of the NUMBERS-LIST.
The numbers-list consists of the Y-axis values."
(let ((height (apply 'max numbers-list))
(symbol-width (length graph-blank))
from-position)
(while numbers-list
(setq from-position (point))
(insert-rectangle
(column-of-graph height (car numbers-list)))
(goto-char from-position)
(forward-char symbol-width)
;; Draw graph column by column.
(sit-for 0)
(setq numbers-list (cdr numbers-list)))
;; Place point for X axis labels.
(forward-line height)
(insert "\n")
))
这里出现了一个新的函数(sit-for 0)
。这个语句将使Emacs重绘屏幕。放在这里,Emacs将一列列的绘制。如果没有,Emacs在函数退出前都不会绘制。
我们可以使用一个较短的包含数字的list来测试graph-body-print。
- 安装graph-symbol,graph-blank,column-of-graph,graph-body-print。
- 复制下面的语句:
(graph-body-print '(1 2 3 4 6 4 3 5 7 6 5 2 3))
- 切换到
*scratch*
缓冲区并把光标放置在要绘制的开始位置。 - 输入
M-:(eval-expresion)
- Yank(C-Y) graph-body-print语句到缓冲区中。
- 回车执行graph-body-print语句。
Emacs将打印出下面的图表:
*
* **
* ****
*** ****
********* *
************
*************
recursive-graph-body-print函数
graph-body-print函数也可以用递归来编写。递归分解为两个部分:外部使用let包装,决定几个变量的值,比如图表最大高度,内部的函数调用是递归调用,用于打印图表,
包装部分不复杂:
(defun recursive-graph-body-print (numbers-list)
"Print a bar graph of the NUMBERS-LIST.
The numbers-list consists of the Y-axis values."
(let ((height (apply 'max numbers-list))
(symbol-width (length graph-blank))
from-position)
(recursive-graph-body-print-internal
numbers-list
height
symbol-width)))
递归函数部分有点复杂。它有四个部分:'do-again-test'打印操作的代码,递归调用,'next-step-expression'。 'do-again-test'是一个if语句用于检查numbers-list是否还有元素,如果有函数将使用打印操作的代码打印一个列,并再次调用自 身。函数调用自身时'next-step-expressin'将截短numbers-list。
(defun recursive-graph-body-print-internal
(numbers-list height symbol-width)
"Print a bar graph.
Used within recursive-graph-body-print function."
(if numbers-list
(progn
(setq from-position (point))
(insert-rectangle
(column-of-graph height (car numbers-list)))
(goto-char from-position)
(forward-char symbol-width)
(sit-for 0) ; Draw graph column by column.
(recursive-graph-body-print-internal
(cdr numbers-list) height symbol-width))))
在安装这个函数后,可以用下面的例子测试:
(recursive-graph-body-print '(3 2 5 6 7 5 3 4 6 4 3 2 1))结果如下:
*
** *
**** *
**** ***
* *********
************
*************
发表评论
-
emacs中使用semantic实现c代码自动完成功能
2008-11-25 16:29 9782环境: windows xp emacs 23 自已编译的cv ... -
Emacs Lisp中的hash table
2008-03-10 16:30 2241(defun zj-hash-test () "h ... -
Emacs Lisp与Shell的交互
2008-03-10 16:27 4517一直以来对于w3m、tramp、dired等与shell关系 ... -
Programming in Emacs Lisp笔记(十八) 终结
2007-07-20 11:34 2683笔记连载完毕。感谢大家的支持! 离线版本可以从这里下载。 -
Programming in Emacs Lisp笔记(十七) 调试
2007-07-20 11:11 5415调试 GNU Emacs中有两个高度器,debug和edeb ... -
Programming in Emacs Lisp笔记(十六).emacs文件
2007-07-20 11:10 6492.emacs文件 Emacs的缺省 ... -
Programming in Emacs Lisp笔记(十四)统计defun中的单词数量
2007-07-19 16:36 2803统计defun中的单词数量 我们的下一个计划是统计函数定义中 ... -
Programming in Emacs Lisp笔记(十三)计数:重复和正则表达式
2007-07-19 16:28 2616计数:重复和正则表达 ... -
Programming in Emacs Lisp笔记(十二)正则表达式查询
2007-07-19 16:26 4505正则表达式查询 在Emacs中正则表达式查询使用得很广泛。在 ... -
Programming in Emacs Lisp笔记(十一)循环和递归
2007-07-04 18:18 3533循环和递归 Emacs Lisp有 ... -
Programming in Emacs Lisp笔记(十)Yanking Text Back
2007-07-04 17:59 2940Yanking Text Back 当使用'kill'命令剪 ... -
Programming in Emacs Lisp笔记的离线版本(2007年7月20日更新,完整版)
2007-07-03 15:45 5212使用muse生成了这个笔记的html版本。里面有带链接的索引, ... -
Programming in Emacs Lisp笔记(九)List的实现
2007-07-03 14:20 2120List的实现 Lisp中list使 ... -
Programming in Emacs Lisp笔记(八)剪切和存储文本
2007-07-02 12:04 2660剪切和存储文本 当使用'kill'命令剪切文本时,Emacs ... -
Programming in Emacs Lisp笔记(七)基础函数:car, cdr, cons
2007-06-29 10:09 3767基础函数:car, cdr, cons Lisp中car,c ... -
Programming in Emacs Lisp笔记(六) Narrowing and Widening
2007-06-28 10:41 2295Narrowing and Widening Narrowi ... -
Programming in Emacs Lisp笔记(五)一些更复杂的函数
2007-06-27 13:04 2625一些更复杂的函数 copy-to-buffer的函数定义 ... -
Programming in Emacs Lisp笔记(四)与缓冲区有关的函数
2007-06-26 13:38 3150部分与缓冲区有关的函数 查找更多信息 可以通过C-h f查看 ... -
Programming in Emacs Lisp笔记(三)编写函数
2007-06-25 15:01 4140编写函数 关于基本函数 ... -
Programming in Emacs Lisp笔记(二)实践
2007-06-25 15:01 2399实践 执行代码 通过C-x C-e执行代码 缓冲区名称 b ...
相关推荐
Programming in Emacs Lisp英文版
An Introduction to Programming in Emacs Lisp Second Edition 经典中的经典!
Robert Chassell:An Introduction to Programming in Emacs Lisp
emacs官网上的那个字体太不好了,这是用源文件重编的,看起来不错
An Introduction to Programming in Emacs Lisp, 3rd Edition
An Introduction to Programming in Emacs Lisp [3.10].chm
Programming in Emacs Lisp: An Introduction (美)Robert J.Chassell 著 毛文涛、吕芳 译 洪峰 审校 本书的作者罗伯特·卡塞尔是自由软件基金会的合创人之一,也是理查德·斯托曼博士青年时期结交的挚友,他...
GNU Emacs Lisp编程入门(清晰版) 英文名:An Introduction to Programming in Emacs Lisp
GNU emacs Lisp manual This is a very interesting text, useful to write and program in several languages. Emacs is a editor made in Lisp, a artificial intelligence language.
GNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvuGNU EMACS lisp编程入门.djvu
Atom-language-emacs-lisp.zip,emacs lisp和yasnippet支持atom和github。Emacs Lisp支持,atom是一个用web技术构建的开源文本编辑器。
gnu emacs lisp 编程指南, 很好的一本书
emacs lisp 函数手册,emacs 24.3版本
GNU Emacs Lisp Reference Manual For Emacs Version 22.1 Revision 2.9, April 2007
GNU Emacs Lisp Reference Manual 英文无水印pdf pdf所有页面使用FoxitReader和PDF-XChangeViewer测试都可以打开
学习 EMACS 和 LISP 极好的入门教材,这可是好东西
强大的emacs lisp编程 不仅仅适用于初学者
emacs-lisp-intro emacs-lisp-intro