最终版的修改

最终版与我们之前的规划有两处不同:第一,它在变量列表中增加了一次性计算的若干值;第二,它增加了一个选项,用于指定每行标注的增量。后一功能后来被证明是必不可少的;否则图表可能会有超出显示器或纸张可容纳的行数。

这一新功能要求修改 Y-axis-column 函数,为其增加 vertical-step 参数。函数如下:

;;; Final version.
(defun Y-axis-column
  (height width-of-label &optional vertical-step)
  "构造纵轴标注列表。
HEIGHT 是图表最大高度。
WIDTH-OF-LABEL 是标注最大宽度。
VERTICAL-STEP 为可选参数,是一个正整数,
指定纵轴标注每行递增多少。
例如,步长 5 表示每一行代表图表的 5 个单位。"  
  (let (Y-axis
        (number-per-line (or vertical-step 1)))
    (while (> height 1)
      (if (zerop (% height Y-axis-label-spacing))
          ;; Insert label.
          (setq Y-axis
                (cons
                 (Y-axis-element
                  (* height number-per-line)
                  width-of-label)
                 Y-axis))
        ;; Else, insert blanks.
        (setq Y-axis
              (cons
               (make-string width-of-label ? )
               Y-axis)))
      (setq height (1- height)))
    ;; Insert base line.
    (setq Y-axis (cons (Y-axis-element
                        (or vertical-step 1)
                        width-of-label)
                       Y-axis))
    (nreverse Y-axis)))

图表最大高度和符号宽度的值由 print-graph 在其 let 表达式中计算;因此 graph-body-print 必须修改以接收这些值。

;;; Final version.
(defun graph-body-print (numbers-list height symbol-width)
  "打印 NUMBERS-LIST 对应的柱状图。
数字列表由纵轴数值组成。
HEIGHT 为图表最大高度。
SYMBOL-WIDTH 为每列宽度。"
  (let (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")))

最后是 print-graph 函数的代码:

;;; Final version.
(defun print-graph
  (numbers-list &optional vertical-step)
  "打印带标注的 NUMBERS-LIST 柱状图。
数字列表由纵轴数值组成。


可选参数 VERTICAL-STEP 为正整数,
指定纵轴标注每行递增多少。
例如,步长 5 表示每一行代表 5 个单位。
  (let* ((symbol-width (length graph-blank))
         ;; 既是最大数字,也是位数最多的数字。
         (height (apply 'max numbers-list))
         (height-of-top-line
          (if (zerop (% height Y-axis-label-spacing))
              height
            ;; else
            (* (1+ (/ height Y-axis-label-spacing))
               Y-axis-label-spacing)))
         (vertical-step (or vertical-step 1))
         (full-Y-label-width
          (length
           (concat
            (number-to-string
             (* height-of-top-line vertical-step))
            Y-axis-tic))))

    (print-Y-axis
     height-of-top-line full-Y-label-width vertical-step)
    (graph-body-print
     numbers-list height-of-top-line symbol-width)
    (print-X-axis numbers-list)))