- 浏览: 237581 次
文章分类
最新评论
-
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 9859环境: windows xp emacs 23 自已编译的cv ... -
Emacs Lisp中的hash table
2008-03-10 16:30 2323(defun zj-hash-test () "h ... -
Emacs Lisp与Shell的交互
2008-03-10 16:27 4595一直以来对于w3m、tramp、dired等与shell关系 ... -
Programming in Emacs Lisp笔记(十八) 终结
2007-07-20 11:34 2726笔记连载完毕。感谢大家的支持! 离线版本可以从这里下载。 -
Programming in Emacs Lisp笔记(十七) 调试
2007-07-20 11:11 5479调试 GNU Emacs中有两个高度器,debug和edeb ... -
Programming in Emacs Lisp笔记(十六).emacs文件
2007-07-20 11:10 6541.emacs文件 Emacs的缺省 ... -
Programming in Emacs Lisp笔记(十四)统计defun中的单词数量
2007-07-19 16:36 2907统计defun中的单词数量 我们的下一个计划是统计函数定义中 ... -
Programming in Emacs Lisp笔记(十三)计数:重复和正则表达式
2007-07-19 16:28 2675计数:重复和正则表达 ... -
Programming in Emacs Lisp笔记(十二)正则表达式查询
2007-07-19 16:26 4589正则表达式查询 在Emacs中正则表达式查询使用得很广泛。在 ... -
Programming in Emacs Lisp笔记(十一)循环和递归
2007-07-04 18:18 3601循环和递归 Emacs Lisp有 ... -
Programming in Emacs Lisp笔记(十)Yanking Text Back
2007-07-04 17:59 2982Yanking Text Back 当使用'kill'命令剪 ... -
Programming in Emacs Lisp笔记的离线版本(2007年7月20日更新,完整版)
2007-07-03 15:45 5249使用muse生成了这个笔记的html版本。里面有带链接的索引, ... -
Programming in Emacs Lisp笔记(九)List的实现
2007-07-03 14:20 2231List的实现 Lisp中list使 ... -
Programming in Emacs Lisp笔记(八)剪切和存储文本
2007-07-02 12:04 2747剪切和存储文本 当使用'kill'命令剪切文本时,Emacs ... -
Programming in Emacs Lisp笔记(七)基础函数:car, cdr, cons
2007-06-29 10:09 3837基础函数:car, cdr, cons Lisp中car,c ... -
Programming in Emacs Lisp笔记(六) Narrowing and Widening
2007-06-28 10:41 2346Narrowing and Widening Narrowi ... -
Programming in Emacs Lisp笔记(五)一些更复杂的函数
2007-06-27 13:04 2743一些更复杂的函数 copy-to-buffer的函数定义 ... -
Programming in Emacs Lisp笔记(四)与缓冲区有关的函数
2007-06-26 13:38 3221部分与缓冲区有关的函数 查找更多信息 可以通过C-h f查看 ... -
Programming in Emacs Lisp笔记(三)编写函数
2007-06-25 15:01 4196编写函数 关于基本函数 ... -
Programming in Emacs Lisp笔记(二)实践
2007-06-25 15:01 2440实践 执行代码 通过C-x C-e执行代码 缓冲区名称 b ...
相关推荐
Programming in Emacs Lisp英文版
- **标题**:“An Introduction to Programming in Emacs Lisp”(Emacs Lisp编程入门) - **描述**:该资源是基于Emacs官方文档的重编版本,旨在提供更易阅读的字体样式。 #### 知识点详解 ##### 1. Emacs Lisp...
- **标题**:“An Introduction to Programming in Emacs Lisp Second Edition” - **描述**:本书是关于Emacs Lisp编程的入门教程,被誉为“经典中的经典”。 该书由Robert J. Chassell撰写,由自由软件基金会出版...
### Robert Chassell:An Introduction to Programming in Emacs Lisp #### 知识点概览 - **Lisp Lists**: 介绍Lisp列表的概念及其在Emacs Lisp中的应用。 - **Lisp Atoms**: 解释Lisp原子的基本概念以及它们在...
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 编程入门是一本非常专业的编程书籍,它详细地介绍了如何使用GNU Emacs Lisp这种编程语言来编写程序。GNU Emacs Lisp是一种用于GNU Emacs编辑器的编程语言,它是一种功能强大且灵活的编程工具,广泛...
在“Atom-language-emacs-lisp.zip”这个压缩包中,主要关注的是对Emacs Lisp的支持。Emacs Lisp是一种用于扩展和定制GNU Emacs编辑器的Lisp方言。它提供了丰富的API和交互式编程环境,使得开发者可以编写自定义的...
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
本书的英文版原名是《Programming in Emacs Lisp: An Introduction》,由自由软件基金会出版,享有复制与分发的权限。机械工业出版社在2001年5月出版了中文版,ISBN为7-111-08862-6。书籍的定价为38.00元,并承诺如...
### GNU Emacs Lisp 参考手册知识点总结 #### 1. 引言 GNU Emacs Lisp 参考手册为Emacs版本22.1提供了详细的指导,该版本为修订版2.9,发布于2007年4月。该手册由Bil Lewis、Dan LaLiberte、Richard Stallman以及...
Emacs Lisp(简称Elisp)是GNU Emacs编辑器的核心编程语言,它允许用户自定义、扩展和控制这个功能丰富的文本编辑器。作为一个强大的脚本语言,Elisp在程序员和Emacs爱好者中广受欢迎,不仅适合初学者,也满足高级...
#### 四十、准备Lisp代码发布 - **如何打包和分发Elisp代码**。 - **版本控制与文档的编写**。 - **代码审查与测试**。 #### 附录 - **Emacs 23反新闻**:Emacs 23版本的一些已知问题和改进。 - **GNU自由文档许可...