プログラムでプログラムを扱うこと
例:
%>%
はメタプログラミングの力を借りていることがわかりやすい変数を評価するとコピーが得られる
x <- 1:3
y <- x # Copy x
x[2] <- 9
x
## [1] 1 9 3
y
## [1] 1 2 3
touch <- function(x) x[1] <- 8 # Copied x
a <- c(10, 11)
touch(a)
a
## [1] 10 11
i <- "before"
return_i <- function() i
return_i()
## [1] "before"
i <- "after"
return_i()
## [1] "after"
global <- 1
print_env<- function(){
outer <- 2
list(here = ls(), # 自分の環境の名前
parent = ls(envir = parent.frame())) # 親環境の名前
}
print_list(print_env())
## $here: outer; $parent: c("global", "print_env", "print_list")
i = "global"
const <- function(){
i <- "outer" # Found
function() i
}
f <- const()
f()
## [1] "outer"
<<-
大域束縛で外側の変数を書き換えるcounter <- function(i = 1){
function() {i <<- i + 1; i} # Change Outer i
}
f <- counter()
c(f(), f(), f())
## [1] 2 3 4
<-
の場合counter <- function(i = 1){
function() {i <- i + 1; i} # Make Local i
}
f <- counter()
c(f(), f(), f())
## [1] 2 2 2
自分の環境で名前を探し, なければ親の環境で探す
<<-
大域束縛演算子は挙動を理解して使うべき
f <- function(x, y = 2) sqrt(x + y)
formals(f) # 引数リスト
## $x
##
##
## $y
## [1] 2
body(f) # 関数本体
## sqrt(x + y)
environment(f) # 定義元環境
## <environment: R_GlobalEnv>
.Primitive()
はRで以外で定義された関数f
## function(x, y = 2) sqrt(x + y)
sum
## function (..., na.rm = FALSE) .Primitive("sum")
演算子はバッククオートで関数に
1 + 3
## [1] 4
`+`(1, 3)
## [1] 4
if
も関数制御構造も関数に
if(1 == 2) "T" else "F"
## [1] "F"
`if`(1 == 2, "T", "F")
## [1] "F"
中括弧も関数 (逐次評価, 最後の引数を返す)
{a <- 1; a + a}
## [1] 2
`{`(a <- 2, a + a)
## [1] 4
括弧も関数 (恒等関数)
(1 + 2)
## [1] 3
`(`(1 + 2)
## [1] 3
if
が関数で大丈夫なの?実はRは引数を遅延評価する
do_nothing <- function(x) message("ok ?") # xを評価していない
do_nothing(message("args")) # 引数は評価されない
## ok ?
eval_x <- function(x) {x; message("ok ?")}
eval_x(message("args"))
## args
## ok ?
このため, if
が関数でも問題ない
if
もし, ifが引数をすべて評価する場合, どちらのmessasge
も起きてしまう
if (1 == 2) message("T") else message("F")
## F
しかし, Rでは引数は必要になるまで評価されない
条件に応じてどちらかが評価される
(
を上書きする`(` <- function(x) x + 1
c(3, (3), (1 + 2) * 3)
## [1] 3 4 12
rm("(")
Rは関数を呼ぶとき, 引数をどのように渡しているだろう
Rはどちらでもない
substitute
で引数の表現式が見れるs <- function(x) substitute(x)
s(1 + noname)
## 1 + noname
s(sqrt(5 * (1 + 3)))
## sqrt(5 * (1 + 3))
library(dplyr)
などはこれを利用しているs <- function(x) deparse(substitute(x))
s(1 + 5)
## [1] "1 + 5"
s(library(dplyr))
## [1] "library(dplyr)"
quote
とeval
quote
表現式を返すeval
表現式を評価x <- 1
quote(1 + 2)
## 1 + 2
quote(x + 1)
## x + 1
eval(quote(x + 1))
## [1] 2
quote
とsubstitute
の違いsubstitute
はローカルな変数を置き換えるquote
はそのままf <- function(x = 1) {
y <- 2
c(quote(x + y + z),
substitute(x + y + z))
}
f()
## [[1]]
## x + y + z
##
## [[2]]
## 1 + 2 + z
eval
new.env()
で作れるe <- new.env()
e$x <- 5
x <- 10 # Globalの環境
eval(quote(x), e)
## [1] 5
eval(quote(x), globalenv())
## [1] 10
eval
の環境にできるeval(quote(x), list(x=5))
## [1] 5
acc <- function(x, key) eval(substitute(key), x)
acc(list(z=1), z)
## [1] 1
filter
はこの機能を利用している
filter(iris, Sepal.Length > 7.7)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 7.9 3.8 6.4 2 virginica
library(pryr)
ast(sqrt(x + y))
## \- ()
## \- `sqrt
## \- ()
## \- `+
## \- `x
## \- `y
"a"
, 1
)x
, sqrt
()
(a = 1, b = 3)
ast(function(x, y = 1){z <- x + y; z})
## \- ()
## \- `function
## \- []
## \ x =`MISSING
## \ y = 1
## \- ()
## \- `{
## \- ()
## \- `<-
## \- `z
## \- ()
## \- `+
## \- `x
## \- `y
## \- `z
## \- <srcref>
a <- quote(x + y)
a[[1]] <- `*` # 関数名
a[[2]] <- 5 # 第1引数
a[[3]] <- quote(2 + 3) # 第2引数
a
## .Primitive("*")(5, 2 + 3)
eval(a)
## [1] 25
call("filter", quote(hoge), quote(key <= 5))
## filter(hoge, key <= 5)
こういう関数を準備
lisping <- function(l) {
if(l[[1]] != as.name("{")) return(l)
args <- lapply(as.list(l)[c(-1,-2)], lisping)
f <- as.character(l[[2]])
do.call(call, c(f, args))
}
Lispy <- function(l) eval(lisping(subs(l)), envir = parent.frame())
Lispy(
{"+"
{mean
{c
1
10
{":"
2
9}}}
100}
)
## [1] 105.5
これってS式では??
RはLisp方言だった