15.1 カスタム言語エンジンを登録する (*)

knitr::knit_engines$set() でカスタム言語エンジンを登録できます. これは関数を入力として受け容れます. これが例です.

knitr::knit_engines$set(foo = function(options) {
  # ソースコードは options$code にある
  # それを使ってやりたいことは何でもやろう
})

これは foo エンジンを登録し, ```{foo} で始まるコードチャンクを使えるようになります.

エンジン関数は1つの引数 options を取り, これはコードチャンクのオプションのリストです. options$code にある文字列ベクトルとして, チャンクのソースコードにアクセスできます. 例えば, このコードチャンクに対して考えます.

```{foo}
1 + 1
2 + 2
```

optionscode 要素は文字列ベクトル c('1 + 1', '2 + 2') になります.

言語エンジンは実はプログラミング言語として動作しなくてもよいですが, コードチャンクの任意のテキストを処理できます. まずは, コードチャンクの本文を大文字に変換するエンジンの例をお見せします.

knitr::knit_engines$set(upper = function(options) {
  code <- paste(options$code, collapse = "\n")
  if (options$eval)
    toupper(code) else code
})

ポイントは toupper 関数を「コード」に適用して, \n でコードの全ての行を連結し, 単一の文字列として結果を返すことです. toupper() はチャンクオプション eval = TRUEの時にのみ適用され, そうでなければ元の文字列が返されることに注意してください. このことは eval のようなチャンクオプションをエンジン関数内で利用する方法を示唆しています. 同様に, results = 'hide' の時に出力を隠すため, 関数内に if (options$results == 'hide') return() を加えることも検討することもできます. 以下は upper エンジンをオプションとともに使用するチャンクの例です.

```{upper}
Hello, **knitr** engines!
```

HELLO, KNITR ENGINES!

次に, py という名前のもう1つの Python エンジン41の例を紹介します. このエンジンは単純に R の system2() 関数から python コマンドを呼び出すことで実装しています.

knitr::knit_engines$set(py = function(options) {
  code <- paste(options$code, collapse = '\n')
  out  <- system2(
    'python3', c('-c', shQuote(code)), stdout = TRUE
  )
  knitr::engine_output(options, code, out)
})

上記のエンジン関数を完全に理解するために, 以下を知っておく必要があります.

  1. Python コードは文字列として与えられ (上記関数の code), コードはコマンドラインの呼び出し python -c 'code' によって実行できます. これが system2() のしていることです. system2() stdout = TRUE を指定することでテキスト出力を収集しています.

  2. 最終的な出力を生成するため, チャンクオプション・ソースコード・テキスト出力を knitr::engine_output() 関数に与えることができます. この関数は echo = FALSEresults = 'hide' のようなよく使うオプションを処理します. よってあなたはこれらの場合に注意する必要はありません.

knitr の多くの言語エンジンはこのようにして定義されています. つまり system2() を使って言語に対応するコマンドを実行してます. もし技術的に詳しい話に興味があるなら, R ソースコードにはほとんどの言語エンジンが書かれているここ https://github.com/yihui/knitr/blob/master/R/engine.R を確認することもできます.

そして今や, 新しいエンジン py を使うことができます. 例えばこのように.

```{py}
print(1 + 1)
```
## 2

あなたのバージョンの言語エンジンが knitr の既存の言語エンジンよりも必要性がるか, より良いものだと確信しているなら, knitr::knit_engines$set() によって既存のものを上書きすることすらできます. たいていの場合は既存のエンジンに慣れたユーザーが驚いてしまうかもしれないので, そうすることはお薦めしませんが, どちらにせよこの可能性は頭の片隅に置いてほしいです.


  1. 実用的には組み込みの python エンジンを使うべきです. これは reticulate パッケージに基づいており, より良く Python コードチャンクをサポートしてくれます (15.2節参照).↩︎