第一个函数用于打印横轴刻度标记。我们必须指定刻度标记本身及其间距:
(defvar X-axis-label-spacing
(if (boundp 'graph-blank)
(* 5 (length graph-blank)) 5)
"横轴相邻两个标注之间的单位数。")
(注意 graph-blank 的值由另一个 defvar 设置。boundp 谓词用于检测它是否已被定义;若未定义则返回 nil。如果 graph-blank 未绑定且我们未使用这种条件构造,就会进入调试器并看到错误信息:‘Debugger entered--Lisp error: (void-variable graph-blank)’。)
下面是 X-axis-tic-symbol 的 defvar 定义:
(defvar X-axis-tic-symbol "|" "用于指向横轴某一列的插入字符串。")
目标是生成如下所示的一行:
| | | |
第一个刻度缩进,使其位于第一列下方,而第一列本身已缩进以便为纵轴标注留出空间。
一个刻度元素由相邻刻度间的空白加上一个刻度符号组成。空白数量由刻度符号宽度和 X-axis-label-spacing 共同决定。
代码如下:
;;; X-axis-tic-element ... (concat (make-string ;; Make a string of blanks. (- (* symbol-width X-axis-label-spacing) (length X-axis-tic-symbol)) ? ) ;; Concatenate blanks with tic symbol. X-axis-tic-symbol) ...
接下来,我们计算将第一个刻度缩进至图表第一列所需的空白数。这里使用由 print-graph 函数传入的 full-Y-label-width 值。
生成 X-axis-leading-spaces 的代码如下:
;; X-axis-leading-spaces ... (make-string full-Y-label-width ? ) ...
我们还需要确定横轴长度(即数字列表的长度)以及横轴上的刻度数量:
;; X-length ... (length numbers-list)
;; tic-width ... (* symbol-width X-axis-label-spacing)
;; number-of-X-ticks
(if (zerop (% (X-length tic-width)))
(/ (X-length tic-width))
(1+ (/ (X-length tic-width))))
所有这些直接导出了打印横轴刻度行的函数:
(defun print-X-axis-tic-line
(number-of-X-tics X-axis-leading-spaces X-axis-tic-element)
"Print ticks for X axis."
(insert X-axis-leading-spaces)
(insert X-axis-tic-symbol) ; Under first column.
;; Insert second tic in the right spot. (insert (concat (make-string (- (* symbol-width X-axis-label-spacing) ;; Insert white space up to second tic symbol. (* 2 (length X-axis-tic-symbol))) ? ) X-axis-tic-symbol))
;; Insert remaining ticks.
(while (> number-of-X-tics 1)
(insert X-axis-tic-element)
(setq number-of-X-tics (1- number-of-X-tics))))
数字行的实现同样简单:
首先,创建每个数字前带空白的编号元素:
(defun X-axis-element (number)
"构造带编号的横轴元素。"
(let ((leading-spaces
(- (* symbol-width X-axis-label-spacing)
(length (number-to-string number)))))
(concat (make-string leading-spaces ? )
(number-to-string number))))
接下来,创建函数打印数字行,以第一列下方的数字 1 开始:
(defun print-X-axis-numbered-line
(number-of-X-tics X-axis-leading-spaces)
"Print line of X-axis numbers"
(let ((number X-axis-label-spacing))
(insert X-axis-leading-spaces)
(insert "1")
(insert (concat
(make-string
;; Insert white space up to next number.
(- (* symbol-width X-axis-label-spacing) 2)
? )
(number-to-string number)))
;; Insert remaining numbers.
(setq number (+ number X-axis-label-spacing))
(while (> number-of-X-tics 1)
(insert (X-axis-element number))
(setq number (+ number X-axis-label-spacing))
(setq number-of-X-tics (1- number-of-X-tics)))))
最后,我们需要编写使用 print-X-axis-tic-line 和 print-X-axis-numbered-line 的 print-X-axis 函数。
该函数必须确定两个子函数所用变量的局部值,然后调用它们。同时,它还需要打印分隔两行的换行符。
该函数包含一个指定五个局部变量的变量列表,以及对两个行打印函数的调用:
(defun print-X-axis (numbers-list)
"打印与 NUMBERS-LIST 长度匹配的横轴标注。"
(let* ((leading-spaces
(make-string full-Y-label-width ? ))
;; symbol-width is provided by graph-body-print
(tic-width (* symbol-width X-axis-label-spacing))
(X-length (length numbers-list))
(X-tic
(concat
(make-string
;; Make a string of blanks.
(- (* symbol-width X-axis-label-spacing)
(length X-axis-tic-symbol))
? )
;; Concatenate blanks with tic symbol.
X-axis-tic-symbol))
(tic-number
(if (zerop (% X-length tic-width))
(/ X-length tic-width)
(1+ (/ X-length tic-width)))))
(print-X-axis-tic-line tic-number leading-spaces X-tic)
(insert "\n")
(print-X-axis-numbered-line tic-number leading-spaces)))
你可以测试 print-X-axis:
X-axis-tic-symbol、X-axis-label-spacing、
print-X-axis-tic-line,以及 X-axis-element、
print-X-axis-numbered-line 和 print-X-axis。
(progn
(let ((full-Y-label-width 5)
(symbol-width 1))
(print-X-axis
'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16))))
eval-expression)。
yank)将测试表达式粘贴到小缓冲。
Emacs 会如下打印横轴:
| | | | |
1 5 10 15 20