Rの構文解析
はじめに
この記事はAdvanced R by Hadley Wickhamの内容を自分の備忘録としてまとめたものです。Rのことを知りたいなら、このブログを読む前にAdvanced R by Hadley Wickhamを読むこと絶対的におすすめします。学び多い。
Expressions
表現式を使って計算するというのはどういうことでしょうか。その点の理解を深めていきたいと思います。例えば、y <- x * 10
を実行するとx
がない、と怒られるわけですね。
y <- x * 10 エラー: オブジェクト 'x' がありません
rlang::expr()
を使うことで、評価はせずに表現式だけ補足することが可能なので、エラーは返ってきません。
z <- rlang::expr(y <- x * 10) z y <- x * 10
そして、この表現式を評価して実行したければeval()
とx
を用意すれば問題なく実行できます。
x <- 4 eval(z) y [1] 40
この一連の流れが、コードから表現式を補足して評価するというものです。
Expressions
constant scalars
表現式は、スカラー定数、シンボル、コール・オブジェクト、およびペアリストを持ちます。
まず、スカラー定数は、ASTの最も単純な構成要素。NULLか、長さ1のアトミックベクターなんかが定数です。アトミックベクターはtypeof()
がcharacter
、integer
、doble
、logical
、complex
、raw
です。factor
はデータ型がinteger
の属性がfactor
クラスということなので、factor
というアトミックベクターはありません。
identical(expr(TRUE), TRUE) [1] TRUE identical(expr(1), 1) [1] TRUE identical(expr(2L), 2L) [1] TRUE identical(expr("x"), "x") [1] TRUE
symbols
次はシンボル。シンボルは、x
、mtcars
、rnorm
のようなオブジェクトの名前のことです。is.symbol()
で判断できます。シンボルを作成するには、2つの方法があり、expr()
でオブジェクトを参照するコードを捕捉する方法と、rlang::sym()
で文字列をシンボルに変換する方法です。
expr(rnorm(10))
は呼び出しオブジェクトでシンボルオブジェクトでありませんが、expr(rnorm)
はシンボルオブジェクトです。
is.symbol(expr(rnorm(10))) [1] FALSE is.call(expr(rnorm(10))) [1] TRUE is.symbol(expr(rnorm)) [1] TRUE is.symbol(sym("rnorm")) [1] TRUE
call objects
コールオブジェクトは呼び出しオブジェクトとか呼ばれるものです。先程の例にもあるようにexpr(rnorm(10))
は呼び出しオブジェクトです。
lobstr::ast(rnorm(n = 10, mean = 10, sd = 1)) █─rnorm ├─n = 10 ├─mean = 10 └─sd = 1 x <- expr(rnorm(n = 10, mean = 10, sd = 1)) typeof(x) [1] "language" is.call(x) [1] TRUE
呼び出しオブジェクトはリストのように振る舞うので、リストのように[[
で操作が可能です。
as.list(x) [[1]] rnorm $n [1] 10 $mean [1] 10 $sd [1] 1
call_standardise()
は、引数のマッチングに関する柔軟な規則を回避するために使われます。
call_standardise(x) rnorm(n = 10, mean = 10, sd = 1)
Parsing and grammar
Rの構文解析を行います。正直、普通に使う文なら意識することはあまりありませんが、抽象構文ツリーを使って構文解析してみます。
Rは演算子の優先順位というルールがあります。捉えようによったら、(1 + 2) * 3)
、(1 + (2 * 3)
とも捉えられるかもしれませんが、Rでは、この抽象構文ツリーのとおり、2*3
が計算され、それに+1
が計算されます。
lobstr::ast(1 + 2 * 3) █─`+` ├─1 └─█─`*` ├─2 └─3
こんな算術演算であれば小学校でも習うので、あれですが、!x %in% y
はどうでしょうか。これも抽象構文ツリーを使えばわかります。「x
の中でy
と一致するものを判断してから否定する」という流れのようです。
lobstr::ast(!x %in% y) █─`!` └─█─`%in%` ├─x └─y
こんな感じ。x %in% y
を計算して!
するということですね。
x <- 1:3 y <- c(2:3,5) x %in% y [1] FALSE TRUE TRUE !(x %in% y) [1] TRUE FALSE FALSE
値を足すのみという場合においても順序が存在します。Rでは、ほとんどの演算子は左結合です。すなわち、左の演算が最初に評価されていきます。
lobstr::ast(1+2+3+4+5) █─`+` ├─█─`+` │ ├─█─`+` │ │ ├─█─`+` │ │ │ ├─1 │ │ │ └─2 │ │ └─3 │ └─4 └─5
Infix calls
中置呼び出し(Infix calls)というのも確認しておきます。
y <- x * 10 `<-`(y, `*`(x, 10)) lobstr::ast(y <- x * 10) █─`<-` ├─y └─█─`*` ├─x └─10 lobstr::ast(`<-`(y, `*`(x, 10))) █─`<-` ├─y └─█─`*` ├─x └─10 expr(y <- x * 10) y <- x * 10 expr(`<-`(y, `*`(x, 10))) y <- x * 10