GNU Emacs 32.0.50 (9008bf64c3) のマニュアルを PLaMo 翻訳で機械翻訳したものです。誤訳を含むことがあります。原文: https://www.gnu.org/software/emacs/manual/html_node/eintr/graph_002dbody_002dprint.html ・ emacs-doc.conao3.com

15.1 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をテストできます。

  1. グラフの列の出力にあるgraph-symbol、graph-blank、column-of-graph、およびgraph-body-printをインストールしてください。
  2. 次の式を写しなさい:
    (graph-body-print '(1 2 3 4 6 4 3 5 7 6 5 2 3))
    
  3. *scratch*バッファに切り替え、グラフを開始したい位置にカーソルを置きます。
  4. 型 M-: (eval-expression)。
  5. graph-body-print 式を C-y (yank) でミニバッファにヤンクする。
  6. RET を押して graph-body-print 式を評価してください。

Emacsは次のようなグラフを出力します:

                    *
                *   **
                *  ****
               *** ****
              ********* *
             ************
            *************