「ほぼ印刷できる状態だ」って言っただろ!もちろん、print-graph関数にはバグがあって……vertical-stepオプションはあるけど、horizontal-stepオプションはないんだ。top-of-rangeスケールは10刻みで10から300までだ。でもprint-graph関数は1刻みでしか印刷しない。
これは、一部で最も陰湿なバグの種類とされる「記載漏れ」というバグの典型的な例です。これはコードを精査しても見つかる類のものではなく、コードの中に存在するのではなく、機能が書き漏らされているためです。最善の策は、プログラムを早い段階から頻繁にテストすること、そして可能な限り、理解しやすく変更しやすいコードを書くよう心がけることです。また、書き上げたものは何であれ、すぐにではなくともいずれは書き直されることになるということを、可能な限り意識するようにしてください。守るのが難しい格言です。
修正が必要なのはprint-X-axis-numbered-line関数で、その上でprint-X-axisとprint-graph関数も調整する必要があります。大がかりな作業は必要ありませんが、一点だけ細かい注意点があります。数値を目盛りの下に合わせることです。これには少し工夫が必要です。
修正後のprint-X-axis-numbered-lineは以下の通りです:
(defun print-X-axis-numbered-line
(number-of-X-tics X-axis-leading-spaces
&optional horizontal-step)
"Print line of X-axis numbers"
(let ((number X-axis-label-spacing)
(horizontal-step (or horizontal-step 1)))
(insert X-axis-leading-spaces)
;; Delete extra leading spaces.
(delete-char
(- (1-
(length (number-to-string horizontal-step)))))
(insert (concat
(make-string
;; Insert white space.
(- (* symbol-width
X-axis-label-spacing)
(1-
(length
(number-to-string horizontal-step)))
2)
? )
(number-to-string
(* number horizontal-step))))
;; Insert remaining numbers.
(setq number (+ number X-axis-label-spacing))
(while (> number-of-X-tics 1)
(insert (X-axis-element
(* number horizontal-step)))
(setq number (+ number X-axis-label-spacing))
(setq number-of-X-tics (1- number-of-X-tics)))))
Infoでこれを読んでいる場合は、print-X-axis と print-graph
の新バージョンを確認し、評価することができます。印刷された書籍で読んでいる場合は、こちらで変更箇所を確認できます(全文を掲載するには分量が多すぎます)。
(defun print-X-axis (numbers-list horizontal-step) "Print X axis labels to length of NUMBERS-LIST. Optionally, HORIZONTAL-STEP, a positive integer, specifies how much an X axis label increments for each column."
;; Value of symbol-width and full-Y-label-width
;; are passed by print-graph.
(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 horizontal-step)))
(defun print-graph (numbers-list &optional vertical-step horizontal-step) "Print labeled bar graph of the NUMBERS-LIST. The numbers-list consists of the Y-axis values.
Optionally, VERTICAL-STEP, a positive integer, specifies how much a Y axis label increments for each line. For example, a step of 5 means that each row is five units.
Optionally, HORIZONTAL-STEP, a positive integer,
specifies how much an X axis label increments for
each column."
(let* ((symbol-width (length graph-blank))
;; height is both the largest number
;; and the number with the most digits.
(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 horizontal-step)))