Rは関数型プログラミング言語なので、コード1とデータの境界は曖昧です。四則演算+,-,*,/も関数で、コードと言っても引数がセットされた関数オブジェクト2のリスト構造のデータの集まりに過ぎません3。Rではコードを格納した変数を、表現式オブジェクトと言って、expressionやparseでつくってevalで評価4します・・・と書いても、慣れていないと理解し難いモノなので、ぽちぽちと関係した命令を入力して反応を見て行きましょう。
1 表現式オブジェクトを使ってみる
習うより慣れろと言うことで、試しに動かしてみましょう。
1.1
expressionとevalの基本的な挙動
expressionとevalの振る舞いを把握するのは難しくなく、以下の4行で済むと思います。
[1] "x * yを評価すると6になります"
すぐに、expressionはRが計算する式を表す式オブジェクトを返し、evalに渡すと実際に評価してくれることが想像つくと思います。
1.2
expressionには関数も行列も入る
追加と言うよりは確認の説明ですが、expressionには関数も行列も入ります。
[1] "x * y - z(4)を評価すると-10になります"
expression(X %*% Y)
[,1] [,2]
[1,] 9 19
[2,] 7 17
1.3 文字列を表現式オブジェクトにする
文字列を表現式オブジェクトにできます。これはsprintf関数と組み合わせると便利なので、よく使います。
[1] "x + yを評価すると5になります"
1.5 表現式オブジェクトをプロットに用いる
Rのplotは、題名や軸名や凡例に表現式オブジェクトをとることで、数式を表示することができます。
?plotmathでヘルプが出るので描画可能な数式が分かり、demo(plotmath)でプロットの実例が見られますが、試しに使ってみましょう。
縦軸のラベルが数式
またcurve関数は表現式をプロットできます。
あまり綺麗な数式ではなく文法も独特になるので、latex2expパッケージを用いてTeX表記を使う人が多いのですが、ちょっとしたものであれば有用です。
2 表現式以外の言語オブジェクト
表現式オブジェクトの感覚を養ったところで、Rの言語オブジェクトについて説明します。Rにはコール、表現式、ネームの3種類の言語オブジェクトがあります。
callは一般にはクロージャと呼ばれるもので、関数に値をセットしたオブジェクトです。表現式オブジェクトは内部にクロージャを持つオブジェクトになります。クロージャと表現式オブジェクトは似た存在ですが別の扱いなので、表現式オブジェクトを引数にとる関数にクロージャは入れられないですし、逆も同様です。
nameは間接的に参照できるオブジェクトです。eval("x")をしても"x"が文字列として評価されるだけですが、eval(as.name("x"))とすると、変数xとして評価されます。
| オブジェクト | is.call |
is.expression |
is.name |
is.language |
|---|---|---|---|---|
call |
✓ | ✓ | ||
| 表現式 | ✓ | ✓ | ||
name |
✓ | ✓ |
2.1 クロージャをつくる
exprssion関数で表現式オブジェクトをつくれるように、quote関数でクロージャ(callオブジェクト)をつくれます。
x - y
[1] TRUE
[1] FALSE
[1] FALSE
[1] TRUE
2.2 表現式の変数に値を代入してクロージャをつくる
後述する理由で使い勝手が悪いのですが、substitute関数を使うと代入できます。
2^y
bquote関数を使うと、リストを作らなくても代入できます。
3^y
環境にある変数の値を代入する変数名を.(と)で囲います。
substituteもbquoteも表現式オブジェクトを引数にとり、クロージャ(callオブジェクト)を戻します。
3
evalが使う環境
eval関数が使う環境は、デフォルトではeval関数を呼び出す親の環境になり、引数envirを指定することで変えられます。
e1 <- expression(x^y)
x <- 2; y <- 3
myenv <- new.env()
myenv$x <- 3
myenv$y <- 4
eval(e1) # x=2, y=3で計算[1] 8
[1] 81
関数内では別環境になるので、環境を指定しなくても変数のスコープが切り替わります。
e1 <- expression(x^y)
fn <- function(e){
x <- 3
y <- 4
eval(e) # 関数のenvironmentを参照して評価する
}
x <- 2; y <- 3
eval(e1) # x=2, y=3で計算[1] 8
[1] 81
3.1 引数で渡された表現式の関数内での処理
dataとsubsetを引数に持つ関数は多いのですが、evalを使って同様に引数で指定された数式を使うのにはコツが要ります。
evalの引数に単純に変数名を書くと、evalが処理する前に変数の中身を評価してしまうので、evalが機能しません。引数の式はexpression関数の中に無いからです。dataにある変数の値を参照しない不具合がおきます。
そこで、引数で渡された式を処理する関数では、例えば
e_subset <- function(data, subset){
data[eval(substitute(subset), data), ]
}
e_subset(data.frame(x = 1:10), x <= 5)[1] 1 2 3 4 5
と言う風に、substituteで式の入った変数をラップします。evalがsubstitute(subset)を評価するときにはじめてsubsetが評価されます。
4 まとめ
表現式オブジェクトなどの言語オブジェクトは、多くのユーザーがプロットに数式を入れるとき以外は意識していない存在で、使うときもおまじないぐらいに捉えられていると思いますが、汎用的な関数を作るときには役立つときもあります。知っていれば使いたくなるものなので、大雑把に目を通されると、後々、ためになるかも知れません。
