Rのこと。

記事は引っ越し作業中。2023年中までに引っ越しを完了させてブログは削除予定

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()characterintegerdoblelogicalcomplexrawです。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

次はシンボル。シンボルは、xmtcarsrnormのようなオブジェクトの名前のことです。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