graph-body-print 関数
¶前節での準備を踏まえれば、graph-body-print
関数の実装はいたって簡単です。この関数は、numbers’リストの要素を用いて各列のアスタリスク数を指定し、アスタリスクと空白を列ごとに繰り返し出力します。これは反復的な処理であるため、値をデクリメントする
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
の要素を1つずつ順に処理します。(setq numbers-list (cdr
numbers-list)) 式によって短縮されるにつれ、リストの各インスタンスにおける CAR は、column-of-graph
の引数の値となります。
while ループの各サイクルで、insert-rectangle 関数は column-of-graph が返すリストを挿入します。insert-rectangle 関数は point
を挿入された矩形の右下に移動させるため、矩形が挿入された時点の point の位置を保存し、矩形の挿入後にその位置に戻り、さらに insert-rectangle が呼び出される次の場所まで水平方向に移動する必要があります。
挿入された列の幅が1文字分である場合(単一の空白やアスタリスクを使用する場合など)、再配置コマンドは単に (forward-char 1)
となります。しかし、列の幅が1文字を超える場合もあります。この場合、再配置コマンドは (forward-char
symbol-width) と記述する必要があります。symbol-width 自体は
graph-blank の長さであり、式 (length
graph-blank) を用いて求めることができます。symbol-width
変数をグラフ列の幅の値にバインドする最適な場所は、let 式の varlist 内です。
これらの考察から、次のような関数定義が導かれます。
(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")
))
この関数の中で唯一意外な表現は、while ループ内の (sit-for 0)
という式です。この式があることで、グラフの描画処理が、そうでない場合よりも興味深いものになっています。この式により、Emacs は長さ 0 の時間だけ
待機 または何もしない状態となり、その後画面を再描画します。ここに配置することで、Emacs
は画面を1列ずつ再描画するようになります。この式がなければ、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)。
graph-body-print 式を C-y
(yank) でミニバッファにヤンクする。
graph-body-print
式を評価してください。
Emacsは次のようなグラフを出力します:
*
* **
* ****
*** ****
********* *
************
*************