graph-body-print
在前一节的准备工作完成后,graph-body-print
函数就很简单了。该函数将按列打印星号和空格,使用数字列表中的元素来指定每列中星号的数量。这是一个重复的操作,这意味着我们可以使用递减的 while
循环或递归函数来完成这项工作。在本节中,我们将使用 while
循环来编写定义。
column-of-graph
函数需要图表的高度作为参数,因此我们应该确定并记录它作为一个局部变量。
这导致了以下 while
循环版本的函数模板:
(defun graph-body-print (numbers-list) "文档…" (let ((height … …))
(while numbers-list 插入列并重新定位点 (setq numbers-list (cdr numbers-list)))))
我们需要填写模板的空白部分。
显然,我们可以使用表达式 (apply 'max numbers-list)
来确定图表的高度。
while
循环将逐个元素循环遍历 numbers-list
。由于它被 (setq numbers-list (cdr numbers-list))
表达式缩短,列表的每个实例的 CAR 是传递给 column-of-graph
的参数值。
在 while
循环的每个循环中,insert-rectangle
函数插入由 column-of-graph
返回的列表。由于 insert-rectangle
函数将点移动到插入的矩形的右下角,我们需要保存矩形插入时点的位置,在插入矩形后返回到该位置,然后水平移动到下一个调用 insert-rectangle
的位置。
如果插入的列只有一个字符宽,就像使用单个空格和星号时一样,重新定位命令就是简单的 (forward-char 1)
;然而,列的宽度可能大于一个字符。这意味着重新定位命令应该写为 (forward-char symbol-width)
。symbol-width
本身是一个 graph-blank
的长度,可以使用表达式 (length graph-blank)
找到。将 symbol-width
变量绑定到图列的宽度值的最佳位置是在 let
表达式的 varlist 中。
这些考虑导致了以下函数定义:
(defun graph-body-print (numbers-list) "打印 NUMBERS-LIST 的条形图。 numbers-list 由 Y 轴的值组成。" (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)
;; 逐列绘制图形。
(sit-for 0)
(setq numbers-list (cdr numbers-list)))
;; 为 X 轴标签定位点。
(forward-line height)
(insert "\n")
))
这个函数中唯一意外的表达式是 (sit-for 0)
表达式在 while
循环中。这个表达式使得图形打印操作比没有它更有趣。该表达式导致 Emacs 在零时间内“坐下”或什么都不做,然后重新绘制屏幕。放在这里,它导致 Emacs 逐列重新绘制屏幕。如果没有它,Emacs 将在函数退出时才重新绘制屏幕。
我们可以使用一组短数字列表测试 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))
eval-expression
).
yank
) 将 graph-body-print
表达式粘贴到迷你缓冲区中。
graph-body-print
表达式。
Emacs 将打印一个类似于以下的图形:
* * ** * **** *** **** ********* * ************ *************