オプションやフックを利用してお手軽に関数をいじれるようにする


この記事は『R Advent Calendar 2014』の 21 日目です。

関数の処理は関数作成者が決めるのが普通で,使用者は関数のパラメーターという形でのみ,処理の内容に影響を与えることができます。しかし作成者が知り得ない情報を用いて関数の処理を行いたい場合があります。

R では,使用者が関数の挙動に介入できる仕組みとして,オプションやフックを利用することができます。

オプションは,ユーザーが適当な値を決めて,関数はユーザーの指定した値を読み込んで利用するものです。例えば,関数を並列処理する際に何コアまで使用して良いか,画面に表示するメッセージの詳細レベルはどの程度か,といった情報を設定します。関数の呼び出しが 1 階層で済むならパラメーターでもかまいませんが,関数が複数階層になると,パラメーターとしてずっと持ち回されることになって不便です。

フックは,作成者が,使用者に対して,使用者の独自操作を関数内に差し込むことができるようにする仕組みです。また,パッケージのロード時などにも利用されます[A]。オプションは単一の値を設定できるのに対し,フックでは複数の処理を差し込むことが可能です。 R においてはフックの仕組みを利用しなくても,比較的簡単に使用者が関数を修正することができます[B]。とはいえ,プログラミングのあまり得意でない人にこれをやれといっても難しい話です。お気軽に使用者に簡単に独自処理を差し込んでもらうために,フックを採用するのは良い選択です。

以下でオプションやフックの使い方を紹介します。

オプション

オプションの利用方法

オプションは options 関数により設定ができます。例えば some.option という名前のオプションに値を設定する場合は,以下のようにします。

options(some.option=1)

取得する場合は getOption 関数を使います。

getOption("some.option", 3)

第 2 引数は,オプションが指定されていない場合のデフォルト値です。

オプションのデータ保存場所

オプションは .Options という名前のリストにデータが保存されています。通常のリストなので,リストを操作するように直接値を取得・設定することも可能です。

フック

フックの利用方法

フックの利用も簡単です。以下のような処理を,自作関数のフックしてもらいたい箇所に挿入するだけです。

for (fun in getHook("your.hook.name")) {
   if (is.character(fun)) {
      fun <- get(fun)
   }
   try(fun())
}

getHook 関数はフックの取得です。フックはオプションと異なり,複数の処理が存在しうるのでループ処理します。なおフックが与えられない場合は空のリストが返ります。

使用者は,以下のように setHook 関数により処理を差し込むことができます。

hookFunction <- function() {
   # 処理
}
setHook("your.hook.name", hookFunction)

このような処理が実際に組み込まれている関数に,例えば graphics パッケージの plot.new (before.plot.new フック) や, Rcpp パッケージの sourceCpp (sourceCpp.onBuild) があります。

例えば before.plot.new フックを以下のように設定してみると,グラフィックデバイスの初期か処理が行われなくなるため,プロットができなくなります。

setHook("before.plot.new", return)

フックのデータ保存場所

フックのデータは .userHooksEnv という名前の環境オブジェクトに保存されています。各フックは,リスト形式のオブジェクトです。

まとめ

オプションやフックを利用することで,関数の作成者は,使用者しか知り得ない情報や処理を関数内に取り込むことができます。適切に利用すれば,関数の設計がスマートになります。積極的に活用しましょう。

脚注

  1. 本当はこちらがメインの使い方です。 []
  2. 例えば『有意差でたよっ』では関数の末尾に処理を追加しています。 []