このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

R言語: 最小のキータイプで関数を呼び出す方法

対話的な利用状況において、Rの関数を、これ以上は短く出来ない最小キータイプ数で呼び出します。究極のキータイプ節約法。

内容:

  1. 動機
  2. やり方
  3. 理由

動機

Rコンソール内でカレントディレクトリを確認したいことがあります。getwd()関数を呼び出せばいいのですが、getwdという綴りに馴染めません。OSシェルと同じ名前pwdのほうがいいなー。

であるならば、pwd <- getwd と直接に別名定義するか、pwd <- function() getwd() と関数定義すれば済む話です。そうしましょう。

すると、pwdという名前でgetwdを呼び出せます。ですが、Rの関数呼び出しには丸括弧 () が必須です。単にpwdとすると、pwd関数の内容が表示されてしまいます。


> pwd <- function() getwd()

function() getwd()
>

OSシェルと同じように、単にpwdだけ(括弧はなし)入力して関数を呼べないでしょうか。

やり方

Rの関数はオブジェクト(データとみなせるモノ)です。オブジェクトには属性を付けることができます。特に、classという名前の属性により“オブジェクトのクラス”(正確にはS3クラス)が決定されます。Rのクラス(S3クラス)は、「class属性に指定された名前」以上の意味を持ちません。オブジェクトを扱う関数の側が、その名前(S3クラス名)を目安に「それらしく」処理するだけです。

さて、「対話的に引数なしで使いたい関数」の目印として、interactiveCommandというクラス名を使うことにします。


> attr(pwd, "class") <- "interactiveCommand"

[1] "interactiveCommand"
>

次に、interactiveCommandクラスに対するprintメソッドを定義します。メソッドとは何か? は次の節で説明します。


> print.interactiveCommand <- function(x) print(x())

これにより、print(pwd) は print.interactiveCommand(pwd) を意味することになります。Rコンソールのプロンプトに対するユーザー入力の結果は自動的にprintされるので、print.interactiveCommand(pwd) を経由して、pwd() の結果が表示されます。


> pwd
[1] "C:/Users/hiyama/Work/Chimaira.org/R-sample"
>

スタートアップファイル ~/.Rprofile に上記の手順を入れておくなら、次のような2行です。

print.interactiveCommand <- function(x) print(x())
pwd <- structure(function() getwd(), class="interactiveCommand")

もちろん、pwd以外の関数をinteractiveCommandに設定してもかまいません。

理由

言語処理系のインタプリタは、Read-Eval-Printループと呼ばれる繰り返し処理をしています。Rコンソールも例外ではありません。ユーザーからのキーボード入力をReadして、Eval(式の評価)をして、その結果をPrintします。

Printの際には、print関数が自動的に呼ばれます。このprint関数ですが、Rでは汎用関数(generic function)と呼ばれるタグイの関数で、自分では何も行いません。print(x) が呼ばれると、引数xのクラス名(class属性の値)を調べて、print.<クラス名>という名前の関数にディスパッチ(下請けに丸投げ)します。

Rにおけるメソッドとは、print.<クラス名>という名前を持った関数に過ぎないのです。要するに、クラスとメソッドは、名前に対するお約束により形成されているのです。そして、オブジェクトのクラス名と対応するメソッドを仲介するのが汎用関数です。

今回の例では、処理は次の流れで進みます。

  1. ユーザーが pwd と入力する。(Read)
  2. pwd の評価結果は関数オブジェクトになる。(Eval)
  3. 結果である関数オブジェクトを表示する。(Print)
    1. 関数オブジェクトを引数として汎用関数printを呼ぶ。
    2. 関数オブジェクトもオブジェクトであるから、そのクラス名を調べる。
    3. クラス名がinteractiveCommandなので、print.interactiveCommand関数(メソッド)にディスパッチする。
    4. print.interactiveCommand関数が、引数である関数を実行して、その結果を普通に表示する。