12 出力フック (*)

knitr パッケージによって, あなたはコードチャンクから出力されるものを各パーツ, ソースコード・テキスト出力・メッセージ・グラフといったものごとに制御しています. この制御は「出力フック」 (output hook(s))によって実現されています. 出力フックは出力の各パーツを入力 (典型的には文字列ベクトルとして扱います) とし, 文字列を出力文書に書き出すために返す一連の関数です. 現時点ではこのしくみを理解するのは簡単ではないでしょうが, これから説明する簡単な例を見ればこのアイディアがはっきりと理解できるものと思います. この例ではコードチャンクの出力が knitr の出力フックを介してレンダリングされる様子を表しています.

このような1行だけのコードチャンクについて考えてみてください.

```{r}
1 + 1
```

knitr がコードチャンクを評価すると, 2つの出力要素を得ます. 2つとも文字列ベクトルとして保持されます. ソースコードの "1 + 1" と, テキスト出力の "[1] 2" です. これらの文字列は求められている出力フォーマットに応じて, チャンクフックによってさらなる処理がなされます. たとえば Markdown 文書では knitr はソースコードに言語名を付けてコードブロックで囲みます. これは source フックを介して行われ, だいたいこのような関数となります.

# 上記のケースでは, `x` は文字列 '1 + 1' に相当
function(x, options) {
  # ここの小文字 'r' は言語名を表す
  paste(c("```r", x, "```"), collapse = "\n")
}

同様に, テキスト出力はこのような output フック関数によって処理されます.

function(x, options) {
  paste(c("```", x, "```"), collapse = "\n")
}

よって上記のコードチャンクの最終的な出力はこのようになります.

```r
1 + 1
```

```
[1] 2
```

実際のフックは上記のような2つの関数よりも複雑ですが, 発想は同じです. knit_hooks オブジェクトから get() メソッドで実際のフック関数を取得できます. これが例です.

# 意味のある出力のため, 以下のコードは knitr
# 文書のコードチャンクの *内部で* 実行されるべき
knitr::knit_hooks$get("source")
knitr::knit_hooks$get("output")
# または knitr::knit_hooks$get(c('source', 'output'))

knitr パッケージの開発に貢献することに本当に関心がない方には, 組み込みのフック関数のソースコードを読むことをお薦めしません. 関心がある方は, このコードは https://github.com/yihui/knitr/tree/master/R にある hooks-*.R という形式で命名されたスクリプトファイルでご覧ください. 例えば hooks-md.R には R Markdown 文書に対するフックが含まれています. 普通であれば knitr ユーザーにとっては, 組み込みのフックを活かして使うカスタム出力フックの作り方を知っていれば十分です. この章ではそれが学べますし, 以下では基本的な考え方を示します.

カスタム出力フックは knit_hooksset() メソッドによって登録されます. このメソッドは既存のデフォルトのフックを上書きするので, 既存のフックのコピーを保存し, 好きなように出力要素を処理して, その結果をこのデフォルトのフックに与えるようにしておくことをお薦めします. 構文はたいてい次のようになります.

# ここで local() を使うかは任意 (ここでは単に `hook_old`
# のような不要なグローバル変数を作ることを避ける目的)
local({
  hook_old <- knitr::knit_hooks$get("NAME")  # 古いフックを保存する
  knitr::knit_hooks$set(NAME = function(x, options) {
    # ここで, x に何らかの処理を行い, それから 新しい x
    # を古いフックに与える
    hook_old(x, options)
  })
})

ここで, NAME は以下のいずれかのフックの名前を意味します.

  • source: ソースコードを処理するフック.

  • output: テキスト出力を処理するフック.

  • warning: 警告 (たいていは warning() で発生するもの) を処理するフック.

  • message: メッセージ (たいていは message() で発生するもの) を処理するフック.

  • error: エラーメッセージ (たいていは stop() で発生するもの) を処理するフック.

  • plot: グラフのファイルパスを処理するフック.

  • inline: インライン R コードからの出力を処理するフック.

  • chunk: チャンク全体の出力を処理するフック.

  • document: 文書全体を処理するフック.

フック関数の引数 x の意味は上記のリストで説明されています. options 引数は現在のコードチャンクのオプションのリストを意味します. 例えば foo = TRUE と設定したなら, フック関数内では options$foo でこの値を得ることができます. options 引数は inline および document フックでは利用できません.

出力フックによって, チャンクと文書の出力の部品1つ1つに対して究極のコントロールを得られます. あらかじめ定義された目的を持つチャンクオプションと比較すると, 出力フックはユーザー定義関数なのではるかに強力であり, 関数内ではあなたが望むことはなんでもできます.