R

トップページ
Google
WWWを検索
サイト内を検索

1 引数は使われるまで評価されない

例を見るとすぐに理解できると思うのですが、関数を呼ぶときにつけた引数は、関数内で参照されるまで評価されない仕組みが遅延実行です。

x <- 0
fn <- function(arg){
    x <<- 1 # super assignmentで親スコープのxを更新
    arg
}
fn(x^2)
[1] 1

関数を呼んだ時点のx0ですが、関数内で引数を使う前に1に更新されています。結果は1なので、関数を使うときにx^2が評価されたことが分かります。なお、遅延実行される式を予約(Promise)オブジェクトと呼びます。

2 ローカルスコープの変数は参照されない

引数の変数は呼び出し元のスコープのxになるので注意してください。以下のようにローカルスコープの変数は参照されません。

x <- 0
fn <- function(arg){
    x <- 1 # ローカルスコープにxを定義
    arg
}
fn(x^2)
[1] 0

3 引数は数式でなくてもよい

{}で囲まれた式も引数として渡すことができます。

fn <- function(arg){
    print("2")
    arg
}
fn({ print("1") })
[1] "2"
[1] "1"

関数を呼んだ時点で引数を評価していれば、以下のような結果になるわけですが、逆なので遅延実行していることが分かります。

[1] "1"
[1] "2"

3.1 小技

system.time({ })は遅延実行を活かして、実行前の時間の測ってから、引数の{ }を実行することで処理時間を測っています。 真似をして、

saveplot <- function(filename, arg){
    png(filename, type="cairo")
    arg
    dev.off()
}

saveplot("example.png", { curve(x^2, -1, 1) })

としたら、プロットの保存と、プロット自体を分離することができます。例えばR Markdownでの利用のように、ファイル保存などを行なわないコードで描画するプロットを、別の目的で保存したくなったときなどに便利です。画面にプロットしてから保存もできますが。

4 引数でなくても遅延評価にできる

こちらは明示的に書く必要がありますが、遅延評価で変数に値を割り当てることもできます。

x <- 1
delayedAssign("y", x^2)
x <- 3
print(y)
[1] 9

式にして後で評価しても良い気がしますが、evalしなくても値が入るので便利かも知れません。 組み合わせても遅延評価されます。delayedAssignでつくった予約オブジェクトを関数の引数にとっても、関数内で参照されるまでdelayedAssignで指定した式が評価されないです。

x <- 1
delayedAssign("y", x^2)
x <- 2
fn <- function(arg){
    x <<- 3
    arg
}
fn(y)
[1] 9

5 まとめ

意識しないと即時評価も遅延評価も差異はないのですが、遅延評価を上手く使うとコードの再利用性が高まります。Rらしいコードにもなりますし、ややこしい記述でもないので、意識して使っていきたいですね。